Spring最重要的功能毫无疑问就是操作数据。数据库的百年城是互联网编程的基础,Spring为开发者提供了JDBC模板模式,那就是它自身的JdbcTemplate。Spring还提供了TransactionTemplate支持事务的模板。Spring并没有支持MyBatis,好在MyBatis社区开发了接入Spring的开发包,该包也提供了SqlSessionTemplate给开发者使用,该包还可以屏蔽SqlSessionTemplate这样的功能性代码,可以在编程中擦除SqlSessionTemplate让开发者直接使用接口编程,大大提高了编码的可读性。
一、传统JDBC代码的弊端
例如,下面的代码的作用是,通过JDBC读取数据库,然后将结果集以POJO的形式返回。
package com.ssm.chapter12.jdbc; import java.sql.Connection; import java.sql.DriverManager; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import com.ssm.chapter12.pojo.Role; public class JdbcExample { public Role getRole(Long id) { Role role = null; // 声明JDBC变量 Connection con = null; PreparedStatement ps = null; ResultSet rs = null; try { // 注册驱动程序 Class.forName("com.mysql.jdbc.Driver"); // 获取连接 con = DriverManager.getConnection("jdbc:mysql://localhost:3306/chapter12", "root", "123456"); // 预编译SQL ps = con.prepareStatement("select id, role_name, note from t_role where id = ?"); // 设置参数 ps.setLong(1, id); // 执行SQL rs = ps.executeQuery(); // 组装结果集返回到POJO while (rs.next()) { role = new Role(); role.setId(rs.getLong(1)); role.setRoleName(rs.getString(2)); role.setNote(rs.getString(3)); } } catch (ClassNotFoundException | SQLException e) { // 异常处理 e.printStackTrace(); } finally { // 关闭数据库连接资源 try { if (rs != null && !rs.isClosed()) { rs.close(); } } catch (SQLException e) { e.printStackTrace(); } try { if (ps != null && !ps.isClosed()) { ps.close(); } } catch (SQLException e) { e.printStackTrace(); } try { if (con != null && !con.isClosed()) { con.close(); } } catch (SQLException e) { e.printStackTrace(); } } return role; } }
传统的JDBC
从代码可以看出,即使是执行一条简单的SQL,其过程也不简单,太多的try...catch...finally...语句,造成了代码泛滥。
在JDBC中,大量的JDBC代码都是用于Chau给你姐爱你连接和语句以及异常处理的样版代码。
实际上,这些样版代码是非常重要的。清理资源和处理错误确保了数据访问的健壮性。如果没有它们的话,就不会发现错误而且资源也会处于打开的状态,这将会导致意外的代码和资源泄露。我们不仅需要这些代码,而且还要保证它是正确的。基于这样的原因,才需要框架来保证这些代码只写一次而且是正确的。
二、使用Spring配置数据库资源
在Spring中配置数据库资源很简单,在实际工作中,大部分会配置成数据库连接池,既可以通过使用Spring内部提供的类,也可以使用第三方数据库连接池或者从Web服务器中通过JNDI获取数据源。由于使用了第三方的类,一般而言在工程中会偏向于采用XML的方式进行配置。
1.使用简单数据库配置
Spring提供了一个类org.springframework.jdbc.datasource.SimpleDriverDataSource可以支持简单数据库配置,但是不支持数据库连接池。
这种配置一般用于测试,因为它不是一个数据库连接池。
<!-- <bean id="dataSource" class="org.springframework.jdbc.datasource.SimpleDriverDataSource"> <property name="username" value="root" /> <property name="password" value="123456" /> <property name="driverClass" value="com.mysql.jdbc.Driver" /> <property name="url" value="jdbc:mysql://localhost:3306/chapter12" /> </bean> -->
2.使用第三方数据库连接池
当使用第三方数据库连接池时,比如DBCP数据库连接池,需要下载第三方包common-dbcp.jar和common-pool包,然后在Spring中简单配置后,就能够使用它了。
<!-- 数据库连接池 --> <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"> <property name="driverClassName" value="com.mysql.jdbc.Driver" /> <property name="url" value="jdbc:mysql://localhost:3306/chapter12" /> <property name="username" value="root" /> <property name="password" value="123456" /> <!--连接池的最大数据库连接数 --> <property name="maxActive" value="255" /> <!--最大等待连接中的数量 --> <property name="maxIdle" value="5" /> <!--最大等待毫秒数 --> <property name="maxWait" value="10000" /> </bean>
3.使用JNDI数据库连接池
在Tomcat、WebLogic等Java EE服务器上配置数据源,这是他存在一个JNDI的名称。也可以通过Spring所提供的JNDI机制获取对应的数据源,这也是常用的方式。
假设在Tomcat上配置了JNDI为jdbc/chapter12的数据源,这样就可以在Web工程中获取这个JNDI数据源。
<bean id="dataSource" class="org.springframework.jndi.JndiObjectFactoryBean"> <property name="jndiName" vaule="java:comp/env/jdbc/chapter12" /> </bean>
三、JDBC代码失控的解决方案--JdbcTemplate
JdbcTemplate是Spring针对JDBC代码失控提供的解决方案,虽然不算成功,但是用技术提供模板化的编程,减少了开发者的工作量。
Spring的JDBC框架承担了资源管理和异常处理的工作,从而简化了JDBC代码,让我们只需编写从数据库读写数据的必须代码。
1.配置JdbcTemplate,其中dataSource在之前的三种方法中选一种即可
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"> <property name="dataSource" ref="dataSource" /> </bean>
2.配置好了JdbcTemplate和dataSource就可以操作JdbcTemplate了,假设Spring配置文件为spring-cfg.xml,则要想完成第一个例子中JDBC完成的工作,只需要:
public static void tesSpring() { ApplicationContext ctx = new ClassPathXmlApplicationContext("spring-cfg.xml"); JdbcTemplate jdbcTemplate = ctx.getBean(JdbcTemplate.class); Long id = 1L; String sql = "select id, role_name, note from t_role where id = " + id; Role role = jdbcTemplate.queryForObject(sql, new RowMapper<Role>() { @Override public Role mapRow(ResultSet rs, int rownum) throws SQLException { Role result = new Role(); result.setId(rs.getLong("id")); result.setRoleName(rs.getString("role_name")); result.setNote(rs.getString("note")); return result; } }); System.out.println(role.getRoleName()); }
其中,使用了jdbcTemplate的queryForObject方法,它包含了两个参数,一个是SQL,另一个是RowMapper接口。在mapRow()方法中,从ResultSet对象中取出查询得到的数据,组装成一个Role对象,而无需再写任何关闭数据库资源的代码。因为JdbcTemplate内部实现了它们,这便是Spring所提供的模板规则。
3.JdbcTemplate的增、删、改、查
package com.ssm.chapter12.jdbc; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.Statement; import java.util.List; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import org.springframework.jdbc.core.JdbcTemplate; import com.ssm.chapter12.pojo.Role; public class JdbcTemplateTest { public static void main(String[] args) { ApplicationContext ctx = new ClassPathXmlApplicationContext("spring-cfg.xml"); JdbcTemplate jdbcTemplate = ctx.getBean(JdbcTemplate.class); JdbcTemplateTest test = new JdbcTemplateTest(); test.getRoleByConnectionCallback(jdbcTemplate, 1L); test.getRoleByStatementCallback(jdbcTemplate, 1L); test.insertRole(jdbcTemplate); List roleList = test.findRole(jdbcTemplate, "role"); System.out.println(roleList.size()); Role role = new Role(); role.setId(1L); role.setRoleName("update_role_name_1"); role.setNote("update_note_1"); test.updateRole(jdbcTemplate, role); test.deleteRole(jdbcTemplate, 1L); } /*** * 插入角色 * @param jdbcTemplate --模板 * @return 影响条数 */ public int insertRole(JdbcTemplate jdbcTemplate) { String roleName = "role_name_1"; String note = "note_1"; String sql = "insert into t_role(role_name, note) values(?, ?)"; return jdbcTemplate.update(sql, roleName, note); } /** * 删除角色 * @param jdbcTemplate -- 模板 * @param id -- 角色编号,主键 * @return 影响条数 */ public int deleteRole(JdbcTemplate jdbcTemplate, Long id) { String sql = "delete from t_role where id=?"; return jdbcTemplate.update(sql, id); } public int updateRole(JdbcTemplate jdbcTemplate, Role role) { String sql = "update t_role set role_name=?, note = ? where id = ?"; return jdbcTemplate.update(sql, role.getRoleName(), role.getNote(), role.getId()); } /** * 查询角色列表 * @param jdbcTemplate--模板 * @param roleName --角色名称 * @return 角色列表 */ public List<Role> findRole(JdbcTemplate jdbcTemplate, String roleName) { String sql = "select id, role_name, note from t_role where role_name like concat(‘%‘,?, ‘%‘)"; Object[] params = {roleName};//组织参数 //使用RowMapper接口组织返回(使用lambda表达式) List<Role> list = jdbcTemplate.query(sql, params, (ResultSet rs, int rowNum) -> { Role result = new Role(); result.setId(rs.getLong("id")); result.setRoleName(rs.getString("role_name")); result.setNote(rs.getString("note")); return result; }); return list; } /** * 使用ConnectionCallback接口进行回调 * @param jdbcTemplate 模板 * @param id 角色编号 * @return 返回角色 */ public Role getRoleByConnectionCallback(JdbcTemplate jdbcTemplate, Long id) { Role role = null; //这里写成Java 8的Lambda表达式,如果你使用低版本的Java,需要使用ConnectionCallback匿名类 role = jdbcTemplate.execute((Connection con) -> { Role result = null; String sql = "select id, role_name, note from t_role where id = ?"; PreparedStatement ps = con.prepareStatement(sql); ps.setLong(1, id); ResultSet rs = ps.executeQuery(); while (rs.next()) { result = new Role(); result.setId(rs.getLong("id")); result.setNote(rs.getString("note")); result.setRoleName(rs.getString("role_name")); } return result; }); return role; } /** * 使用StatementCallback接口进行回调 * @param jdbcTemplate模板 * @param id角色编号 * @return返回角色 */ public Role getRoleByStatementCallback(JdbcTemplate jdbcTemplate, Long id) { Role role = null; //这里写成Java 8的lambda表达式,如果你使用低版本的Java,需要使用StatementCallback的匿名类 role = jdbcTemplate.execute((Statement stmt) -> { Role result = null; String sql = "select id, role_name, note from t_role where id = " + id; ResultSet rs = stmt.executeQuery(sql); while (rs.next()) { result = new Role(); result.setId(rs.getLong("id")); result.setNote(rs.getString("note")); result.setRoleName(rs.getString("role_name")); } return result; }); return role; } }
JdbcTemplate的增、删、改、查
4.执行多条SQL
一个JdbcTemplate只执行了一条SQL,当需要多次执行SQL时,可以使用execute方法。它将允许传递ConnectionCallback或者StatementCallback等接口进行回调。
/** * 使用ConnectionCallback接口进行回调 * @param jdbcTemplate 模板 * @param id 角色编号 * @return 返回角色 */ public Role getRoleByConnectionCallback(JdbcTemplate jdbcTemplate, Long id) { Role role = null; //这里写成Java 8的Lambda表达式,如果你使用低版本的Java,需要使用ConnectionCallback匿名类 role = jdbcTemplate.execute((Connection con) -> { Role result = null; String sql = "select id, role_name, note from t_role where id = ?"; PreparedStatement ps = con.prepareStatement(sql); ps.setLong(1, id); ResultSet rs = ps.executeQuery(); while (rs.next()) { result = new Role(); result.setId(rs.getLong("id")); result.setNote(rs.getString("note")); result.setRoleName(rs.getString("role_name")); } return result; }); return role; } /** * 使用StatementCallback接口进行回调 * @param jdbcTemplate模板 * @param id角色编号 * @return返回角色 */ public Role getRoleByStatementCallback(JdbcTemplate jdbcTemplate, Long id) { Role role = null; //这里写成Java 8的lambda表达式,如果你使用低版本的Java,需要使用StatementCallback的匿名类 role = jdbcTemplate.execute((Statement stmt) -> { Role result = null; String sql = "select id, role_name, note from t_role where id = " + id; ResultSet rs = stmt.executeQuery(sql); while (rs.next()) { result = new Role(); result.setId(rs.getLong("id")); result.setNote(rs.getString("note")); result.setRoleName(rs.getString("role_name")); } return result; }); return role; }
执行多条SQL
四、MyBatis-Spring项目
目前大部分的互联网项目中都使用SSM搭建平台的。使用Spring IoC可以有效管理各类Java资源,达到即插即拔的功能;通过AOP框架,数据库事务可以委托给Spring处理,消除很大一部分的事务代码,配合MyBatis的高灵活、可配置、可优化SQL等特性,完全可以构建高性能的大型网站。
在Spring环境中使用MyBatis也更加简单,节省了不少代码,甚至可以不用SqlSessionFactory、SqlSession等对象。因为MyBatis-Spring为我们封装了它们。
配置MyBatis-Spring项目需要下面几步:
- 配置数据源
- 配置SqlSessionFactory
- 可以选择的配置由SqlSessionTemplate,在同时配置SqlSessionTemplate和SqlSessionFactory的情况下,优先采用SqlSessionTemplate
- 配置Mapper,可以配置单个Mapper,也可以通过扫描的方法生成Mapper,比较灵活。此时Spring IoC会生成对应接口的实例,这样就可以通过注入的方式来获取资源。
- 事务管理。
1.配置SqlSessionFactory Bean
MyBatis中SqlSessionFactory是产生SqlSession的基础,因此配置SqlSessionFactory十分关键。在MyBatis-Spring项目中提供了SqlSessionFactoryBean支持SqlSessionFactory的配置。
(1)在Spring的配置文件spring-cfg.xml中配置SqlSessionFactoryBean
这里虽然只是配置了数据源,然后引入了一个MyBatis配置文件,这样的好处在于不至于使得SqlSessionFactoryBean的配置全部依赖于Spring提供的规则,导致配置的复杂性。
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource" ref="dataSource" /> <property name="configLocation" value="classpath:sqlMapConfig.xml" /> </bean>
(2)引入的MyBatis配置文件sqlMapConfig.xml
<?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> <settings> <!-- 这个配置使全局的映射器启用或禁用缓存 --> <setting name="cacheEnabled" value="true" /> <!-- 允许 JDBC 支持生成的键。需要适合[修改为:适当]的驱动。如果设置为true,则这个设置强制生成的键被使用,尽管一些驱动拒绝兼容但仍然有效(比如 Derby) --> <setting name="useGeneratedKeys" value="true" /> <!-- 配置默认的执行器。SIMPLE 执行器没有什么特别之处。REUSE 执行器重用预处理语句。BATCH 执行器重用语句和批量更新 --> <setting name="defaultExecutorType" value="REUSE" /> <!-- 全局启用或禁用延迟加载。当禁用时,所有关联对象都会即时加载 --> <setting name="lazyLoadingEnabled" value="true"/> <!-- 设置超时时间,它决定驱动等待一个数据库响应的时间 --> <setting name="defaultStatementTimeout" value="25000"/> </settings> <!-- 别名配置 --> <typeAliases> <typeAlias alias="role" type="com.ssm.chapter12.pojo.Role" /> </typeAliases> <!-- 指定映射器路径 --> <mappers> <mapper resource="com/ssm/chapter12/sql/mapper/RoleMapper.xml" /> </mappers> </configuration>
(3)然后引入映射器RoleMapper.xml
<?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.ssm.chapter12.mapper.RoleMapper"> <insert id="insertRole" useGeneratedKeys="true" keyProperty="id"> insert into t_role(role_name, note) values (#{roleName}, #{note}) </insert> <delete id="deleteRole" parameterType="long"> delete from t_role where id=#{id} </delete> <select id="getRole" parameterType="long" resultType="role"> select id, role_name as roleName, note from t_role where id = #{id} </select> <update id="updateRole" parameterType="role"> update t_role set role_name = #{roleName}, note = #{roleName} where id = #{id} </update> </mapper>
(4)与映射器配置文件对应的接口类java文件RoleMapper.java
package com.ssm.chapter12.mapper; import org.apache.ibatis.annotations.Param; import org.springframework.stereotype.Repository; import com.ssm.chapter12.pojo.Role; public interface RoleMapper { public int insertRole(Role role); public Role getRole(@Param("id") Long id); public int updateRole(Role role); public int deleteRole(@Param("id") Long id); }
至此,MyBatis框架的主要代码就已经配置完成了,但是,由于RoleMapper是一个接口,而不是一个类,它没有办法产生示例,因此应该如何配置呢?
2.SqlSessionTemplate组件
SqlSessionTemplate并不是一个必需配置的组件,但是它也存在一定的价值。首先,它是线程安全的类,也就是确保每个线程使用的SqlSession唯一且不互相冲突。其次,它提供了一系列的功能,比如增、删、改、查等常用功能。
配置方法如下:SqlSessionTemplate类要通过带有参数的构造方法去创建对象,常用的参数是sqlSessionFactory和MyBatis执行器(Executor)类型,取值范围是SIMPLE、REUSE、BATCH。
<bean id="sqlSessionTemplate" class="org.mybatis.spring.SqlSessionTemplate"> <constructor-arg ref="sqlSessionFactory" /> <!-- <constructor-arg value="BATCH"/> --> </bean>
SqlSessionTemplate配置完成就可以使用它了,例如:
public static void testSqlSessionTemplate() { ApplicationContext ctx = new ClassPathXmlApplicationContext("spring-cfg.xml"); // ctx为Spring IoC容器 SqlSessionTemplate sqlSessionTemplate = ctx.getBean(SqlSessionTemplate.class); Role role = new Role(); role.setRoleName("role_name_sqlSessionTemplate"); role.setNote("note_sqlSessionTemplate"); sqlSessionTemplate.insert("com.ssm.chapter12.mapper.RoleMapper.insertRole", role); Long id = role.getId(); sqlSessionTemplate.selectOne("com.ssm.chapter12.mapper.RoleMapper.getRole", id); role.setNote("update_sqlSessionTemplate"); sqlSessionTemplate.update("com.ssm.chapter12.mapper.RoleMapper.updateRole", role); sqlSessionTemplate.delete("com.ssm.chapter12.mapper.RoleMapper.deleteRole", id); }
运行结果:从结果中可以看到,每运行一个SqlSessionTemplate时,它就会重新获取一个新的SqlSession,也就是说每一个SqlSessionTemplate运行的时候会产生新的SqlSession,所以每一个方法都是独立的SqlSession,这意味着它是安全的线程。
SqlSessionTemplate目前运用已经不多,它需要使用字符串表明运行哪个SQL,字符串包含业务含义,只是功能性代码,并不符合面向对象的规范。与此同时,使用字符串时,IDE无法检查代码逻辑的正确性,所以这样的用法渐渐被人们抛弃了。但是,SqlSessionTemplate允许配置执行器的类型,当同时配置SqlSessionTemplate和SqlSessionFactory时,优先采用SqlSessionTemplate。
DEBUG 2018-10-09 17:32:51,048 org.mybatis.spring.SqlSessionUtils: Creating a new SqlSession DEBUG 2018-10-09 17:32:51,052 org.mybatis.spring.SqlSessionUtils: SqlSession [[email protected]] was not registered for synchronization because synchronization is not active DEBUG 2018-10-09 17:32:51,065 org.springframework.jdbc.datasource.DataSourceUtils: Fetching JDBC Connection from DataSource DEBUG 2018-10-09 17:32:51,329 org.mybatis.spring.transaction.SpringManagedTransaction: JDBC Connection [jdbc:mysql://localhost:3306/chapter6?useSSL=false, [email protected], MySQL Connector Java] will not be managed by Spring DEBUG 2018-10-09 17:32:51,333 org.apache.ibatis.logging.jdbc.BaseJdbcLogger: ==> Preparing: insert into t_role(role_name, note) values (?, ?) DEBUG 2018-10-09 17:32:51,367 org.apache.ibatis.logging.jdbc.BaseJdbcLogger: ==> Parameters: role_name_sqlSessionTemplate(String), note_sqlSessionTemplate(String) DEBUG 2018-10-09 17:32:51,372 org.apache.ibatis.logging.jdbc.BaseJdbcLogger: <== Updates: 1 DEBUG 2018-10-09 17:32:51,375 org.mybatis.spring.SqlSessionUtils: Closing non transactional SqlSession [[email protected]] DEBUG 2018-10-09 17:32:51,375 org.springframework.jdbc.datasource.DataSourceUtils: Returning JDBC Connection to DataSource DEBUG 2018-10-09 17:32:51,375 org.mybatis.spring.SqlSessionUtils: Creating a new SqlSession DEBUG 2018-10-09 17:32:51,375 org.mybatis.spring.SqlSessionUtils: SqlSession [[email protected]] was not registered for synchronization because synchronization is not active DEBUG 2018-10-09 17:32:51,377 org.springframework.jdbc.datasource.DataSourceUtils: Fetching JDBC Connection from DataSource DEBUG 2018-10-09 17:32:51,378 org.mybatis.spring.transaction.SpringManagedTransaction: JDBC Connection [jdbc:mysql://localhost:3306/chapter6?useSSL=false, [email protected], MySQL Connector Java] will not be managed by Spring DEBUG 2018-10-09 17:32:51,378 org.apache.ibatis.logging.jdbc.BaseJdbcLogger: ==> Preparing: select id, role_name as roleName, note from t_role where id = ? DEBUG 2018-10-09 17:32:51,378 org.apache.ibatis.logging.jdbc.BaseJdbcLogger: ==> Parameters: 7(Long) DEBUG 2018-10-09 17:32:51,390 org.apache.ibatis.logging.jdbc.BaseJdbcLogger: <== Total: 1 DEBUG 2018-10-09 17:32:51,393 org.mybatis.spring.SqlSessionUtils: Closing non transactional SqlSession [[email protected]] DEBUG 2018-10-09 17:32:51,393 org.springframework.jdbc.datasource.DataSourceUtils: Returning JDBC Connection to DataSource DEBUG 2018-10-09 17:32:51,393 org.mybatis.spring.SqlSessionUtils: Creating a new SqlSession DEBUG 2018-10-09 17:32:51,393 org.mybatis.spring.SqlSessionUtils: SqlSession [[email protected]] was not registered for synchronization because synchronization is not active DEBUG 2018-10-09 17:32:51,393 org.springframework.jdbc.datasource.DataSourceUtils: Fetching JDBC Connection from DataSource DEBUG 2018-10-09 17:32:51,394 org.mybatis.spring.transaction.SpringManagedTransaction: JDBC Connection [jdbc:mysql://localhost:3306/chapter6?useSSL=false, [email protected], MySQL Connector Java] will not be managed by Spring DEBUG 2018-10-09 17:32:51,394 org.apache.ibatis.logging.jdbc.BaseJdbcLogger: ==> Preparing: update t_role set role_name = ?, note = ? where id = ? DEBUG 2018-10-09 17:32:51,394 org.apache.ibatis.logging.jdbc.BaseJdbcLogger: ==> Parameters: role_name_sqlSessionTemplate(String), role_name_sqlSessionTemplate(String), 7(Long) DEBUG 2018-10-09 17:32:51,397 org.apache.ibatis.logging.jdbc.BaseJdbcLogger: <== Updates: 1 DEBUG 2018-10-09 17:32:51,397 org.mybatis.spring.SqlSessionUtils: Closing non transactional SqlSession [[email protected]] DEBUG 2018-10-09 17:32:51,397 org.springframework.jdbc.datasource.DataSourceUtils: Returning JDBC Connection to DataSource DEBUG 2018-10-09 17:32:51,397 org.mybatis.spring.SqlSessionUtils: Creating a new SqlSession DEBUG 2018-10-09 17:32:51,397 org.mybatis.spring.SqlSessionUtils: SqlSession [[email protected]] was not registered for synchronization because synchronization is not active DEBUG 2018-10-09 17:32:51,397 org.springframework.jdbc.datasource.DataSourceUtils: Fetching JDBC Connection from DataSource DEBUG 2018-10-09 17:32:51,398 org.mybatis.spring.transaction.SpringManagedTransaction: JDBC Connection [jdbc:mysql://localhost:3306/chapter6?useSSL=false, [email protected], MySQL Connector Java] will not be managed by Spring DEBUG 2018-10-09 17:32:51,398 org.apache.ibatis.logging.jdbc.BaseJdbcLogger: ==> Preparing: delete from t_role where id=? DEBUG 2018-10-09 17:32:51,398 org.apache.ibatis.logging.jdbc.BaseJdbcLogger: ==> Parameters: 7(Long) DEBUG 2018-10-09 17:32:51,400 org.apache.ibatis.logging.jdbc.BaseJdbcLogger: <== Updates: 1 DEBUG 2018-10-09 17:32:51,400 org.mybatis.spring.SqlSessionUtils: Closing non transactional SqlSession [[email protected]] DEBUG 2018-10-09 17:32:51,400 org.springframework.jdbc.datasource.DataSourceUtils: Returning JDBC Connection to DataSource
3.配置MapperFactory Bean
MyBatis的运行只需要提供类似于RoleMapper.java的接口,而无需提供一个实现类。而根据MyBatis的运行原理,它是由MyBatis体系创建的动态代理对象运行的,所以Spring也没有办法为其生成一个实现类。为了解决这个问题,MyBatis-Spring项目提供了一个MapperFactoryBean类作为中介,可以通过配置这个类来实现想要的Mapper。使用了Mapper接口编程方式可以有效地在逻辑代码中擦除SqlSessionTemplate,这样代码就按照面向对象的规范进行编写了。
配置RoleMapper对象:
<bean id="roleMapper" class="org.mybatis.spring.mapper.MapperFactoryBean"> <property name="mapperInterface" value="com.ssm.chapter12.mapper.RoleMapper" /> <property name="sqlSessionFactory" ref="sqlSessionFactory" /> <property name="sqlSessionTemplate" ref="sqlSessionTemplate"/> </bean>
有三个属性:
- mapperInterface
- sqlSessionFactory
- SqlSessionTemplate
其中,如果同时配置sqlSessionFactory和SqlSessionTemplate,那么就会启用sqlSessionFactory,而SqlSessionTemplate作废。
可以通过RoleMapper roleMapper = ctx.getBean(RoleMapper.class);来获取映射器
public static void testRoleMapper() { ApplicationContext ctx = new ClassPathXmlApplicationContext("spring-cfg.xml"); RoleMapper roleMapper = ctx.getBean(RoleMapper.class); roleMapper.getRole(2L); }
4.配置MapperScannerConfigurer
在项目比较大的情况下,如果一个个配置Mapper会造成配置量大的问题,这显然不利于开发,因此可以使用MapperScannerConfigurer类来用扫描的形式去生产对应的Mapper。
在Spring配置前需要给Mapper一个注解,在Spring中往往是使用@Repository表示DAO层的,
package com.ssm.chapter12.mapper; import org.apache.ibatis.annotations.Param; import org.springframework.stereotype.Repository; import com.ssm.chapter12.pojo.Role; @Repository public interface RoleMapper { public int insertRole(Role role); public Role getRole(@Param("id") Long id); public int updateRole(Role role); public int deleteRole(@Param("id") Long id); }
然后在Spring配置文件中进行配置:在配置中:
第一行:basePackage指定让Spring自动扫描的包,它会逐层深入扫描,如果遇到多个包可以使用半角逗号分隔。
第二行:指定在Spring中定义的sqlSessionFactory的Bean名称。
第三行:如果类被annotationClass声明的注解标识的时候,才进行扫描。这里是只将被@Repository注解的接口类注册成对应的Mapper。
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <property name="basePackage" value="com.ssm.chapter12.mapper" /> <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory" /> <!-- 使用sqlSessionTemplateBeanName将覆盖sqlSessionFactoryBeanName的配置 --> <!-- <property name="sqlSessionTemplateBeanName" value="sqlSessionFactory"/> --> <!-- 指定标注才扫描成为Mapper --> <property name="annotationClass" value="org.springframework.stereotype.Repository" /> </bean>
5.测试Spirng+Mybatis
经过上面的归纳认识,整理出一份标准的XML配置文件:包括dataSource、sqlSessionFactory和MapperScannerConfigurer
<?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-4.0.xsd"> <!-- 数据库连接池 --> <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"> <property name="driverClassName" value="com.mysql.jdbc.Driver" /> <property name="url" value="jdbc:mysql://localhost:3306/chapter6?useSSL=false" /> <property name="username" value="root" /> <property name="password" value="bjtungirc" /> <!--连接池的最大数据库连接数 --> <property name="maxActive" value="255" /> <!--最大等待连接中的数量 --> <property name="maxIdle" value="5" /> <!--最大等待毫秒数 --> <property name="maxWait" value="10000" /> </bean> <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource" ref="dataSource" /> <property name="configLocation" value="classpath:sqlMapConfig.xml" /> </bean> <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <property name="basePackage" value="com.ssm.chapter12.mapper" /> <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory" /> <!-- 使用sqlSessionTemplateBeanName将覆盖sqlSessionFactoryBeanName的配置 --> <!-- <property name="sqlSessionTemplateBeanName" value="sqlSessionFactory"/> --> <!-- 指定标注才扫描成为Mapper --> <property name="annotationClass" value="org.springframework.stereotype.Repository" /> </bean> </beans>
验证方法:
public static void testMybatisSpring() { ApplicationContext ctx = new ClassPathXmlApplicationContext("spring-cfg.xml"); // ctx为Spring IoC容器 RoleMapper roleMapper = ctx.getBean(RoleMapper.class); Role role = new Role(); role.setRoleName("role_name_mapper"); role.setNote("note_mapper"); roleMapper.insertRole(role); Long id = role.getId(); roleMapper.getRole(id); role.setNote("note_mapper_update"); roleMapper.updateRole(role); roleMapper.deleteRole(id); }
输出结果:从日志中可以看出每当使用一个RoleMapper接口的方法吗,它就会产生一个新的SqlSession,运行完成后就会自动关闭。
从关闭的日志Closing non transactional SqlSession中可以看出是在一个非事务的场景下运行,所以这里并不完整,只是简单地使用了数据库,并没有启动数据库事务。
DEBUG 2018-10-09 18:17:36,687 org.mybatis.spring.SqlSessionUtils: Creating a new SqlSession DEBUG 2018-10-09 18:17:36,692 org.mybatis.spring.SqlSessionUtils: SqlSession [[email protected]] was not registered for synchronization because synchronization is not active DEBUG 2018-10-09 18:17:36,697 org.springframework.jdbc.datasource.DataSourceUtils: Fetching JDBC Connection from DataSource DEBUG 2018-10-09 18:17:36,937 org.mybatis.spring.transaction.SpringManagedTransaction: JDBC Connection [jdbc:mysql://localhost:3306/chapter6?useSSL=false, [email protected], MySQL Connector Java] will not be managed by Spring DEBUG 2018-10-09 18:17:36,942 org.apache.ibatis.logging.jdbc.BaseJdbcLogger: ==> Preparing: insert into t_role(role_name, note) values (?, ?) DEBUG 2018-10-09 18:17:36,964 org.apache.ibatis.logging.jdbc.BaseJdbcLogger: ==> Parameters: role_name_mapper(String), note_mapper(String) DEBUG 2018-10-09 18:17:36,968 org.apache.ibatis.logging.jdbc.BaseJdbcLogger: <== Updates: 1 DEBUG 2018-10-09 18:17:36,971 org.mybatis.spring.SqlSessionUtils: Closing non transactional SqlSession [[email protected]] DEBUG 2018-10-09 18:17:36,971 org.springframework.jdbc.datasource.DataSourceUtils: Returning JDBC Connection to DataSource DEBUG 2018-10-09 18:17:36,973 org.mybatis.spring.SqlSessionUtils: Creating a new SqlSession DEBUG 2018-10-09 18:17:36,973 org.mybatis.spring.SqlSessionUtils: SqlSession [[email protected]] was not registered for synchronization because synchronization is not active DEBUG 2018-10-09 18:17:36,974 org.springframework.jdbc.datasource.DataSourceUtils: Fetching JDBC Connection from DataSource DEBUG 2018-10-09 18:17:36,975 org.mybatis.spring.transaction.SpringManagedTransaction: JDBC Connection [jdbc:mysql://localhost:3306/chapter6?useSSL=false, [email protected], MySQL Connector Java] will not be managed by Spring DEBUG 2018-10-09 18:17:36,975 org.apache.ibatis.logging.jdbc.BaseJdbcLogger: ==> Preparing: select id, role_name as roleName, note from t_role where id = ? DEBUG 2018-10-09 18:17:36,975 org.apache.ibatis.logging.jdbc.BaseJdbcLogger: ==> Parameters: 8(Long) DEBUG 2018-10-09 18:17:36,985 org.apache.ibatis.logging.jdbc.BaseJdbcLogger: <== Total: 1 DEBUG 2018-10-09 18:17:36,987 org.mybatis.spring.SqlSessionUtils: Closing non transactional SqlSession [[email protected]] DEBUG 2018-10-09 18:17:36,987 org.springframework.jdbc.datasource.DataSourceUtils: Returning JDBC Connection to DataSource DEBUG 2018-10-09 18:17:36,988 org.mybatis.spring.SqlSessionUtils: Creating a new SqlSession DEBUG 2018-10-09 18:17:36,988 org.mybatis.spring.SqlSessionUtils: SqlSession [[email protected]] was not registered for synchronization because synchronization is not active DEBUG 2018-10-09 18:17:36,988 org.springframework.jdbc.datasource.DataSourceUtils: Fetching JDBC Connection from DataSource DEBUG 2018-10-09 18:17:36,988 org.mybatis.spring.transaction.SpringManagedTransaction: JDBC Connection [jdbc:mysql://localhost:3306/chapter6?useSSL=false, [email protected], MySQL Connector Java] will not be managed by Spring DEBUG 2018-10-09 18:17:36,988 org.apache.ibatis.logging.jdbc.BaseJdbcLogger: ==> Preparing: update t_role set role_name = ?, note = ? where id = ? DEBUG 2018-10-09 18:17:36,989 org.apache.ibatis.logging.jdbc.BaseJdbcLogger: ==> Parameters: role_name_mapper(String), role_name_mapper(String), 8(Long) DEBUG 2018-10-09 18:17:36,990 org.apache.ibatis.logging.jdbc.BaseJdbcLogger: <== Updates: 1 DEBUG 2018-10-09 18:17:36,991 org.mybatis.spring.SqlSessionUtils: Closing non transactional SqlSession [[email protected]] DEBUG 2018-10-09 18:17:36,991 org.springframework.jdbc.datasource.DataSourceUtils: Returning JDBC Connection to DataSource DEBUG 2018-10-09 18:17:36,991 org.mybatis.spring.SqlSessionUtils: Creating a new SqlSession DEBUG 2018-10-09 18:17:36,991 org.mybatis.spring.SqlSessionUtils: SqlSession [[email protected]] was not registered for synchronization because synchronization is not active DEBUG 2018-10-09 18:17:36,991 org.springframework.jdbc.datasource.DataSourceUtils: Fetching JDBC Connection from DataSource DEBUG 2018-10-09 18:17:36,992 org.mybatis.spring.transaction.SpringManagedTransaction: JDBC Connection [jdbc:mysql://localhost:3306/chapter6?useSSL=false, [email protected], MySQL Connector Java] will not be managed by Spring DEBUG 2018-10-09 18:17:36,992 org.apache.ibatis.logging.jdbc.BaseJdbcLogger: ==> Preparing: delete from t_role where id=? DEBUG 2018-10-09 18:17:36,992 org.apache.ibatis.logging.jdbc.BaseJdbcLogger: ==> Parameters: 8(Long) DEBUG 2018-10-09 18:17:36,994 org.apache.ibatis.logging.jdbc.BaseJdbcLogger: <== Updates: 1 DEBUG 2018-10-09 18:17:36,994 org.mybatis.spring.SqlSessionUtils: Closing non transactional SqlSession [[email protected]] DEBUG 2018-10-09 18:17:36,994 org.springframework.jdbc.datasource.DataSourceUtils: Returning JDBC Connection to DataSource
五、Spring数据库事务管理
原文地址:https://www.cnblogs.com/BigJunOba/p/9762001.html