Spring Boot 整合 Mybatis

Mybatis 是一个基于 JDBC 实现的,支持普通 SQL 查询、存储过程和高级映射的优秀持久层框架,去掉了几乎所有的 JDBC 代码和参数的手工设置以及对结果集的检索封装。Mybatis 主要思想是将程序中大量的 SQL 语句剥离出来,配置在配置文件中,以实现 SQL 的灵活配置。

1.Mybatis的使用

首先在 MySQL 数据库创建表:

CREATE TABLE `product_info` (
  `product_id` bigint(18) unsigned NOT NULL COMMENT '商品id, 主键',
  `product_name` varchar(32) NOT NULL COMMENT '商品名称',
  `product_price` decimal(8,2) unsigned NOT NULL DEFAULT '99999.99' COMMENT '商品单价',
  `gmt_create` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  `gmt_modified` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间',
  PRIMARY KEY (`product_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

使用 Mybatis 需要添加 Maven 依赖:

<!-- mybatis -->
<dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
    <version>1.3.5</version>
</dependency>
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
</dependency>

然后通过 application.properties 中的 spring.datasource.* 前缀配置属性:

spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/spring_test?characterEncoding=utf8&useSSL=false
spring.datasource.username=mysql
spring.datasource.password=123456

# 开启调试模式,打印sql
#logging.level.com.example.server.soa.dao.mysql.mapper=DEBUG

并新建 domain 对象:

public class ProductInfo {
    private Long productId;
    private String productName;
    private BigDecimal productPrice;
    private Date gmtCreate;
    private Date gmtModified;
    // 省略getter、setter方法
}

Mybatis 有 xml 和注解两种开发方式,推荐使用 xml 开发方式。

1.Mybatis xml开发方式

xml 版本保持了映射文件的老传统,系统会自动根据方法名在映射文件中找对应的 sql。在 application.properties 中新增 mybatis.* 前缀配置:

mybatis.config-location=classpath:mybatis-config.xml
mybatis.mapper-locations=classpath:mapper/*Mapper.xml

其中 resources/mybatis-config.xml 中可以以传统 xml 的方式配置 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>
</configuration>

在 resources/mapper 目录下添加 ProductInfoMapper.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.example.server.soa.dao.mysql.mapper.ProductInfoMapper">
  <resultMap id="BaseResultMap" type="com.example.server.soa.dao.mysql.domain.ProductInfo">
    <id column="product_id" jdbcType="BIGINT" property="productId" />
    <result column="product_name" jdbcType="VARCHAR" property="productName" />
    <result column="product_price" jdbcType="DECIMAL" property="productPrice" />
    <result column="gmt_create" jdbcType="TIMESTAMP" property="gmtCreate" />
    <result column="gmt_modified" jdbcType="TIMESTAMP" property="gmtModified" />
  </resultMap>
  <sql id="Base_Column_List">
    product_id, product_name, product_price, gmt_create, gmt_modified
  </sql>
  <select id="select" parameterType="java.lang.Long" resultMap="BaseResultMap" >
    select
    <include refid="Base_Column_List" />
    from product_info
    where product_id = #{productId,jdbcType=BIGINT}
  </select>
  <select id="selectAll" resultMap="BaseResultMap">
    select
    <include refid="Base_Column_List" />
    from product_info
  </select>
  <delete id="delete" parameterType="java.lang.Long" >
    delete from
    product_info
    where
    product_id =#{productId}
  </delete>
  <insert id="insert" parameterType="com.example.server.soa.dao.mysql.domain.ProductInfo">
    insert into product_info
    <trim prefix="(" suffix=")" suffixOverrides=",">
      <if test="productName != null">
        product_name,
      </if>
      <if test="productPrice != null">
        product_price,
      </if>
    </trim>
    <trim prefix="values (" suffix=")" suffixOverrides=",">
      <if test="productName != null">
        #{productName,jdbcType=VARCHAR},
      </if>
      <if test="productPrice != null">
        #{productPrice,jdbcType=DECIMAL},
      </if>
    </trim>
  </insert>
  <update id="update" parameterType="com.example.server.soa.dao.mysql.domain.ProductInfo">
    update product_info
    <set>
      <if test="productName != null">
        product_name = #{productName,jdbcType=VARCHAR},
      </if>
      <if test="productPrice != null">
        product_price = #{productPrice,jdbcType=DECIMAL},
      </if>
    </set>
    where product_id = #{productId,jdbcType=BIGINT}
  </update>
</mapper>

然后编写 dao 层的 Java 代码:

import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface ProductInfoMapper {
    ProductInfo select(Long productId);
    List<ProductInfo> selectAll();
    int delete(Long productId);
    int insert(ProductInfo productInfo);
    int update(ProductInfo productInfo);
}

这样就完成了 dao 层的开发,使用的时候当作普通的类注入就可以了:

@Resource
private ProductInfoMapper productInfoMapper;

2.Mybatis注解开发方式

这种版本无需编写 xml,直接编写 mapper 类即可:

import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface ProductInfoMapper {
    @Select("SELECT * FROM product_info WHERE product_id =#{productId,jdbcType=BIGINT}")
    @Results({
            @Result(property = "productId",  column = "product_id"),
            @Result(property = "productName", column = "product_name"),
            @Result(property = "productPrice", column = "product_price"),
            @Result(property = "gmtCreate", column = "gmt_create"),
            @Result(property = "gmtModified", column = "gmt_modified")
    })
    ProductInfo select(Long productId);

    @Select("SELECT * FROM product_info")
    @Results({
            @Result(property = "productId",  column = "product_id"),
            @Result(property = "productName", column = "product_name"),
            @Result(property = "productPrice", column = "product_price"),
            @Result(property = "gmtCreate", column = "gmt_create"),
            @Result(property = "gmtModified", column = "gmt_modified")
    })
    List<ProductInfo> selectAll();

    @Delete("DELETE FROM product_info WHERE product_id =#{productId,jdbcType=BIGINT}")
    int delete(Long productId);

    @Insert("INSERT INTO product_info(product_id,product_name,product_price) VALUES(#{productId,jdbcType=BIGINT},#{productName,jdbcType=VARCHAR},#{productPrice,jdbcType=DECIMAL})")
    int insert(ProductInfo productInfo);

    @UpdateProvider(type = ProductSqlBuilder.class, method = "buildUpdate")
    int update(ProductInfo productInfo);

    class ProductSqlBuilder {
        public String buildUpdate (final ProductInfo productInfo) {
            return new SQL(){{
                UPDATE("product_info");
                if (productInfo.getProductName() != null) {
                    SET("product_name=#{productName,jdbcType=VARCHAR}");
                }
                if (productInfo.getProductPrice() != null) {
                    SET("product_price=#{productPrice,jdbcType=DECIMAL}");
                }
                WHERE("product_id =#{productId,jdbcType=BIGINT}");
            }}.toString();
        }
    }
}

如果传入的属性为枚举,可以使用 javaType 来指定:

@Select("SELECT * FROM product_info")
@Results({
        @Result(property = "productEnum",  column = "product_enum", javaType = ProductEnum.class),
})
List<ProductInfo> selectAll();

编写 Mapper 一步到位,这样就完成了 dao 层的开发,使用方法和 xml 开发方式没有任何区别。

3.多数据源的支持

1、同源数据库的多源支持

创建一个 Spring 配置类,定义两个 DataSource 用来读取 application.properties 中的不同配置。如下例子中,主数据源配置为 spring.datasource.primary 开头的配置,第二数据源配置为 spring.datasource.secondary 开头的配置。这里给出 primary 的配置,secondary 的配置类似。

@Configuration
@MapperScan(basePackages = "com.example.server.soa.dao.mysql.mapper.primary", sqlSessionTemplateRef  = "primarySqlSessionTemplate")
public class DataSourceConfig {

    @Bean(name = "primaryDataSource")
    @Qualifier("primaryDataSource")
    @ConfigurationProperties(prefix="spring.datasource.primary")
    public DataSource primaryDataSource() {
        return DataSourceBuilder.create().build();
    }

    @Bean(name = "primarySqlSessionFactory")
    @Primary
    public SqlSessionFactory testSqlSessionFactory(@Qualifier("primaryDataSource") DataSource dataSource) throws Exception {
        SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
        bean.setDataSource(dataSource);
        bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mybatis/mapper/primary/*.xml"));
        return bean.getObject();
    }

    @Bean(name = "primaryTransactionManager")
    @Primary
    public DataSourceTransactionManager testTransactionManager(@Qualifier("primaryDataSource") DataSource dataSource) {
        return new DataSourceTransactionManager(dataSource);
    }

    @Bean(name = "primarySqlSessionTemplate")
    @Primary
    public SqlSessionTemplate testSqlSessionTemplate(@Qualifier("primarySqlSessionFactory") SqlSessionFactory sqlSessionFactory) throws Exception {
        return new SqlSessionTemplate(sqlSessionFactory);
    }
}

最关键的地方就是这块了,一层一层注入,再创建 DataSource,再创建 SqlSessionFactory,再创建事务,最后包装到 SqlSessionTemplate 中。其中需要制定分库的 mapper 文件地址以及分库 Dao 层代码。对应的 application.properties 配置如下:

spring.datasource.primary.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.primary.url=jdbc:mysql://localhost:3306/spring_test?characterEncoding=utf8&useSSL=false
spring.datasource.primary.username=mysql
spring.datasource.primary.password=123456

spring.datasource.secondary.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.secondary.url=jdbc:mysql://localhost:3306/spring_test2?characterEncoding=utf8&useSSL=false
spring.datasource.secondary.username=mysql
spring.datasource.secondary.password=123456

然后 Mybatis 操作同源不同地址的数据库,只需要在对应的目录下编写代码即可。

Common Mapper 可以随意的按照自己的需要选择通用方法,还可以很方便的开发自己的通用方法。支持单表操作,不支持通用的多表联合查询。分页插件 PageHelper 支持任何复杂的单表、多表分页。这两个开源项目的作者是同一个人,所以这里放到一起说,在使用上两者没有任何依赖关系。

2.Common Mapper的使用

首先在 MySQL 数据库创建表:

CREATE TABLE `product_info` (
  `product_id` bigint(18) unsigned NOT NULL COMMENT '商品id, 主键',
  `product_name` varchar(32) NOT NULL COMMENT '商品名称',
  `product_price` decimal(8,2) unsigned NOT NULL DEFAULT '99999.99' COMMENT '商品单价',
  `gmt_create` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  `gmt_modified` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间',
  PRIMARY KEY (`product_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

使用 Common Mapper 需要添加 Maven 依赖:

<!-- mybatis -->
<dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
    <version>1.3.5</version>
</dependency>
<!-- common mapper -->
<dependency>
    <groupId>tk.mybatis</groupId>
    <artifactId>mapper-spring-boot-starter</artifactId>
    <version>2.1.5</version>
</dependency>
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
</dependency>

然后通过 application.properties 中的 spring.datasource.* 前缀配置属性:

spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/spring_test?characterEncoding=utf8&useSSL=false
spring.datasource.username=mysql
spring.datasource.password=123456

# 开启调试模式,打印sql
#logging.level.com.example.server.soa.dao.mysql.mapper=DEBUG

并新建 domain 对象:

import javax.persistence.Id;
public class ProductInfo {
    @Id
    private Long productId;
    private String productName;
    private BigDecimal productPrice;
    private Date gmtCreate;
    private Date gmtModified;
    // 省略getter、setter方法
}

这里需要注意的是为主键字段标记 @Id 注解,否则当你使用带有 ByPrimaryKey 的方法时,所有的字段会作为联合主键来使用。

下来我们编写 dao 层的 Java 代码,只需要继承通用 Mapper 接口即可:

import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface ProductInfoMapper extends tk.mybatis.mapper.common.Mapper<ProductInfo> {
}

通用 Mapper 提供了大量的通用接口:

selectOne
select
selectAll
selectCount
selectByPrimaryKey
方法太多,省略其他...

这样就完成了 dao 层的开发,使用的时候当作普通的类注入就可以了:

@Resource
private ProductInfoMapper productInfoMapper;

3.PageHelper的使用

使用分页插件 PageHelper 需要添加 Maven 依赖:

<!-- mybatis物理分页插件 -->
<dependency>
    <groupId>com.github.pagehelper</groupId>
    <artifactId>pagehelper-spring-boot-starter</artifactId>
    <version>1.2.12</version>
</dependency>

我们在 Service 层中调用 Mapper 时,即可无侵入的进行分页查询:

// 物理分页
PageHelper.startPage(pageNum, pageSize);
PageHelper.orderBy("product_id desc");
List<ProductInfo> productInfoList = productInfoMapper.selectAll();

productInfoList 即是分页查询后的结果。其中 productInfoList 表面上是 List 类型,实际上是 Page (extends ArrayList) 类型。在 productInfoList 没有经过处理直接返回的情况下,我们就可以在 Controller 层获取 PageInfo 对象:

// 取分页后结果
PageInfo<ProductInfo> pageInfo = new PageInfo<ProductInfo>(productInfoList);
log.info("总记录数: {}", pageInfo.getTotal());

4.Mybatis原理

1、Mybatis 怎么把 xml 文件加载成 sql 执行的 ?

2、跟 jdbc 编译、预编译有什么区别?

3、连接池?

参考:http://mybatis.tk/
http://apidoc.gitee.com/free/Mapper/
http://github.com/abel533/Mapper/wiki
http://apidoc.gitee.com/free/Mybatis_PageHelper/
http://github.com/pagehelper/Mybatis-PageHelper/blob/master/wikis/zh/HowToUse.md
http://github.com/abel533/MyBatis-Spring-Boot

  • 11
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值