1、为什么要整合持久层
持久层是帮助我们对数据库进行操作的
常见的持久层框架有:
- JDBC
- Hibernate
- MyBatis
但是这三个持久层技术有大量的代码重复
2、Spring整合MyBatis
Spring对MyBatis进行了代码封装,简便我们的开发过程过程
MyBatis的开发步骤
- 实体
- 实体别名
- 表
- 创建Dao接口
- 创建Mapper文件
- 注册Mapper文件
- MyBatisAPI调用
下面,我们通过代码来演示一遍
添加依赖
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>5.1.4.RELEASE</version> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid-spring-boot-starter</artifactId> <version>1.1.9</version> </dependency> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis-spring</artifactId> <version>1.3.0</version> </dependency> <!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.40</version> </dependency> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.5.6</version> </dependency> <!-- https://mvnrepository.com/artifact/org.springframework/spring-context --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.1.4.RELEASE</version> </dependency> <!-- https://mvnrepository.com/artifact/org.springframework/spring-web --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-web</artifactId> <version>5.1.4.RELEASE</version> </dependency> <!-- slf4j日志 --> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j12</artifactId> <version>1.7.25</version> </dependency> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.17</version> </dependency>
编写实体类
public class User { private Integer id; private String name; private String password; public User(Integer id, String name, String password) { this.id = id; this.name = name; this.password = password; } public User() { } public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } }
编写实体别名
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"> <configuration> <typeAliases> <!--为了后面书写mapper的时候不需要再重复写全限定名--> <typeAlias alias="user" type="com.wx.mybatis.entity.User"></typeAlias> </typeAliases> </configuration>
创建表
创建Dao接口
public interface UserDao { public void save(User user); }
创建Mapper文件
<?xml version="1.0" encoding="utf-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" > <mapper namespace="com.chenxin.spring5.mybatis.dao.UserDao"> <insert id="save" parameterType="user"> insert into t_user(name, password) values (#{name}, #{password}) </insert> </mapper>
注册Mapper文件
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"> <configuration> <typeAliases> <!--为了后面书写mapper的时候不需要再重复写全限定名--> <typeAlias alias="user" type="com.wx.mybatis.entity.User"></typeAlias> </typeAliases> <environments default="mysql"> <environment id="mysql"> <transactionManager type="JDBC"></transactionManager> <dataSource type="POOLED"> <property name="driver" value="com.mysql.jdbc.Driver"></property> <property name="url" value="jdbc:mysql://localhost:3306/users?useSSL=false"></property> <property name="username" value="root"></property> <property name="password" value="root"></property> </dataSource> </environment> </environments> <mappers> <mapper resource="mapper/UserMapper.xml"></mapper> </mappers> </configuration>
调用MyBatisAPI测试
public class TestMyBatis { public static void main(String[] args) throws IOException { //获取文件流 InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml"); //创建sqlsessionFactory工厂对象 SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(inputStream); //得到会话对象 SqlSession sqlSession = factory.openSession(); //获取mapper对象 UserDao mapper = sqlSession.getMapper(UserDao.class); User user = new User("wx", "123"); mapper.save(user); //执行完需要提交事务 sqlSession.commit(); } }
注意的坑:lineNumber:1 columnNumber:1 前言中不允许有内容
有两种情况:
3、MyBatis开发存在的问题
重复的代码非常多
- 实体
- 实体别名
- 表
- 创建Dao接口
- 创建Mapper文件
- 注册Mapper文件
- MyBatisAPI调用
第二步,编写实例别名,是十分繁琐且复杂的一个操作。我们除了名称不一样,其他的基本都一样。
<typeAliases> <!--为了后面书写mapper的时候不需要再重复写全限定名--> <typeAlias alias="user" type="com.wx.mybatis.entity.User"></typeAlias> <typeAlias alias="user1" type="com.wx.mybatis.entity.User1"></typeAlias> <typeAlias alias="user2" type="com.wx.mybatis.entity.User2"></typeAlias> ... </typeAliases>
随着我们的项目越来越大,实体类越来越多,每一个实体类对应一个别名。这样写,显然是不可取的,而且还很容易漏掉注册mapper文件
第六步,注册mapper文件也是一样
<mappers> <mapper resource="UserMapper.xml"></mapper> <mapper resource="UserMapper1.xml"></mapper> <mapper resource="UserMapper2.xml"></mapper> ... </mappers>
第七步,调用MyBatis的API也有很多重复的代码
为了执行自己的业务逻辑,我们这几行代码是不是每个业务逻辑都需要写一遍呢?这也太麻烦了吧
//获取文件流 InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml"); //创建sqlsessionFactory工厂对象 SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(inputStream); //得到会话对象 SqlSession sqlSession = factory.openSession(); //获取mapper对象 UserDao mapper = sqlSession.getMapper(UserDao.class);
只有最后获取mapper对象的时候不同,根据传入的类文件,获取到对应的mapper文件
Spring整合MyBatis的核心就是减少我们的第二步、第六步、第七步的代码重复
4、Spring整合MyBatis思路做法
Spring真核MyBatis的整合,其实是对这段代码的封装
//获取文件流 InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml"); //创建sqlsessionFactory工厂对象 SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(inputStream); //得到会话对象 SqlSession sqlSession = factory.openSession(); //获取mapper对象 UserDao mapper = sqlSession.getMapper(UserDao.class);
前两行是通过文件流创建出来SqlSessionFactory工厂对象,那么这个对象直接交由Spring来进行管理,<baean id='sqlSessionFactoryBean' class='SqlSessionFactoryBean全类路径'></bean>
第一行是读取配置文件,然后根据配置创建我们的工厂对象,既然spring现在管理了工厂对象,我们只需要将配置注入到对象中即可。
配置文件中主要是三部分:
- typeAliases别名
- dataSource数据源
- mappers文件
整合之前,需要先添加几个maven坐标
<dependency> <groupId>com.alibaba</groupId> <artifactId>druid-spring-boot-starter</artifactId> <version>1.1.9</version> </dependency> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis-spring</artifactId> <version>1.2.2</version> </dependency> <!-- slf4j日志 --> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j12</artifactId> <version>1.7.25</version> </dependency> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.17</version> </dependency>
Log4j的配置文件
resources # resources⽂件夹根⽬录下 ### ### 配置根 log4j.rootLogger = debug,console ### ### ⽇志输出到控制台显示 log4j.appender.console=org.apache.log4j.ConsoleAppender log4j.appender.console.Target=System.out log4j.appender.console.layout=org.apache.log4j.PatternLayout log4j.appender.console.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n
既然是SqlSessionFactory工厂对象的依赖,我们可以将他作为属性,注入进来
<?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="dataSource" class="com.alibaba.druid.pool.DruidDataSource"> <property name="driverClassName" value="com.mysql.jdbc.Driver"> </property> <property name="url" value="jdbc:mysql://localhost:3306/users?useSSL=false"></property> <property name="username" value="root"></property> <property name="password" value="527713"></property> </bean> <!--创建SqlsessionFactoryBean--> <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource" ref="dataSource"></property> <!-- 执行实体对应的包--> <property name="typeAliasesPackage" value="com.wx.mybatis.entity"></property> <property name="mapperLocations"> <list> <value>classpath:mapper/*Mapper.xml</value> </list> </property> </bean> </beans>
前面两行,Spring通过注入的方式来帮我们管理,再来看看后面两行
//得到会话对象 SqlSession sqlSession = factory.openSession(); //获取mapper对象 UserDao mapper = sqlSession.getMapper(UserDao.class);
我们可以通过SqlSessionFactoryBean工厂对象创建除了SqlSessionFactory对象,我们只需要在获取SqlSession对象和Dao接口的class。这样我们就可以使用依赖,将我们需要的这两个对象依赖进来,spring为我们提供了一个对象,叫MapperScannerConfigure
<bean id="scanner" class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"></property> <!--设置dao所在的包路径--> <property name="basePackage" value="com.wx.mybatis.entity"></property> </bean>
这个MapperScannerConfigure类最后帮我们创建好了dao对象,我们只需要ctx.getBean("beanid")即可,他是采用的驼峰命名
5、Spring和MyBatis整合开发步骤
去掉上面的开发步骤,我们只剩下以下的步骤:
- 实体
- 表
- 创建Dao接口
- 创建Mapper文件
其实这里的实体和表,我们也是无法省略的。spring整合mybatis的完整配置文件
<?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="dataSource" class="com.alibaba.druid.pool.DruidDataSource"> <property name="driverClassName" value="com.mysql.jdbc.Driver"> </property> <property name="url" value="jdbc:mysql://localhost:3306/users?useSSL=false"></property> <property name="username" value="root"></property> <property name="password" value="527713"></property> </bean> <!--创建SqlsessionFactoryBean--> <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource" ref="dataSource"></property> <!-- 执行实体对应的包--> <property name="typeAliasesPackage" value="com.wx.mybatis.entity"></property> <property name="mapperLocations"> <list> <value>classpath:mapper/*Mapper.xml</value> </list> </property> </bean> <bean id="scanner" class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"></property> <!--设置dao所在的包路径--> <property name="basePackage" value="com.wx.mybatis.entity"></property> </bean> </beans>
其实我们的创建dao接口,就是我们之前经常使用ctx.getBean()获取到的,下面我们来测试下
6、Spring和MyBatis整合细节
在我们还没有整合spring的时候,我们的是使用sqlSession手动sqlSession.commit();提交事务的
2021-04-27 22:00:06 DEBUG SpringManagedTransaction:89 - JDBC Connection [com.mysql.jdbc.JDBC4Connection@366ef90e] will not be managed by Spring
控制台的这个信息告诉我们,事务不会被spring进行管理,那么到底是谁在帮助我们管理事务呢?
事务不是由MbBatis来控制的,因为我们没有执行commit()操作;Spring也告诉我们,事务不是有它来控制的。
其实我们的事务是由连接池来控制的。Connection.setAutoCommit(ture);来控制事务自动提交。