【Java EE】Spring和数据库编程

Spring和数据库编程

Spring最重要的功能就是操作数据。在Java互联网项目中,数据大部分存储在数据库和NoSQL中,数据库的编程时互联网编程的基础,Spring为开发者提供了JDBC的模板模式,那就是JdbcTemplate,它可以简化许多代码的编程,但是在实际中并不常用。
对于持久层,工作中更多的时候用的时Hibernate框架和MyBatis框架,对于Hibernate框架,Spring提供了HibernateTemplate给予支持,它能有效简化对Hibernate的编程。对于MyBatis编程,社区开发了SqlSessionTemplate给开发者使用。

传统的JDBC代码的弊端

示例代码如下:

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过程如下:首先打开数据库连接执行SQL,然后组装结果,最后关闭数据库资源。但是太多的try…catch…finally…语句,造成了代码的泛滥。

配置数据库资源

在Spring中配置数据库资源很简单,大部分配置成为数据库连接池,既可以使用Spring内部提供的类,也可以使用第三方数据库连接池或者从web服务器中通过JNDI获取数据,由于使用了第三方的类,一般而言在工程中会偏向于采用XML的方式进行配置,当然也可以采用注解的方式进行配置。对于项目的公共资源,建议统一采用XML进行配置。

使用简单数据库配置

它是Spring提供的一个类org.springframework.jdbc.datasource.SimpleDriverSource。它很简单,不支持数据库连接池。可以通过XML的形式配置:

<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>

这个配置一般用于测试,因为它不是一个数据库连接池,只是一个很简单的数据库连接的应用。

使用第三方数据库连接池

使用DBCP数据库连接池,在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>

Spring为配置JNDI数据库连接池提供了对应的支持。

使用JNDI数据库连接池

在Tomcat、WebLogic等java EE服务器上配置数据源,这时它存在一个JNDI的名称。也可以通过Spring所提供的JNDI机制获取对应的数据源。
假设在Tomcat上已经配置了jdbc/chapter12的数据源,在项目中获得这个JNDI数据源的代码如下:

<bean id="dataSource" class="org.springframework.jndi.JndiObjectFactoryBean">
	<property name="jndiName" value="java:comp/env/jdbc/chapter12"/>
</bean>

这样就能够在Spring中定义JNDI数据源了。

JDBC代码失控的解决方案——JdbcTemplate

JdbcTemplate是Spring针对JDBC代码失控提供的解决方案。首先对JdbcTemplate进行配置,代码如下:

<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
   <property name="dataSource" ref="dataSource" />
</bean>

配置好了dataSource和JdbcTemplate就可以操作JdbcTemplate了,假设Spring配置文件为spring-cfg.xml,则代码如下:

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接口。而且这里使用了匿名类,所以采用new关键字去创建一个RowMapper接口对象,如果是Java 8,可以采用Lambda表达式的写法,代码如下:

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, (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;
    });
System.out.println(role.getRoleName());

在mapRow方法中,从ResultSet对象中取出查询得到的数据,组装成一个Role对象,而无须再写任何关闭数据库资源的代码。因为JdbcTemplate内部实现了它们,这便是Spring所提供的模板规则。

JdbcTemplate的增删查改操作

JdbcTemplate的增删查改操作代码如下:

public static void main(String[] args) {
    ApplicationContext ctx = new ClassPathXmlApplicationContext("spring-cfg.xml");
    JdbcTemplate jdbcTemplate = ctx.getBean(JdbcTemplate.class);
    
    JdbcTemplateTest test = new JdbcTemplateTest();
    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);
}

插入角色的代码:

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);
}

删除角色的代码:

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());
}

查询角色列表的代码:

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;
}

执行多条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;
}

通过实现ConnectionCallback或者StatementCallback接口的方法获取Connection对象或者Statement对象,这样便能够执行多条SQL了。

JdbcTemplate的源码分析

这里查看StatementCallback接口回调的源码:
参考:https://blog.csdn.net/DorMOUSENone/article/details/79046865

@Override
public <T > T execute(StatementCallback < T > action) throws DataAccessException {
   Assert.notNull(action, "Callback object must not be null");

   Connection con = DataSourceUtils.getConnection(getDataSource());

   Statement stmt = null;
   try {
      Connection conToUse = con;
      if (this.nativeJdbcExtractor != null && this.nativeJdbcExtractor.isNativeConnectionNecessaryForNativeStatements()) {
         conToUse = this.nativeJdbcExtractor.getNativeConnection(con);
      }
      stmt = conToUse.createStatement();   // 通过连接(Connection)获取一个 Statement
      applyStatementSettings(stmt);   // 配置 Statement 参数
      Statement stmtToUse = stmt;
      if (this.nativeJdbcExtractor != null) {
         stmtToUse = this.nativeJdbcExtractor.getNativeStatement(stmt);
      }
      // 回调执行 doInXXX() 方法, 并获得 result
      T result = action.doInStatement(stmtToUse);
      handleWarnings(stmt);
      return result;
   } catch (SQLException ex) {
      //Release Connection early, to avoid potential connection pool deadlock
      //in the case when the exception translator hasn't been initialized yet
      JdbcUtils.closeStatement(stmt);
      stmt = null;
      DataSourceUtils.releaseConnection(con, getDataSource());
      con = null;
      throw getExceptionTranslator().translator("StatementCallback", getSql(action), ex);
   } finally {
      JdbcUtils.closeStatement(stmt);
      DataSourceUtils.releaseConnection(con, getDataSource());
   }
}

首先从数据源获取一条连接,然后对接口进行了回调,而在catch语句中会关闭对应的资源。从源码中可以看出,Spring要实现数据库连接资源获取和释放的逻辑,只要完成回调接口的方法逻辑即可,这便是它所提供的模板功能。但是并没有看到任何的事务管理,这是因为JdbcTemplate是不能支持事务的,还需要引入对应的事务管理器才能够支持事务。
只是这里的数据库资源获取和释放的功能还没有那么简单,例如下面的代码:

Connection con = DataSourceUtils.getConnection(getDataSource());
......
DataSourceUtils.releaseConnection(con, getDataSource());

在Spring中,它会在内部再次判断事务是否交由事务管理器处理,如果是,则数据库连接将会从数据库事务管理器中获取,并且JdbcTemplate的资源链接请求的关闭也将由事务管理器决定,而不是由JdbcTemplate自身决定。由于这里只是简单应用,数据库事务并没有交由事务管理器管理,所以数据库资源是由JbdcTemplate自身管理的。

MyBatis-Spring项目

大部分的Java互联网项目,都是用Spring MVC+Spring+MyBatis搭建平台的。使用Spring IoC可以有效管理各类Java资源,达到即插即拔功能;通过AOP框架,数据库事务可以委托给Spring处理,消除很大一部分的事务代码,配合MyBatis的高灵活、可配置、可优化SQL等特性,完全可以构建高性能的大型网站。
配置MyBatis-Spring项目需要以下几步:

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

配置SqlSessionFactoryBean

从MyBatis的介绍中,可以知道SqlSessionFactory是产生SqlSession的基础,因此配置SqlSessionFactory十分关键,在MyBatis-Spring项目中提供了SqlSessionFactoryBean去支持SqlSessionFactory的配置,源码如下:

public class SqlSessionFactoryBean implements FactoryBean<SqlSessionFactory>, InitializingBean, ApplicationListener<ApplicationEvent>{
	// 日志
	private static final Log LOGGER = LogFactory.getLog(SqlSessionFactoryBean.class);
	// MyBatis配置文件
	private Resource configuration;
	// Configuration对象
	private Configuration configuration;
	// Mapper配置路径
	private Resource[] mapperLocations;
	// 数据库
	private DataSource dataSource;
	// 事务管理器
	private TransactionFactory transactionFactory;
	// 配置属性
	private Properties configurationProperties;
	// SqlSessionFactoryBuilder
	private SqlSessionFactoryBuilder SqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
	// SqlSessionFactory
	private SqlSessionFactory SqlSessionFactory;
	// environment
	// EnvironmentAware requies spring 3.1
	private String environment = SqlSessionFactoryBean.class.getSimpleName();
	// 当加载后,是否检测所有MyBatis的映射语句加载完全,默认为false
	private boolean failFast;
	// 插件
	private Interceptor[] plugins;
	// 类型转换器, typeHandlers
	private TypeHandler<?>[] typeHandlers;
	// 类型转换器包,用于扫描装载
	private String typeHandlersPackage;
	// 别名
	private Class<?>[] typeAliases;
	// 别名包,用于扫描加载
	private String typeAliasesPackage;
	// 当扩展了上面Class类后,就生成别名,但是如果你没有配置typeAliasesPackage则不会生效
	private Class<?>[] typeAliasesSuperType;
	// 数据库厂商标识
	/ issue #19.No default provider
	private DatabaseIdProvider databaseIdProvider;
	// unix的文件操作
	private Class<? extends VFS> vfs;
	// 缓存
	private Cache cache;
	// ObjectFactory
	private ObjectFactory objectFactory;
	// 对象包装器
	private ObjectWrapperFactory objectWrapperFactory;
	/********* setter and getter**********/
}

从源码中可以看出,几乎可以配置所有关于MyBatis的组件,并且它也提供了对应的setter方法让Spring设置它们,所以完全可以通过Spring IoC容器的规则去配置它们。
由于使用了第三方的包,一般而言,更倾向于XML的配置,简单配置如下:

<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
   <property name="dataSource" ref="dataSource" />
   <property name="configLocation" value="classpath:sqlMapConfig.xml" />
</bean>

这里配置了SqlSessionFactoryBean,但是只是配置了数据源,然后引入了一个MyBatis配置文件,当然如果配置的内容很简单,是可以完全不引入MyBatis配置文件的,只需要通过Spring IoC容器注入即可,但是对于较为复杂的配置,还是建议使用MyBatis的配置文件。配置文件代码如下:

<?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>

这里定义了MyBatis的一些配置项,然后定义了一个角色的别名role,跟着引入了一个映射器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>

这里定义了一个命名空间:com.ssm.chapter12.mapper.RoleMapper,并且提供了对角色的增删查改方法。按照MyBatis的规则需要定义一个接口RoleMapper.java,才能够调用它:

import org.apache.ibatis.annotations.Param;
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是一个接口,而不是一个类,它没有办法产生实例,那么该如何配置它呢?

SqlSessionTemplate组件

严格地,SqlSessionTemplate并不是一个必须配置的组件,但是它也存在一定的价值。首先,它是线程安全的类,也就是确保每个线程使用的SqlSession唯一且不互相冲突。其次,它提供了一系列的功能,比如增删查改等常用操作。这里首先对其进行配置:

<bean id="sqlSessionTemplate" class="org.mybatis.spring.SqlSessionTemplate">
   <constructor-arg ref="sqlSessionFactory" />
   <!-- <constructor-arg value="BATCH"/> -->
</bean>

SqlSessionTemplate要通过带有参数的构造方法去创建对象,常用的参数是SqlSessionFactory和MyBatis执行器(Executor)类型,取值范围是SIMPLE、REUSE、BATCH。
篇日志好了SqlSessionTemplate之后,就可以使用它了。增删查改的代码如下:

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允许配置执行器的类型,当同时配置SqlSessionFactory和SqlSessionTemplate的时候,SqlSessionTemplate的优先级大于SqlSessionFactory。

配置MapperFactoryBean

MyBatis的运行只需要提供类似于RoleMapper.java接口,而无须提供一个实现类,它是由MyBatis体系创建的动态代理对象运行的,而Spring没有办法为其生成实现类。因此MyBatis-Spring团队提供了一个MapperFactoryBean类作为中介,可以通过配置它来实现我们想要的Mapper。使用了Mapper接口编程方式可以有效地在逻辑代码中擦除SqlSessionTemplate。
配置如下:

<bean id="roleMapper" class="org.mybatis.spring.mapper.MapperFactoryBean">
	<!--RoleMapper接口将被扫描为Mapper--> 
   <property name="mapperInterface" value="com.ssm.chapter12.mapper.RoleMapper" />
   <property name="sqlSessionFactory" ref="sqlSessionFactory" />
   <!--如果同时注入sqlSessionFactory和sqlSessionTemplate,则只会启用sqlSessionTemplate-->
   <property name="sqlSessionTemplate" ref="sqlSessionTemplate]"/>
</bean>

这里的MapperFactoryBean存在3个属性可以配置,分布是mapperInterface、sqlSessionTemplate和SqlSessionFactory,其中:

  • mapperInterface是映射器的接口
  • 如果同时配置sqlSessionTemplate和SqlSessionFactory,那么它就会启用sqlSessionTemplate。
    配置完成这样的一个Bean之后,就可以使用下面代码获取映射器了:
RoleMapper roleMapper = ctx.getBean(RoleMapper.class);

有时候项目会比较大,如果一个个配置Mapper会造成配置量大的问题,MapperScannerConfigurer可以用扫描的形式去生产对应的Mapper。

配置MapperScannerConfigurer

对于MapperScannerConfigurer的主要配置项有:

  • basePackages,它指定了让Spring自动扫描什么包,它会逐层深入扫描,如果遇到多个包可以使用半角逗号分隔;
  • annotationClass,表示如果类被这个注解标识的时候,才进行扫描。对于开发而言,建议使用这个方式进行注册对应的Mapper。在Spring中往往使用注解@Repository表示数据访问层(DAO)
  • SqlSessionFactoryBeanName,指定在Spring中定义SqlSessionFactory的Bean名称。如果sqlSessionTemplateBeanName被定义,那么它将失去作用。
  • markerInterface,指定实现了什么接口就认为它是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扫描哪个包,这样就可能扫出对应的Mapper到Spring IoC容器中了,代码如下:

<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>

通过这样的配置Spring IoC容器就知道将包命名为com.ssm.chapter12.mapper,把注解为@Repository的接口扫描为Mapper对象,存放在容器中,对于多个包的扫描可以用半角逗号分隔开。

使用@Repository注解将允许把接口放到各个包当中,然后通过简单的定义类MapperScannerConfigurer的basePackage属性扫描出来,有利于对包的规划。

测试Spring+MyBatis

XML配置文件如下:

<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>
<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" />
   <!-- <property name="markerInterface" value="com.ssm.chapter12.base.BaseMapper"/> -->
</bean>

验证函数如下:

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 .insert(role);
Long id = role.getId();
roleMapper.getRole(id);
role.setNote("note_mapper_update");
roleMapper.update(role);
roleMapper.delete(id);
发布了279 篇原创文章 · 获赞 169 · 访问量 32万+

猜你喜欢

转载自blog.csdn.net/ARPOSPF/article/details/105309162