Spring + MyBatis - 整合

Spring + MyBatis

1、准备

1.1、添加依赖

  • 父模块 ( parent )
    必须先在 父模块 的 <dependencyManagement> 内部的 <dependencies> 中 添加依赖,子模块才可以继承
  • 当前模块( integration )
   <dependencies>

        <!-- MySQL Driver -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>

        <!-- Oracle Driver -->
        <dependency>
            <groupId>com.oracle</groupId>
            <artifactId>oracle-thin</artifactId>
        </dependency>

        <!-- 阿里巴巴提供的数据源组件 ( Druid ) -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
        </dependency>

        <!-- Spring Context -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
        </dependency>

        <!-- Spring JDBC -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
        </dependency>

        <!-- Spring Test -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
        </dependency>
        
        <!-- JUnit 单元测试 -->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
        </dependency>

        <!-- AspectJ 是 Spring  AOP 的支持包 -->
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjrt</artifactId>
        </dependency>

        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
        </dependency>
        

        <!-- MyBatis 核心包 -->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
        </dependency>

        <!-- MyBatis 为 整合 Spring 提供的 支持包 -->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis-spring</artifactId>
        </dependency>

    </dependencies>

1.2、创建实体类

    package com.itlaobing.integration.entity;
    
    import java.io.Serializable;
    import java.util.Date;
    
    public class Customer implements Serializable {
    
        private Integer id ; // Object Identifier Field
        private String username ; // 登录名称
        private String password ;  // 登录密码
        private String name ; // 姓名
        private char gender ; // 性别
        private Date birthdate ; // 出生年月
        
    }

1.3、创建表

  • MySQL

-- 如果 t_customers 表已经存在则删除它
DROP TABLE IF EXISTS t_customers ;

-- 创建表
CREATE TABLE t_customers (
    id INT(5) ,
    username VARCHAR(20) UNIQUE  NOT NULL ,
    password VARCHAR(32) NOT NULL ,
    name VARCHAR(60) ,
    gender CHAR(1) ,
    birthdate date ,
    PRIMARY KEY( id )
);

INSERT INTO t_customers ( id , username , password )
  VALUES ( 1 , 'zhangsanfeng' , 'hello' ) , ( 2 , 'zhangcuishan' , 'hello' ) , ( 3 , 'yinsusu' , 'hello'  ) ;

  • Oracle


CREATE TABLE t_customers (
   id NUMBER(5) ,
   username VARCHAR2(20) UNIQUE  NOT NULL ,
   password VARCHAR2(32) NOT NULL ,
   name VARCHAR2(60) ,
   gender CHAR(1) ,
   birthdate date ,
   PRIMARY KEY( id )
);

TRUNCATE TABLE  t_customers ;

INSERT ALL
  INTO t_customers ( id , username , password ) VALUES ( 1 , 'zhangsanfeng' , 'hello' )
  INTO t_customers ( id , username , password , name ) VALUES ( 2 , 'zhangcuishan' , 'hello' , '张翠山' )
  INTO t_customers ( id , username , password , name ) VALUES ( 3 , 'yinsusu' , 'hello' , '殷素素' )
SELECT * FROM dual ;

1.4、提供属性文件 ( db.properties )

在 类路径下的 com/itlaobing/integration 目录下 提供 db.properties

jdbc.connection.driver = oracle.jdbc.driver.OracleDriver
jdbc.connection.url = jdbc:oracle:thin:@localhost:1521:ycpower
jdbc.connection.username = itlaobing
jdbc.connection.password =itlaobing

druid.filters = stat
druid.maxActive =20
druid.initialSize = 1
druid.maxWait = 60000
druid.minIdle = 10
druid.maxIdle = 15
druid.timeBetweenEvictionRunsMillis = 60000
druid.minEvictableIdleTimeMillis = 300000
druid.validationQuery = SELECT 'x' FROM dual
druid.testWhileIdle = true
druid.testOnBorrow = false
druid.testOnReturn = false
druid.maxOpenPreparedStatements = 20
druid.removeAbandoned = true  
druid.removeAbandonedTimeout = 1800
druid.logAbandoned = true

1.5、提供 Spring 配置文件 ( beans.xml )

在 类路径下的 com/itlaobing/integration 目录下 提供 beans.xml

<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
                             http://www.springframework.org/schema/beans/spring-beans.xsd
                             http://www.springframework.org/schema/context
                             http://www.springframework.org/schema/context/spring-context.xsd
                             http://www.springframework.org/schema/tx
                             http://www.springframework.org/schema/tx/spring-tx.xsd
                             http://www.springframework.org/schema/aop
                             http://www.springframework.org/schema/aop/spring-aop.xsd">
                             
</beans>

2、数据源

2.1、读取 db.properties 文件

在 com/itlaobing/integration/beans.xml 文件中添加以下内容:

    <!-- 指定 属性文件的 位置 -->
    <context:property-placeholder location="classpath*:com/**/integration/db.properties" />

2.2、声明 DataSource 对应的 bean

可以通过 Spring 的 JDBC 模块中的 DriverManagerDataSource 来创建数据源。

也可以借助于第三方的实现,比如 DBCP 、C3P0 、Druid 等。这里选择使用 阿里巴巴 的 Druid 。

在 com/itlaobing/integration/beans.xml 文件中添加以下内容:

    <!-- 配置  DruidDataSource -->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">

        <property name="driverClassName" value="${jdbc.connection.driver}" />
        <property name="url" value="${jdbc.connection.url}" />
        <property name="username" value="${jdbc.connection.username}" />
        <property name="password" value="${jdbc.connection.password}" />

        <property name="filters" value="${druid.filters}" />
        <!-- 最大并发连接数 -->
        <property name="maxActive" value="${druid.maxActive}" />
        <!-- 初始化连接数量 -->
        <property name="initialSize" value="${druid.initialSize}" />
        <!-- 配置获取连接等待超时的时间 -->
        <property name="maxWait" value="${druid.maxWait}" />
        <!-- 最小空闲连接数 -->
        <property name="minIdle" value="${druid.minIdle}" />
        <!-- 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 -->
        <property name="timeBetweenEvictionRunsMillis" value="${druid.timeBetweenEvictionRunsMillis}" />
        <!-- 配置一个连接在池中最小生存的时间,单位是毫秒 -->
        <property name="minEvictableIdleTimeMillis" value="${druid.minEvictableIdleTimeMillis}" />
        <property name="validationQuery" value="${druid.validationQuery}" />
        <property name="testWhileIdle" value="${druid.testWhileIdle}" />
        <property name="testOnBorrow" value="${druid.testOnBorrow}" />
        <property name="testOnReturn" value="${druid.testOnReturn}" />
        <property name="maxOpenPreparedStatements" value="${druid.maxOpenPreparedStatements}" />
        <!-- 1800 秒,也就是 30 分钟 -->
        <property name="removeAbandonedTimeout" value="${druid.removeAbandonedTimeout}" />
        <!-- 关闭 abanded 连接时输出错误日志 -->
        <property name="logAbandoned" value="${druid.logAbandoned}" />
    </bean>

3、MyBatis

3.1、提供数据访问接口

    package com.itlaobing.integration.mapper;
    
    public interface CustomerMapper {
    
    }

3.2、提供 Mapper

就是 提供 数据访问接口 所对应的 Mapper.xml 文件。

在类路径下的 com/itlaobing/integration/mapper 中创建 CustomerMapper.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 的值 必须跟 将要实现的接口的 全限定类名一致 -->
<mapper namespace="com.itlaobing.integration.mapper.CustomerMapper">

</mapper>

MyBatis 内部 通过 解析 CustomerMapper.xml 文件来动态产生一个 CustomerMapper 接口的实现类,并实现其中所有的抽象方法。

3.3、添加 find 方法

3.3.1、提供 TypeHandler

为了解决 从数据库中获取 到 的 类型 跟 实体类字段 类型 之间的转换问题,所以需要定义 TypeHandler 。

  • 解决 String 和 char 之间的转换
package com.itlaobing.integration.handler;

import org.apache.ibatis.type.*;
import java.sql.*;

public class GenderHandler implements TypeHandler<Character> {

    @Override
    public void setParameter(PreparedStatement ps, int patamIndex, Character character, JdbcType jdbcType) throws SQLException {
        String gender = null ;
        if( character != null ) {
            char ch = character.charValue();
            switch ( ch ){
                case '女' :  gender = "F" ; break;
                case '男' : gender = "M" ; break;
            }
        }

        ps.setString( patamIndex , gender );
    }

    @Override
    public Character getResult(ResultSet rs, String label ) throws SQLException {
        Character ch = null ; // Character 是 char 类型的包装类
        String gender = rs.getString( label );
        if( gender != null && gender.trim().length() > 0 ) {
            char c = gender.trim().charAt( 0 ); // 从 gender 对应的字符串中剔除 首尾空白后 获取 第一个字符
           ch = ( c == 'F' ? '女' : ( c == 'M' ? '男' : '\u0000' ) );
        }
        return ch;
    }

    @Override
    public Character getResult(ResultSet rs, int index) throws SQLException {
        Character ch = null ;
        String gender = rs.getString( index );
        if( gender != null && gender.trim().length() > 0 ) {
            char c = gender.trim().charAt( 0 );
            ch = ( c == 'F' ? '女' : ( c == 'M' ? '男' : '\u0000' ) );
        }
        return ch;
    }

    @Override
    public Character getResult(CallableStatement cs, int index) throws SQLException {
        Character ch = null ;
        String gender = cs.getString( index );
        if( gender != null && gender.trim().length() > 0 ) {
            char c = gender.trim().charAt( 0 );
            ch = ( c == 'F' ? '女' : ( c == 'M' ? '男' : '\u0000' ) );
        }
        return ch;
    }

}

  • 解决 java.sql.Date 和 java.util.Date 之间的转换
package com.itlaobing.integration.handler;

import org.apache.ibatis.type.*;

import java.sql.*;

public class DateHandler implements TypeHandler<java.util.Date> {

    @Override
    public void setParameter(PreparedStatement ps , int paramIndex , java.util.Date date, JdbcType jdbcType) throws SQLException {
        java.sql.Date d = null ;
        if( date != null ) {
           d = new java.sql.Date( date.getTime() );
        }
        ps.setDate( paramIndex , d );
    }

    @Override
    public java.util.Date getResult(ResultSet rs , String label ) throws SQLException {
        return rs.getDate( label );
    }

    @Override
    public java.util.Date getResult(ResultSet rs , int index ) throws SQLException {
        return rs.getDate( index );
    }

    @Override
    public java.util.Date getResult(CallableStatement cs, int index ) throws SQLException {
        return cs.getDate( index ) ;
    }

}

3.3.2、在 数据访问接口中 声明 抽象方法
    package com.itlaobing.integration.mapper;

    import com.itlaobing.integration.entity.Customer;

    public interface CustomerMapper {
        
            Customer find( Integer id ) ; // 需要在 CustomerMapper.xml 中提供 "实现"
    
    }
3.3.3、在 Mapper 文件中提供 对 抽象方法的 “实现”

在 com/itlaobing/integration/mapper/CustomerMapper.xml 文件中追加以下内容:


    <!-- 用来将 结果集中的 某一行的 每个列 映射到 一个对象中 -->
    <resultMap id="customerResultMap" type="com.itlaobing.integration.entity.Customer" >
        <id column="id" property="id" />
        <result column="username" property="username" />
        <result column="password" property="password" />
        <result column="name" property="name" />
        <result column="gender" typeHandler="com.itlaobing.integration.handler.GenderHandler" property="gender" />
        <result column="birthdate" typeHandler="com.itlaobing.integration.handler.DateHandler" property="birthdate" />
    </resultMap>

    <!-- CustomerMapper 接口中声明的抽象方法为:  Customer find( Integer id ) ; -->
    <!-- 【强制】 select 的 id 必须 跟 Mapper 接口中的方法名保持一致 -->
    <select id="find" parameterType="int" resultMap="customerResultMap">
        SELECT id , username , password , name , gender , birthdate FROM t_customers WHERE id = #{id}
    </select>
    

3.4、添加 update 方法

3.4.1、在 数据访问接口 中声明抽象方法
    package com.itlaobing.integration.mapper;
    
    import com.itlaobing.integration.entity.Customer;
    import org.springframework.stereotype.Repository;
    
    public interface CustomerMapper {
    
        Customer find( Integer id ) ;
    
        int update( Customer c ) ;
    
    }
3.4.2、在 Mapper 文件中提供 对 抽象方法的 “实现”

注意,这里所使用的 typeHandler 是 3.3.1 中已经创建好的。

    <!-- CustomerMapper 接口中声明的抽象方法为:   int update( Customer c ) ; -->
    <update id="update" parameterType="com.itlaobing.integration.entity.Customer" >
        UPDATE t_customers
        SET
          username = #{username} ,
          password=#{password} ,
          name = #{name} ,
          gender = #{gender,typeHandler=com.itlaobing.integration.handler.GenderHandler} ,
          birthdate = #{birthdate,typeHandler=com.itlaobing.integration.handler.DateHandler}
        WHERE id = #{id}
    </update>

4、整合

4.1、通过 FactoryBean 方式来创建 SqlSessionFactory

注意,通过 FactoryBean 方式来创建 bean ,参见 IoC 部分的举例: DateFactoryBean 。

在 com/itlaobing/integration/beans.xml 文件中添加以下内容:

<!-- 通过配置  SqlSessionFactoryBean 来创建  sqlSessionFactory -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
    <property name="dataSource" ref="dataSource" />
    <property name="mapperLocations" value="classpath*:com/**/mapper/*Mapper.xml" />
</bean>

这里 通过为 SqlSessionFactoryBean 注入 dataSource 属性来解决数据库连接问题。

通过 为 SqlSessionFactoryBean 注入 mapperLocations 属性来确定 Mapper 文件的位置。

将来由 SqlSessionFactoryBean 所创建的 SqlSessionFactory 实例就明确需要连接那个数据库、需要解析那些 Mapper 文件。

4.2、提供 MapperScannerConfigurer

在 com/itlaobing/integration/beans.xml 文件中添加以下内容:

    <bean id="mapperScannerConfigurer" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <!-- 指定 sqlSessionFactory  对应的 bean 的名字 -->
        <property name="sqlSessionFactoryBeanName">
            <idref bean="sqlSessionFactory" />
        </property>
        <!-- 指定 数据访问接口 ( Mapper 接口 ) 所在的 包 -->
        <property name="basePackage" value="com.itlaobing.integration.mapper" />
    </bean>

4.3、在 数据访问接口 上 添加 @Repository 注解

package com.itlaobing.integration.mapper;

import com.itlaobing.integration.entity.Customer;
import org.springframework.stereotype.Repository;

@Repository
public interface CustomerMapper {

    Customer find( Integer id ) ;

    int update( Customer c ) ;

}

为了将来能够通过:

container.getBean( "customerMapper" , CustomerMapper.class )

来获取一个 CustomerMapper 实例,所以这里使用 @Repository 标注 CustomerMapper 接口。

但是一定要注意,CustomerMapper 是个接口,不能直接实例化。

4.4、扫描组件

在 com/itlaobing/integration/beans.xml 文件中添加以下内容:

    <!-- 扫描所有的添加了 @Repository 注解的 Mapper 接口 -->
    <context:component-scan base-package="com.itlaobing.integration.mapper" />

4.5、测试

public class MapperTest {

    public static void main(String[] args) {

        String configLocations = "classpath:com/itlaobing/integration/beans.xml" ;
        
        ApplicationContext container = new ClassPathXmlApplicationContext( configLocations ) ;

        CustomerMapper mapper = container.getBean( "customerMapper" , CustomerMapper.class );

        System.out.println( mapper );
        System.out.println( mapper.getClass() );
        System.out.println( Arrays.toString( mapper.getClass().getInterfaces() ));

        Customer c = mapper.find( 1 );

        System.out.println( c.getUsername() + " : " + c.getName() );

    }
}

5、事务

注意,事务控制代码应该在 业务层 织入 而不是 数据访问层。

5.0、提供业务层

package com.itlaobing.integration.service;

import com.itlaobing.integration.entity.Customer;
import com.itlaobing.integration.mapper.CustomerMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class CustomerService {

    @Autowired
    private CustomerMapper customerMapper;

    public Customer loadCustomer( Integer id ) {
        return customerMapper.find( id );
    }

    public boolean updateCustomer( Customer c ){
        int count = customerMapper.update( c );
        return count == 1 ;
    }

    public CustomerMapper getCustomerMapper() {
        return customerMapper;
    }

    public void setCustomerMapper(CustomerMapper customerMapper) {
        this.customerMapper = customerMapper;
    }

}

5.1、提供平台事务管理器

在 com/itlaobing/integration/beans.xml 文件中添加以下内容:

<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager" >
    <property name="dataSource" ref="dataSource" />
</bean>

5.2、提供 声明式事务 支持

在 com/itlaobing/integration/beans.xml 文件中添加以下内容:

<tx:advice id="txAdvice" transaction-manager="transactionManager">
    <tx:attributes>
        <tx:method name="update*" isolation="READ_COMMITTED" propagation="REQUIRED" read-only="false" />
        <tx:method name="delete*"  isolation="READ_COMMITTED" propagation="REQUIRED" read-only="false" />
        <tx:method name="save*"   isolation="READ_COMMITTED" />
        <tx:method name="load*" isolation="READ_COMMITTED" read-only="true" />
        <tx:method name="find*" isolation="READ_COMMITTED" read-only="true"/>
        <tx:method name="get*" isolation="READ_COMMITTED" read-only="true"/>
    </tx:attributes>
</tx:advice>

5.3、提供 事务切面

在 com/itlaobing/integration/beans.xml 文件中添加以下内容:

<aop:config proxy-target-class="true">
    <aop:pointcut id="txPointcut" expression="execution(* com..service.*.*(..))" />
    <aop:advisor pointcut-ref="txPointcut" advice-ref="txAdvice" />
</aop:config>
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值