Spring(四)Spring与数据库编程

  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项目需要下面几步:

  1. 配置数据源
  2. 配置SqlSessionFactory
  3. 可以选择的配置由SqlSessionTemplate,在同时配置SqlSessionTemplate和SqlSessionFactory的情况下,优先采用SqlSessionTemplate
  4. 配置Mapper,可以配置单个Mapper,也可以通过扫描的方法生成Mapper,比较灵活。此时Spring IoC会生成对应接口的实例,这样就可以通过注入的方式来获取资源。
  5. 事务管理。

  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

时间: 2024-11-02 19:48:29

Spring(四)Spring与数据库编程的相关文章

Spring 学习其三:数据库编程

一.直接利用 JDBC 进行数据库编程 虽然有 Spring 和 MyBatis 等数据持久化的利器在手,但还是有必要去了解下传统的 JDBC 开发方式.无论哪种框架,其本质都是在传统的 JDBC 方式上进行了封装. package JdbcTest; import java.sql.Connection; import java.sql.DriverManager; import java.sql.PreparedStatement; import java.sql.ResultSet; im

Spring(四):面向切面编程AOP

横切关注点:分布于应用中多处的功能 面向切面编程AOP:将横切关注点与业务逻辑相分离 在使用面向切面编程时,仍在一个地方定义通用功能,但是可以通过声明的方式定义这个功能以何种方式在何处应用,而无需修改受影响的类. 横切关注点可以被模块化为特殊的类,这些类被称为切面. 好处: 每个关注点集中于一处,而不是分散到多处代码中 服务模块更加简洁,因为它们只包含主要关注点的代码,次要关注点被转移到切面中了 1.定义AOP术语 1.1.通知(Advice) 切面的工作被称为通知. 通知定义了切面是什么以及何

Spring(四) Spring容器(2)

6.Spring容器中的Bean 对于开发者来说,开发者使用Spring做2件事:(1)开发Bean (2)配置Bean.

Spring AOP: Spring之面向方面编程

Spring AOP: Spring之面向方面编程 面向方面编程 (AOP) 提供从另一个角度来考虑程序结构以完善面向对象编程(OOP). 面向对象将应用程序分解成 各个层次的对象,而AOP将程序分解成各个方面 或者说 关注点 . 这使得可以模块化诸如事务管理等这些横切多个对象的关注点.(这些关注点术语称作 横切关注点.) Spring的一个关键组件就是AOP框架. Spring IoC容器(BeanFactory 和ApplicationContext)并不依赖于AOP, 这意味着如果你不需要

JavaEE学习之Spring Security3.x——模拟数据库实现用户,权限,资源的管理

一.引言 因项目需要最近研究了下Spring Security3.x,并模拟数据库实现用户,权限,资源的管理. 二.准备 1.了解一些Spring MVC相关知识: 2.了解一些AOP相关知识: 3.了解Spring: 4.了解Maven,并安装. 三.实现步骤 本示例中使用的版本是Spring Security3.2.2.通过数据库实现Spring Security认证授权大致需要以下几个步骤: 1.新建maven web project(因为本示例使用的是maven来构建的),项目结构如下,

J2EE进阶(四)Spring配置文件详解

J2EE进阶(四)Spring配置文件详解 前言 Spring配置文件是用于指导Spring工厂进行Bean生产.依赖关系注入(装配)及Bean实例分发的"图纸".Java EE程序员必须学会并灵活应用这份"图纸"准确地表达自己的"生产意图".Spring配置文件是一个或多个标准的XML文档,applicationContext.xml是Spring的默认配置文件,当容器启动时找不到指定的配置文档时,将会尝试加载这个默认的配置文件. 下面列举的是

SpringMVC学习(四)——Spring、MyBatis和SpringMVC的整合

之前我整合了Spring和MyBatis这两个框架,不会的可以看我的文章MyBatis框架的学习(六)——MyBatis整合Spring.本文我再来讲SpringMVC和MyBatis整合开发的方法,这样的话,Spring.MyBatis和SpringMVC三大框架的整合开发我们就学会了.这里我使用的Spring是Spring4.1.3这个版本(SpringMVC自然也是这个版本),MyBatis是MyBatis3.2.7这个版本. 为了更好的学习SpringMVC和MyBatis整合开发的方法

Spring+Mybatis实现主从数据库读写分离

Spring+Mybatis实现主从数据库读写分离 采用配置+注解的方式. 自定义@DataSource注解 import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Retention(RetentionPolicy.RUNTIME

单点登录CAS与Spring Security集成(数据库验证,向客户端发送更多信息)

准备工作 CAS server从网上直接下载下来,里面有一个cas-server-webapp的工程,使用Maven命令构建,导入到Eclipse中,便可以直接使用,cas server我使用的是3.5.2版本.客户端,我是使用以前的工程,只要是Web工程就行,cas-client使用的3.2.1,Spring Security使用的是3.1.4,记得Spring Security的3.1.2版本和CAS集成时,当需要CAS Server传比较多的信息给客户端时,客户端的Spring Secur

161920、使用Spring AOP实现MySQL数据库读写分离案例分析

一.前言 分布式环境下数据库的读写分离策略是解决数据库读写性能瓶颈的一个关键解决方案,更是最大限度了提高了应用中读取 (Read)数据的速度和并发量. 在进行数据库读写分离的时候,我们首先要进行数据库的主从配置,最简单的是一台Master和一台Slave(大型网站系统的话,当然会很复杂,这里只是分析了最简单的情况).通过主从配置主从数据库保持了相同的数据,我们在进行读操作的时候访问从数据库Slave,在进行写操作的时候访问主数据库Master.这样的话就减轻了一台服务器的压力. 在进行读写分离案