【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);
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章