Java EE学习笔记(十)

时间:2023-02-25 15:33:39

MyBatis与Spring的整合

1、整合环境搭建 

1)、要实现MyBatis与Spring的整合,很明显需要这两个框架的JAR包,但是只使用这两个框架中所提供的JAR包是不够的,还需要其他的JAR包来配合使用,整合时所需准备的JAR包具体如下。Spring框架所需的JAR包

Java EE学习笔记(十)

注意:核心容器依赖的commons-logging的JAR在MyBatis框架的lib包中已经包含

2)、MyBatis框架所需的JAR包:(共13个)

Java EE学习笔记(十)

3)、MyBatis与Spring整合的中间JAR:mybatis-spring-1.3.1.jar

4)、数据库驱动JAR(MySQL):mysql-connector-java-5.1.40-bin.jar

5)、数据源所需JAR(DBCP数据库连接池):commons-dbcp2-2.1.1.jar、commons-pool2-2.4.2.jar

6)、编写配置文件:

①创建项目,引入JAR包;

Java EE学习笔记(十)

Java EE学习笔记(十)

②编写db.properties ;

jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/mybatis
jdbc.username=root
jdbc.password=******
jdbc.maxTotal=30
jdbc.maxIdle=10
jdbc.initialSize=5

#密码自己设置

编写Spring配置文件applicationContext.xml;

 1 <?xml version="1.0" encoding="UTF-8"?>
 2 <beans xmlns="http://www.springframework.org/schema/beans"
 3     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
 4     xmlns:aop="http://www.springframework.org/schema/aop"
 5     xmlns:tx="http://www.springframework.org/schema/tx" 
 6     xmlns:context="http://www.springframework.org/schema/context"
 7     xsi:schemaLocation="http://www.springframework.org/schema/beans 
 8     http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
 9     http://www.springframework.org/schema/tx 
10     http://www.springframework.org/schema/tx/spring-tx-4.3.xsd
11     http://www.springframework.org/schema/context 
12     http://www.springframework.org/schema/context/spring-context-4.3.xsd
13     http://www.springframework.org/schema/aop 
14     http://www.springframework.org/schema/aop/spring-aop-4.3.xsd">
15     
16     <!--1、读取db.properties文件的位置 -->
17     <context:property-placeholder location="classpath:db.properties"/>
18     
19     <!-- 2、配置数据源 -->
20     <bean id="dataSourceID" class="org.apache.commons.dbcp2.BasicDataSource">
21     
22         <!--2.1、数据库驱动 -->
23         <property name="driverClassName" value="${jdbc.driver}" />
24         
25         <!--2.2、连接数据库的url -->
26         <property name="url" value="${jdbc.url}" />
27         
28         <!--2.3、连接数据库的用户名 -->
29         <property name="username" value="${jdbc.username}" />
30         
31         <!--2.4、连接数据库的密码 -->
32         <property name="password" value="${jdbc.password}" />
33         
34         <!--2.5、最大连接数 -->
35         <property name="maxTotal" value="${jdbc.maxTotal}" />
36         
37         <!--2.6、最大空闲连接  -->
38         <property name="maxIdle" value="${jdbc.maxIdle}" />
39         
40         <!--2.7、初始化连接数  -->
41         <property name="initialSize" value="${jdbc.initialSize}" />
42         
43     </bean>
44     
45     <!-- 3、事务管理器,依赖于数据源 --> 
46     <bean id="transactionManagerID" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
47         <property name="dataSource" ref="dataSourceID" />
48     </bean>    
49     
50     <!--4、开启事务注解 -->
51     <tx:annotation-driven transaction-manager="transactionManagerID"/>
52     
53     <!--5、配置MyBatis工厂来于Spring整合,
54         构建sqlSessionFactory是通过mybatis.spring包中提供的org.mybatis.spring.SqlSessionFactoryBean类配置的;
55         配置时通常需要2个参数:①数据源;②MyBatis的配置文件路径
56      -->
57     <bean id="sqlSessionFactoryID" class="org.mybatis.spring.SqlSessionFactoryBean">
58          
59          <!--5.1、注入数据源 -->
60          <property name="dataSource" ref="dataSourceID" />
61          
62          <!--5.2、指定核心配置文件位置 :mybatis-config.xml-->
63            <property name="configLocation" value="classpath:mybatis-config.xml"/>
64            
65    </bean>
66    
67    <!--6、实例化Dao -->
68     <bean id="customerDaoID" class="com.itheima.dao.impl.CustomerDaoImpl">
69     <!-- 6.1、注入SqlSessionFactory对象实例-->
70          <property name="sqlSessionFactory" ref="sqlSessionFactoryID" />
71     </bean>
72     
73     <!-- 7、Mapper代理开发(基于MapperFactoryBean) -->
74     <!-- <bean id="customerMapperID" class="org.mybatis.spring.mapper.MapperFactoryBean">
75         
76         指定了接口mapperInterface和工厂sqlSessionFactory
77         <property name="mapperInterface" value="com.itheima.mapper.CustomerMapper" />
78         <property name="sqlSessionFactory" ref="sqlSessionFactoryID" />  
79         
80     </bean> -->
81     
82     <!-- 8、Mapper代理开发(基于MapperScannerConfigurer)
83         配置类路径:org.mybatis.spring.mapper.MapperScannerConfigurer
84         指定映射文件所在的包路径:com.itheima.mapper
85      -->
86     <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
87          <property name="basePackage" value="com.itheima.mapper" />
88     </bean>
89     
90     <!-- 9、使用注解,开启扫描
91         Spring注解提供了另外一种高效的注解配置方式,(对包路径下的所有Bean文件进行扫描) 
92         使用 context 命名空间 ,通知Spring扫描指定包下所有Bean类,进行注解解析  
93      --> 
94     <context:component-scan base-package="com.itheima.service" />
95     
96 </beans>

编写MyBatis配置文件mybatis-config.xml;

 1 <?xml version="1.0" encoding="UTF-8" ?>
 2 <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
 3                         "http://mybatis.org/dtd/mybatis-3-config.dtd">
 4 <configuration>
 5 
 6     <!--1、配置别名:POJO类过多时,使用自动扫描包的形式定义别名 
 7         目的:在于减少全限定类名的冗余。
 8     -->
 9     <typeAliases>
10         <package name="com.itheima.po" />
11     </typeAliases>
12    
13     <!--2、配置Mapper的位置 -->
14     <mappers> 
15        
16        <mapper resource="com/itheima/po/CustomerMapper.xml" />
17        
18        <!-- Mapper接口开发方式 -->
19        <mapper resource="com/itheima/mapper/CustomerMapper.xml" />
20        
21     </mappers>
22 </configuration>

引入log4j.properties。

# Global logging configuration
log4j.rootLogger=ERROR, stdout

# MyBatis logging configuration...
log4j.logger.com.itheima=DEBUG

# Console output...
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n

2、传统DAO方式的开发整合

1)、采用传统DAO开发方式进行MyBatis与Spring框架的整合时,可以使用mybatis-spring包中所提供的SqlSessionTemplate类SqlSessionDaoSupport类来实现。

 2)、SqlSessionTemplate:是mybatis-spring的核心类,它负责管理MyBatis的SqlSession(会话对象)调用MyBatis的SQL方法。当调用SQL方法时,SqlSessionTemplate将会保证使用的SqlSession和当前Spring的事务是相关的。它还管理SqlSession的生命周期,包含必要的关闭、提交和回滚操作

3)、SqlSessionDaoSupport:是一个抽象支持类,它继承DaoSupport类,主要是作为DAO的基类来使用。可以通过SqlSessionDaoSupport类的getSqlSession()方法来获取所需的SqlSession
4)、实现持久层:

①客户持久化类:src/com/itheima/po/Customer.java

 1 package com.itheima.po;
 2 /**
 3  * 客户持久化类
 4  */
 5 public class Customer {
 6     private Integer id;       // 主键id
 7     private String username; // 客户名称
 8     private String jobs;      // 职业
 9     private String phone;     // 电话
10     
11     public Integer getId() {
12         return id;
13     }
14     
15     public void setId(Integer id) {
16         this.id = id;
17     }
18     
19     public String getUsername() {
20         return username;
21     }
22     
23     public void setUsername(String username) {
24         this.username = username;
25     }
26     
27     public String getJobs() {
28         return jobs;
29     }
30     
31     public void setJobs(String jobs) {
32         this.jobs = jobs;
33     }
34     
35     public String getPhone() {
36         return phone;
37     }
38     
39     public void setPhone(String phone) {
40         this.phone = phone;
41     }
42     
43     @Override
44     public String toString() {
45         return "Customer [id=" + id + ", username=" + username + ", jobs=" + jobs + ", phone=" + phone + "]";
46     }
47 }

②创建客户映射(映射数据库表)文件:src/com/itheima/po/CustomerMapper.xml

 1 <?xml version="1.0" encoding="UTF-8"?>
 2 <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
 3                     "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
 4                     
 5 <mapper namespace="com.itheima.po.CustomerMapper">
 6     
 7     <!--根据id查询客户信息 -->
 8     <select id="findCustomerById" parameterType="Integer"
 9              resultType="customer">
10         <!-- 属性resultType:用于指定返回结果的类型,这里表示返回的数据是customer对象类型,
11             可见,其直接使用了别名,减少全限定名称的冗余。    
12             相当于把 com.itheima.po.Customer 换成了customer
13          -->
14         select * from t_customer where id = #{id}
15     </select>
16 </mapper>

 ③在MyBatis的配置文件mybatis-config.xml中,配置映射文件src/com/itheima/po/CustomerMapper.xml

<mapper resource="com/itheima/po/CustomerMapper.xml" />

5)、实现DAO层

①创建Dao的接口:CustomerDao

package com.itheima.dao;
import com.itheima.po.Customer;

public interface CustomerDao {
    // 通过id查询客户
    public Customer findCustomerById(Integer id);
}

②创建接口CustomerDao的实现类:src/com/itheima/dao/impl/CustomerDaoImpl.java

 1 package com.itheima.dao.impl;
 2 import org.mybatis.spring.support.SqlSessionDaoSupport;
 3 import com.itheima.dao.CustomerDao;
 4 import com.itheima.po.Customer;
 5 
 6 
 7 /* CustomerDaoImpl类继承了SqlSessionDaoSupport类,并实现了CustomerDao接口。
 8  * 其中SqlSessionDaoSupport类在使用时需要一个SqlSessionFactory或一个SqlSessionTemplate对象
 9  * 所以需要Spring给SqlSessionDaoSupport类的子类对象注入一个SqlSessionFactory或SqlSessionTemplate。
10  * 这样子类中就能通过调用SqlSessionDaoSupport类的getSqlSession()方法来获取SqlSession对象,并使用SqlSession对象中的方法。
11  *  
12  */
13 
14 public class CustomerDaoImpl extends SqlSessionDaoSupport implements CustomerDao {
15     // 通过id查询客户
16     public Customer findCustomerById(Integer id) {
17          return this.getSqlSession().selectOne("com.itheima.po.CustomerMapper.findCustomerById", id);
18     }
19 }

③整合测试:src/com/itheima/test/DaoTest.java

 1 package com.itheima.test;
 2 import org.junit.Test;
 3 import org.springframework.context.ApplicationContext;
 4 import 
 5      org.springframework.context.support.ClassPathXmlApplicationContext;
 6 import com.itheima.dao.CustomerDao;
 7 import com.itheima.mapper.CustomerMapper;
 8 import com.itheima.po.Customer;
 9 /**
10  * DAO测试类
11  */
12 public class DaoTest {
13     @Test
14     public void findCustomerByIdDaoTest(){
15         ApplicationContext act = new ClassPathXmlApplicationContext("applicationContext.xml");
16           // 根据容器中Bean的id来获取指定的Bean
17 //         CustomerDao customerDao = (CustomerDao) act.getBean("customerDaoID");
18          
19          //Spring获取Bean的另一种方式,这种方式不再需要进行强制类型转换了
20          CustomerDao customerDao = act.getBean(CustomerDao.class); 
21          Customer customer = customerDao.findCustomerById(1);
22          System.out.println(customer);
23     }
24 }

④测试结果:

Java EE学习笔记(十)

3、Mapper接口方式的开发整合

1)、在MyBatis+Spring的项目中,虽然使用传统的DAO开发方式可以实现所需功能,但是采用这种方式在实现类中会出现大量的重复代码在方法中也需要指定映射文件中执行语句的id,并且不能保证编写时id的正确性(运行时才能知道)。为此,我们可以使用MyBatis提供的另外一种编程方式,即使用Mapper接口编程

2)、基于MapperFactoryBean的整合

a)、MapperFactoryBean是MyBatis-Spring团队提供的一个用于根据Mapper接口生成Mapper对象的类,该类在Spring配置文件中使用时可以配置以下参数:

mapperInterface:用于指定接口
SqlSessionFactory:用于指定SqlSessionFactory
SqlSessionTemplate:用于指定SqlSessionTemplate。如果与SqlSessionFactory同时设定,则只会启用SqlSessionTemplate

b)、实操MapperFactoryBean是如何来实现MyBatis与Spring的整合:

创建CustomerMapper接口即对应的映射文件:(注意此处就与上面的操作不同了)src/com/itheima/mapper--->CustomerMapper.java

 1 package com.itheima.mapper;
 2 
 3 import java.lang.Integer;
 4 import com.itheima.po.Customer;
 5 public interface CustomerMapper { // 创建CustomerMapper接口
 6     
 7     // 通过id查询客户
 8     public Customer findCustomerById(Integer id);
 9     
10     // 添加客户
11     public void addCustomer(Customer customer);
12 
13 }

②src/com/itheima/mapper--->CustomerMapper.xml

 1 <?xml version="1.0" encoding="UTF-8"?>
 2 <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
 3                 "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
 4             
 5 <mapper namespace="com.itheima.mapper.CustomerMapper">
 6     
 7     <!--1、根据id查询客户信息,返回类型是customer类型
 8         Mapper接口中的方法名和Mapper.xml中定义的每个执行语句的id相同!!!
 9      -->
10     <select id="findCustomerById" parameterType="Integer"
11              resultType="customer">
12         select * from t_customer where id = #{id}
13     </select>
14     
15     <!--2、添加客户信息,参数类型是 customer类型-->
16     <insert id="addCustomer" parameterType="customer">
17         insert into t_customer(username,jobs,phone)
18         values(#{username},#{jobs},#{phone})
19     </insert>
20     
21 </mapper>

③在MyBatis的配置文件mybatis-config.xml中,配置新的映射文件src/com/itheima/mapper/CustomerMapper.xml

<mapper resource="com/itheima/mapper/CustomerMapper.xml" />

④在Spring的配置文件中,创建CustomerMapper的Bean实例id

   <!-- 7、Mapper代理开发(基于MapperFactoryBean) -->
    <bean id="customerMapperID" class="org.mybatis.spring.mapper.MapperFactoryBean">
        
        <!-- 指定了接口mapperInterface和工厂sqlSessionFactory -->
        <property name="mapperInterface" value="com.itheima.mapper.CustomerMapper" />
        <property name="sqlSessionFactory" ref="sqlSessionFactoryID" />  
        
    </bean>

⑤单元测试:

    @Test
    public void findCustomerByIdMapperTest(){    
        ApplicationContext act = new ClassPathXmlApplicationContext("applicationContext.xml");
        CustomerMapper customerMapper = act.getBean(CustomerMapper.class);   
        Customer customer = customerMapper.findCustomerById(1);
        System.out.println(customer);
    }

⑥测试结果:

Java EE学习笔记(十)

c)、Mapper接口编程方式只需要编写Mapper接口相当于DAO接口),然后由MyBatis框架根据接口的定义创建接口的动态代理对象,等同于DAO接口的实现类

3)、虽然使用Mapper接口编程的方式很简单,但是在具体使用时还是需要遵循一些规范。

a)、Mapper接口的名称对应的Mapper.xml映射文件的名称必须一致

b)、Mapper.xml文件中的namespaceMapper接口类路径相同。(即接口文件和映射文件需要放在同一个包中

c)、注意:Mapper接口中的方法名和Mapper.xml中定义的每个执行语句的id相同

d)、Mapper接口中方法的输入参数类型要和Mapper.xml中定义的每个sql的parameterType的类型相同。

e)、Mapper接口方法的输出参数类型要和Mapper.xml中定义的每个sql的resultType的类型相同。

4)、基于MapperScannerConfigurer的整合

a)、在实际的项目中,DAO层会包含很多接口,如果每一个接口都在Spring配置文件中配置,不但会增加工作量,还会使得Spring配置文件非常臃肿。为此,可以采用自动扫描的形式来配置MyBatis中的映射器——采用MapperScannerConfigurer类

b)、MapperScannerConfigurer类在Spring配置文件中可以配置以下属性:

basePackage:指定映射接口文件所在的包路径,当需要扫描多个包时可以使用分号或逗号作为分隔符。指定包路径后,会扫描该包及其子包中的所有文件
annotationClass:指定了要扫描的注解名称,只有被注解标识的类才会被配置为映射器
sqlSessionFactoryBeanName:指定在Spring中定义的SqlSessionFactory的Bean名称。
sqlSessionTemplateBeanName:指定在Spring中定义的SqlSessionTemplate的Bean名称。如果定义此属性,则sqlSessionFactoryBeanName将不起作用。
markerInterface:指定创建映射器的接口

c)、MapperScannerConfigurer的使用非常简单,只需要在Spring的配置文件中编写如下代码:

<!-- Mapper代理开发(基于MapperScannerConfigurer) -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
     <property name="basePackage" value="com.itheima.mapper" />
</bean>

d)、通常情况下,MapperScannerConfigurer在使用时只需通过basePackage属性指定需要扫描的包即可,Spring会自动的通过包中的接口来生成映射器。这使得开发人员可以在编写很少代码的情况下,完成对映射器的配置,从而提高开发效率。对MapperScannerConfigurer进行测试可得与上面相同的结果。

4、 测试事务

1)、在项目中,Service层既是处理业务的地方,又是管理数据库事务的地方。对事务进行测试:

a)、创建Service层

b)、在Service层编写添加客户操作的代码

c)、有意地添加一段异常代码(如int i = 1/0;)来模拟现实中的意外情况

d)、编写测试方法,调用业务层的添加方法。这样,程序在执行到错误代码时就会出现异常

2)、在没有事务管理的情况下,即使出现了异常,数据也会被存储到数据表中;如果添加了事务管理,并且事务管理的配置正确,那么在执行上述操作时,所添加的数据将不能够插入到数据表中

3)在CustomerMapper接口中,添加addCustomer()方法,然后在CustomerMapper映射文件中编写执行插入操作的SQL配置,具体看上面的代码。

①创建业务接口:src/com/itheima/service/CustomerService.java

package com.itheima.service;
import com.itheima.po.Customer;
public interface CustomerService { // 业务层:创建CustomerService接口
    public void addCustomer(Customer customer);
}

②创建业务接口的实现类:com/itheima/service/impl/CustomerServiceImpl.java

 1 package com.itheima.service.impl;
 2 import org.springframework.beans.factory.annotation.Autowired;
 3 import org.springframework.stereotype.Service;
 4 import org.springframework.transaction.annotation.Transactional;
 5 import com.itheima.mapper.CustomerMapper;
 6 import com.itheima.po.Customer;
 7 import com.itheima.service.CustomerService;
 8 
 9 // 使用Spring的注解@Service来标识业务层的类
10 @Service
11 
12 // 使用@Transactional注解来标识事务处理的类
13 @Transactional
14 public class CustomerServiceImpl implements CustomerService {
15     
16     //通过@Autowired注解注入CustomerMapper
17     @Autowired
18     private CustomerMapper customerMapper;
19     
20     //添加客户
21     public void addCustomer(Customer customer) {
22         // 调用接口中的addCustomer()方法
23         this.customerMapper.addCustomer(customer);
24         
25         //模拟添加操作后系统突然出现的异常问题
26         int i = 1 / 0; 
27     }
28 }

③在Spring的配置文件中,编写开启注解扫描的配置代码:

   <!-- 9、使用注解,开启扫描
        Spring注解提供了另外一种高效的注解配置方式,(对包路径下的所有Bean文件进行扫描) 
        使用 context 命名空间 ,通知Spring扫描指定包下所有Bean类,进行注解解析  
     --> 
    <context:component-scan base-package="com.itheima.service" />

④测试类:com/itheima/test/TransactionTest.java

 1 package com.itheima.test;
 2 import org.springframework.context.ApplicationContext;
 3 import org.springframework.context.support.ClassPathXmlApplicationContext;
 4 import com.itheima.po.Customer;
 5 import com.itheima.service.CustomerService;
 6 /**
 7  * 测试事务
 8  */
 9 public class TransactionTest {
10     public static void main(String[] args) {
11         ApplicationContext act = new ClassPathXmlApplicationContext("applicationContext.xml");
12         CustomerService customerService = act.getBean(CustomerService.class);
13         
14         Customer customer = new Customer();
15         customer.setUsername("zhangsan");
16         customer.setJobs("manager");
17         customer.setPhone("13233334444");
18         customerService.addCustomer(customer);
19     }
20 }

⑤先将@Transactional注释掉后运行测试类的结果如下:

Java EE学习笔记(十)

4)、虽抛出异常,但查询数据库mybatis中的t_customer表可知,新添加的数据已经存储在该表中,这就验证了在没有事务管理的情况下,即使出现了异常,数据也会被存储到数据表中

Java EE学习笔记(十)

5)、将注释去掉,再次执行并查询表如图所示:

Java EE学习笔记(十)

 

Java EE学习笔记(十)

这就说明项目中配置的事务是正确的。

个人总结:

本章详细介绍了如何实现SM的整合步骤。个人认为基于Mapper的接口方式实现MyBatis与Spring的整合是相对比较方便和简单的,至少在编码方面能提高开发者的工作效率,但实现DAO层的整合也需充分理解,总之,实践是检验真知的标准!