三大框架学习之-Mybatis高级查询&spring

一. Mybatis高级查询

- 什么是高级查询

  • 高级查询其实就是多条件查询,我们以前在写查询的时候,多数情况下是写的简单的(select * from *** ),但是在真实开发中,我们是需要根据用户选择的条件来查询某些数据的,例如我们在逛淘宝、京东的时候,我们需要筛选手机->品牌->价格... 这些其实就是查询条件

  • 在Mybatis中,高级查询就是由我们的动态sql实现,后续讲解什么是动态sql

  • 下图其实就是有条件的查询,标题、是否启用。如果用户填写了查询条件,那么我们就需要使用动态sql完成

image.png

- 什么是Query条件对象

  • 在接收条件参数的时候,我们有两种方式

    • 第一种:使用单个变量接收单个条件,多个条件可以用多个变量接收。这种方式在条件少的时候可以,但是如果有10个、20个条件难道我们要用这么多个变量来接收条件吗?

    • 第二种:将查询条件封装到我们的实体类当中,在接收条件和传递条件的时候,我们只需要传递实体类即可。

      注意:我们以后就会使用Query对象(第二种)的方式接收,传递条件对象

  • Query条件对象

    • 其实就是一个实体类对应一个一个查询条件Query对象,我们可以写一个XxxQuery类【Xxx是实体类名或模块名或表名】
public class ArticleQuery{
	//文章标题
	private String title;
	//默认启用状态
	private Boolean enable;
     //此处省略getter和setter方法
}
复制代码

- Mybatis动态sql

  • if 标签:

    • 作用 :

      ①用于判断条件是否满足,满足就拼接sql,不满足不拼接

      ②会自动加上空格,避免造成sql语法错误

    • 注意:

      ①如果参数是字符串类型,那么需要判断null和空字符串【去空白】;

      ②如果参数是数值类型,只需要判断null值;

<if test='ename != null and !"".equals(ename.trim())'>
    and ename like CONCAT('%',trim(#{ename}),'%')
</if>
复制代码
  • where 标签

    • 作用:

      ①拼接了一个where

      ②忽略第一个多余的and 或者 or

      ③自动加空格

  • 模糊查询

    方案一:直接用#,日志中发现值并没有替换,所有不能直接使用#
     
	and (name like '%#{keywords}%' or password like '%#{keywords}%')
        
    方案二:使用$符号,测试成功,但是可能会出现SQL注入
        
	and (name like '%${keywords}%' or password like '%${keywords}%')
        
    方案三:用mysql中字符串拼接函数concat,测试成功,也不会出现SQL注入,建议使用
    
        and(name like concat('%',#{keywords},'%') or password like concat('%',#{keywords},'%')
复制代码
  • 特殊符号处理:在sql语句中,我们会使用到一些比较符号,在xml中有特殊处理
    • 注意:在xml中特殊字符不处理会报错

    • (1)在xml的转义字符:

      • ①符号:<【<】、<=【<=】、>【>】、>=【>=】、&【&】、'【'】 、"【"】

      • ②【gt -> greater than(大于 )lt -> less than(小于)】

    • (2)或者可以用CDATA代码段:

      • ①大于等于 <![CDATA[ >= ]]>

      • ②小于等于 <![CDATA[ <= ]]>

- sql代码块的抽取


    - 在我们java代码中,经常使用到的代码,我们会将他抽取为一个方法,在需要使用的地方引入,那么在sql中也有sql的抽取引入
    
    - sql抽取:
	<sql id="address">
		<if test='address != null and !"".equals(address.trim())'>
			and address like CONCAT('%',trim(#{address}),'%')
		</if>
	</sql>

   - 使用<include>调用:
   
        <include refid="address"></include>
复制代码
  • 使用高级查询标签,进行高级查询测试

二. 为什么学习spring

- 在开发应用时常遇到的问题

  • 问题1:通过下面的代码我们可以发现,代码与代码间的耦合度太高,我们需要使用一个接口,需要在使用他的地方 new 出它的实现类,如若接口或者实现发生改变,那么在使用到他的地方都需要改变。

  • 问题2:如果对象有很多,且互相存在依赖关系,并且有的对象需要单例模式,有的则需要多个实例,处理起来比较繁琐;

  • 问题3:在之前我们开启事务、关闭事务、关闭连接都需要我们手动去添加代码,这样就会导致我们的代码臃肿,开发效率低下

        public class EmployeeServiceImpl {
            //如果接口和实现类在同一地方存在,那么就是高耦合,维护难度增加了。
            private IEmployeeDao employeeDao = new EmployeeJdbcDaoImpl();
        }
    
复制代码

注意:Spring能够解决上述问题

三. 初识spring

- 什么是spring

  • Spring是一个开源的轻量级控制反转(IOC)和面向切面编程(AOP)的容器框架;

    • 开源:免费的技术,有一群人在维护的

    • 轻量级:相对于重量级(框架设计比较繁琐,配置较多,例如EJB(tomcat不支持),现在基本不用了)而言,开发使用都比较简单,功能强大;

    • IOC(Inverse of control - 控制反转):将创建对象的权利和依赖关系维护(字段赋值)交给Spring容器(不再使用以前new关键字创建对象)。对于某个具体的对象而言,以前是它控制其他对象,现在是所有对象都被Spring控制,所以这叫控制反转;

      • Spring提供了强大的IOC机制,能够帮助我们管理对象和依赖关系维护:

        (1)管理对象:包括对象的创建,初始化,和销毁(分情况)

        (2)依赖关系维护:DI(Dependency Injection)依赖注入,后续再讲

        注:通常被Spring管理的类称之为Bean,在Spring容器中的对象称之为Bean对象

    • AOP(Aspect Oriented Programming):将相同的逻辑抽取出来,即将业务逻辑从应用服务中分离出来。然后以拦截的方式作用在一个方法的不同位置。例如日志,事务的处理;

    • 容器框架:容器就是装东西的,那么在spring当中它就是管理对象的(bean)

注意事项:Spring底层原理:xml+dom4j+工厂设计模式+反射

- Sping框架的好处

  • 开源的免费的,并且有固定的团队在维护,社区活跃,java的春天;

  • 方便解耦,降低维护难度,提高开发效率(Spring相当于是一个大的工厂,它提供的IOC思想,可以将对象的创建和依赖关系维护都交给spring管理);

  • spring支持AOP编程(spring提供面向切面编程,可以很方便的实现对程序进行权限拦截和运行监控等功能,可以将一些通用任务如安全、事务、日志等进行集中式管理,从而提供了更好的复用);

  • Spring致力于J2EE应用的各层的解决方案,而不是仅仅专注于某一层的方案。在企业级开发中,通常用于整合其他层次的框架;

  • 方便程序的测试(Spring 对junit4支持,可以通过注解测试Spring 程序,非常便捷);

  • 方便集成各种优秀的框架(Spring并不强制应用完全依赖于Spring,开发者可自由选用Spring框架的部分或全部,也可以集成其他优秀的框架);

  • Spring降低了javaEE API的使用难度(Spring 对javaEE开发中非常难用的一些API,例如JDBC、javaMail、远程调用等,都提供了封装,是这些API应用难度大大降低);

注意:Spring的DI机制降低了业务对象替换的复杂性,提高了组件之间的解耦

- Spring框架的模块化

  • 简单地说,模块化就是有组织地把一个大文件拆成独立并互相依赖的多个小模块;

image.png

Spring框架的功能大约由20个模块组成,这些模块按组可以分为:

  • (1)Core Container(核心容器):

    • ①Beans:负责Bean工厂中Bean的装配,所谓Bean工厂即是创建对象的工厂,Bean的装配也就是对象的创建工作;

    • ②Core:这个模块即是负责IOC(控制反转)最基本的实现;

    • ③Context:Spring的IOC容器,因大量调用Spring Core中的函数,整合了Spring的大部分功能。Bean创建好对象后,由Context负责建立Bean与Bean之间的关系并维护。所以也可以把Context看成是Bean关系的集合;

    • ④SpEl:即Spring Expression Language(Spring表达式语言);

  • (2)Data Access/Integration(数据访问/集成):

    • ① JDBC:对JDBC的简单封装;
    • ② ORM:支持数据集成框架的封装(如Mybatis,Hibernate);
    • ③ OXM:即Object XML Mapper,它的作用是在Java对象和XML文档之间来回转换;
    • ④ JMS:生产者和消费者的消息功能的实现;
    • ⑤ Transations:事务管理;
  • (3)Web与远程调用:

    • ① WebSocket:提供Socket通信,web端的的推送功能;
    • ② Servlet:Spring MVC框架的实现;
    • ③ Web:包含web应用开发用到Spring框架时所需的核心类,包括自动载入WebApplicationContext特性的类,Struts集成类、文件上传的支持类、Filter类和大量辅助工具类;
    • ④ Portlet:实现web模块功能的聚合(如网站首页(Port)下面可能会有不同的子窗口(Portlet));
  • (4)AOP:面向切面;

  • (5)Aspects:同样是面向切面的一个重要的组成部分,提供对AspectJ框架的整合;

  • (6)Instrumentation(设备):相当于一个检测器,提供对JVM以及对Tomcat的检测;

  • (7)Messaging(消息):Spring提供的对消息处理的功能;

  • (8)Test(测试):我们在做单元测试时,Spring会帮我们初始化一些测试过程当中需要用到的资源对象;

  • Spring模块的jar包

image.png

四. spring入门

- 第一步:导入jar包

  • 使用框架的第一步理所应当就应该想到我们要进行导包,因为spring有众多的jar包,所以我们需要用到什么包再导入什么包,接下来我们就先导入我们小入门的四个jar包

image.png

- 第二步:编写核心配置文件

  • 既然核心jar包有了,根据我们学习mybatis的经验,这时候我们就需要一个核心配置文件

    • 在resources下创建一个xml文件,命名可以自定义,但是行业内一般都命名为applicationContext.xml,这就是spring的核心配置文件

    • xml文件中都需要我们的头文件,也就是文档声明和约束

      • 可以参考文档,中英文文档都可以;

        ①spring-framework-4.1.2.RELEASE\docs\spring-framework-reference\pdf

      • 可以百度spring的配置文件;

      • 也可以直接拿以下内容去修改

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
	<bean id="..." class="...">
	<!-- collaborators and configuration for this bean go here -->
	</bean>
</beans>
复制代码

- 第三步:准备普通类

  • 准备一个普通的Java类(MyBean),提供一个无参构造,打印一句话

- 第四步:将我们自己的java类注册进Bean中

  • 将这个类交给Spring去管理即注册到Spring容器中

    • 大家要记得,Spring是一个容器,我们需要把我们的类交给Spring去管理。 因为,我们的测试是创建一个普通的类,然后再通过Spring帮我们把这个类的对象创建出来就算是成功了;

    • 在配置文件中将这个Java类交给Spring管理。在applicationContext.xml中配置

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<beans ...>
  <bean id="myBean" class="cn.itsource._01_hello.MyBean"></bean>
</beans>

复制代码
  • 元素和属性讲解:

    • bean元素:表示对象配置或注册标签;

    • id属性:这个bean对象在Spring容器中的唯一标识,也可以使用name,常用id(唯一特性),获取这个对象的时候就可以通过这个表示来获取;

    • class属性:对应对象所属类的完全限定名。注意这里可以是JDK自带的类,也可以是自己新建的类;

    注意:Spring容器中不允许有两个名字【不管是id指定还是name指定】一个的对象

- 第五步:获取spring核心对象,spring容器的实例化

  • 我们的bean对象交给spring之后,我们肯定是要在我们代码中使用的,那么我们如何在java中使用我们的bean对象呢

    • Spring容器对象有两种:BeanFactory和ApplicationContext;

    • ApplicationContext继承自BeanFactory接口,拥有更多的企业级方法,推荐使用该类型;

- BeanFactory

  • BeanFactory是一个接口,可以通过其实现类XmlBeanFactor获取其实例。接口中有一个getBean()方法可以获取Spring容器中配置或注册的Bean对象;
@Test
public void testHelloSpring() throws Exception {
	/**
	 *我们第一步是要启动框架,而启动框架则需要拿到Spring的核心对象
	 *咱们学习的第一个核心对象是BeanFactory : 顾名思义,这是一个创建Bean的工厂
	 *而Bean工厂创建对象又必需拿到配置文件中的数据
	 *因为:我们的第一步读取配置文件,拿到BeanFactory工厂	
	 */
	
	//第一步:读取资源文件
	Resource resource = new ClassPathResource("applicationContext.xml");
	//第二步:拿到核心对象 BeanFactory
	BeanFactory factory = new XmlBeanFactory(resource);
}
复制代码

- ApplicationContext(推荐使用)

  • ApplicationContext的中文意思是"应用程序上下文",它继承自BeanFactory接口,除了包含BeanFactory的所有功能之外,在国际化支持、资源访问(如URL和文件)、事件传播等方面进行了良好的支持,被推荐为JavaEE应用之首选,可应用在Java APP与Java Web中;

  • 要使用ApplicationContext的功能,必需要导入相应的jar包:

    spring-context-4.1.2.RELEASE.jar -- 上下文

    spring-expression-4.1.2.RELEASE.jar -- Spring表达式语言

    // 使用ApplicationContext
    
        //加载工程classpath下的配置文件实例化
        String conf = "applicationContext.xml";
        ApplicationContext factory = new ClassPathXmlApplicationContext(conf);
复制代码

- 获取实例对象

  • 方式一:通过id直接获取到相应的Bean对象

      //通过xml中配置的id拿到对象
      
      MyBean bean = (MyBean)factory.getBean("myBean");
      
      System.out.println(bean);
      
    复制代码
  • 方式二:通过id与对象的Class对象拿到Bean对象(推荐使用)

      //通过id与对象的class拿到Bean对象
    
      MyBean bean = factory.getBean("myBean",MyBean.class);
    
      System.out.println(bean);
      
    复制代码

- ApplicationContext与BeanFactory的区别【掌握】

  • 联系:

    • ApplicationContext是BeanFactory的子类,拥有更多的功能与方法;
  • 区别:

    • ApplicationContext默认是在读取配置文件的时候就会根据配置创建Bean对象(迫切加载)。而BeanFactory是在使用的时候才进行对象的创建(懒加载/延迟加载)

  • 扩展:

    • 我们在使用ApplicationContext的时候,可以通过配置让它也变成与BeanFactory一样的懒加载,后续我们会进行学习

五. spring依赖注入

- 依赖注入概念

  • IoC是一种思想,它的一个重点是在系统运行中,动态的向某个对象提供它所需要的其他对象。这一点是通过DI(Dependency Injection,依赖注入)来实现的;

  • Spring中的对象都是由Spring进行统一管理,但是在对象中还存在属性,这些属性实际上引用的也是别的对象,那么这些对象也是由Spring来管理的;

  • 在实际使用时,我们需要给Spring中对象的属性字段赋值,这称为依赖注入DI(Dependency Injection);

  • 依赖注入又分为xml注入和注解注入;

- xml注入

  • 在xml中进行配置,但是这种方式必须有对应的setter方法,所有这种注入方式又称之为属性注入或setter方法注入;
// 实体类
package cn.itsource.test;

public class MyUserBean {
	
	private String username;
	
	private String password;
	
	private MyBean myBean;

	public String getUsername() {
		return username;
	}

	public void setUsername(String username) {
		this.username = username;
	}

	public String getPassword() {
		return password;
	}

	public void setPassword(String password) {
		this.password = password;
	}
	
	

	public MyBean getMyBean() {
		return myBean;
	}

	public void setMyBean(MyBean myBean) {
		this.myBean = myBean;
	}

	@Override
	public String toString() {
		return "MyUserBean [username=" + username + ", password=" + password + ", myBean=" + myBean + "]";
	}	

}
复制代码
<!-- 核心配置文件中注入 -->
	<bean id="myUserBean" class="cn.itsource.test.MyUserBean">
        <!-- name就是给这个bean对象谁注入属性,value就是给这个属性注入什么值 -->
		<property name="username" value="张三"></property>
		<property name="password" value="123456"></property>
		
		<!-- name就是给这个bean对象谁注入属性,ref就是给对象属性,指定注入那个bean
			ref的属性值,就是我们交给spring管理的某个bean的id
		 -->
		<property name="myBean" ref="mybean"></property>
	</bean>
复制代码
// 测试
@org.junit.Test
public void testGetMyUserBean() throws Exception {
	ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
	MyUserBean bean = applicationContext.getBean("myUserBean", MyUserBean.class);
	System.out.println(bean);
	}
复制代码

注意:XML注入方式一定要给属性提供set方法,不然会报错

- 注解注入:

  • 通过注解实现注入,这种方式可以将注解写在setter方法上,也可以写在字段上,如果写在字段上可以不需要setter方法,想要注解注入成功,那么需要全注解的方式;

- 使用@Autowired

- @Autowired为Spring提供的注解,需要导入包org.springframework.beans.factory.annotation.Autowired;

- @Qualifier:指定Bean的名称
   
复制代码
package cn.itsource.test;

import javax.annotation.Resource;

import org.springframework.beans.factory.annotation.Autowired;

public class MyUserBean {
	
	private String username;
	
	private String password;
	
	@Autowired
        @Qualifier("myBean")
	private MyBean myBean;

	public String getUsername() {
		return username;
	}

	public void setUsername(String username) {
		this.username = username;
	}

	public String getPassword() {
		return password;
	}

	public void setPassword(String password) {
		this.password = password;
	}
	
	

	public MyBean getMyBean() {
		return myBean;
	}

	@Override
	public String toString() {
		return "MyUserBean [username=" + username + ", password=" + password + ", myBean=" + myBean + "]";
	}

}


//xml配置:
<bean id="date" class="java.util.Date"></bean>
	<bean id="mybean" class="cn.itsource.test.MyBean"></bean>
	
	<bean id="myUserBean" class="cn.itsource.test.MyUserBean">
		<property name="username" value="张三"></property>
		<property name="password" value="123456"></property>
		
		<!-- name就是给这个bean对象谁注入属性,ref就是给对象属性,指定注入那个bean
			ref的属性值,就是我们交给spring管理的某个bean的id
		 -->
		<!-- <property name="myBean" ref="mybean"></property> 因为要测试注解注入,所以注释掉 -->
	</bean>
复制代码

- 使用@Resource

  • @Resource由J2EE提供,需要导入包javax.annotation.Resource,Spring支持该注解

  • name属性:指定Bean的名称

package cn.itsource.test;

import javax.annotation.Resource;

import org.springframework.beans.factory.annotation.Autowired;

public class MyUserBean {
	
	private String username;
	
	private String password;

	@Resource
	private MyBean myBean;

	public String getUsername() {
		return username;
	}

	public void setUsername(String username) {
		this.username = username;
	}

	public String getPassword() {
		return password;
	}

	public void setPassword(String password) {
		this.password = password;
	}
	
	

	public MyBean getMyBean() {
		return myBean;
	}

	@Override
	public String toString() {
		return "MyUserBean [username=" + username + ", password=" + password + ", myBean=" + myBean + "]";
	}

}

复制代码

注意事项:在实例化Bean和注入Bean对象的同时,不要将xml方式和注解方式进行混用,要么都用xml方式【今天用】,要么都用注解方式【使用spring测试或者sproingMVC学完全注解就可以用了】;

六. spring测试

- Spring测试介绍

  • 单元测试在我们的软件开发流程中占有举足轻重的地位;

  • 而且目前基于Java 的企业应用软件来说,Spring 已经成为了标准配置,那么如何在Spring框架中更好的使用单元测试呢?

  • Spring框架提供了对单元测试(junit4)的强大支持,我们不用在使用传统的单元测试去测试Spring功能。通过SpringJunit测试,使用注解帮我们读取配置文件和赋值,简化测试代码,提高测试效率;

image.png

- spring测试的优势

  • 三大框架整合的时候如果Spring的配置文件不能读取,那么整个项目是跑不起来的, 而Spring的测试可以让我们在不启动服务器的情况下,使用注解读取相应的配置文件,把项目跑起来;

- spring测试步骤

  • 导入spring测试需要的jar包

      - spring-test-4.1.2.RELEASE.jar--	测试包
      
      - spring-aop-4.1.2.RELEASE.jar--AOP包
      
    复制代码

    注意:测试包依赖AOP,所以需要导入AOP的jar包,如果没有导入会报错

  • 编写普通测试类和测试方法

  • 在测试类方法上加上两个注解

    • @RunWith(SpringJUnit4ClassRunner.class):表示先启动Spring容器,把junit运行在Spring容器中;
      - SpringJUnit4ClassRunner:代表是Junit4的测试环境

    • @ContextConfiguration("classpath:applicationContext.xml"):表示从CLASSPATH路径去加载资源文件;

       - classpath:表示从项目的跟目录中去找配置文件
       
      复制代码

注意:如果不写classpath,那么默认会在测试类的平级路径去找对应的配置文件。如果只写@ContextConfiguration不写classpath&配置文件名称,那么默认会在测试类的平级路径去找名称为:测试类名称-context.xml的配置文件。

七. spring配置细节

- Bean对象的作用域

  • 作用域指的就是我们的Bean对象是单例还是多例

  • spring默认使用的是单例模式,我们获取一个Bean对象不管多少次拿到的都是一个对象

  • 改变Bean对象默认作用域

    • 在Bean对象中,我们可以通过scope属性改变
      • singleton:默认值,单例
      • prototype:多例
<bean id="scopeBean" class="cn.itsource._03_scope.MyScopeBean" scope="prototype"></bean>
复制代码

其他属性值:一般不用

image.png

- Bean对象配置懒加载

  • 在前面我们已经知道了,我们的ApplicationContext获取bean是迫切加载,那么我们如何让他变为懒加载呢

  • 两种方式

  • 第一种:Spring中所有bean对象全部懒加载:配置文件的根对象中添加default-lazy-init为true

<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://www.springframework.org/schema/beans
	http://www.springframework.org/schema/beans/spring-beans.xsd"
	default-lazy-init="true">
</beans>
复制代码
  • 第二种:单个Bean对象懒加载/延迟加载
<bean id="user" class="cn.itdemo.test.User" lazy-init="true"></bean>
复制代码

- Bean对象的生命周期

  • 不同作用域的Bean,生命周期有所区别:

    • Spring管理的Bean对象默认是单例的;
    • Bean对象的实例化和初始化:
      • 实例化实质是Spring容器调用Bean的无参构造创建Bean对象;
      • 初始化实质上是Spring容器调用指定的初始化方法;
    • BeanFactory管理的Bean默认是在使用的时候才创建Bean对象,即延迟加载,而AppliacationContext管理的Bean默认是在容器创建的时候就会创建Bean对象,即迫切加载;
    • Bean对象的销毁:
      • 实质上是Spring容器调用指定的销毁方法(并不是真正意义上的销毁Bean对象);
      • 在容器关闭的时候(ApplicationContext对象没有close方法,其实现类有),Spring容器会自动调用指定的销毁方法;
  • 可以通过bean元素的init-method和destroy-method属性指定初始化方法和销毁方法。但是一般我们自己不会来配置这个生命周期。而这个基本上Spring自身来使用,例如在Spring操作连接池的时候,它会在DateSource销毁的时候执行;

image.png

  • Mybatis高级查询&spring基础就结束了