前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >玩转 Spring Boot 集成篇(MyBatis、JPA、事务支持)

玩转 Spring Boot 集成篇(MyBatis、JPA、事务支持)

作者头像
一猿小讲
发布2022-02-25 09:41:05
1.6K0
发布2022-02-25 09:41:05
举报
文章被收录于专栏:一猿小讲一猿小讲

在使用 Spring 进行实际项目研发中,Spring 整合 ORM 组件(MyBatis、JPA)是必不可少一个环节,而在整合过程中,往往要进行大量的配置。

借助 SpringBoot 可以屏蔽 Spring 整合 ORM 组件配置的大量简化,进而让研发人员更加专注于业务逻辑的开发,使得企业级项目开发更加快速和高效。

本文将重点分享 Spring Boot 与两种常用的 ORM 组件的整合:MyBatis 和 JPA,顺带提一嘴 Spring Boot 的事务支持。

1. Spring Boot 集成 MyBatis

MyBatis 是一款优秀的持久层框架,它支持定制化 SQL、存储过程以及高级映射。MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。MyBatis 可以使用简单的 XML 或注解来配置和映射原生信息,将接口和 Java 的 POJOs(Plain Ordinary Java Object,普通的 Java对象)映射成数据库中的记录。 MyBatis 特点:简单易学、灵活、解除sql与程序代码的耦合、提供映射标签,支持对象与数据库的orm字段关系映射、提供对象关系映射标签,支持对象关系组建维护、提供xml标签,支持编写动态sql等。

1.1. 引入依赖

代码语言:javascript
复制
<!-- 引入 MyBatis 依赖-->
<dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybaits-spring-boot-starter</artifactId>
    <version>2.2.1</version>
</dependency>

1.2. 添加配置

代码语言:javascript
复制
# MySQL 链接信息
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/test?serverTimezone=UTC
spring.datasource.username=root
spring.datasource.password=123456
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver

## MyBatis 的配置
# Mapper资源文件存放的路径
mybatis.mapper-locations=classpath*:mapper/*Mapper.xml
# Dao 接口文件存放的目录
mybatis.type-aliases-package=com.example.demo.dao
# 开启 debug,输出 SQL
logging.level.com.example.demo.dao=debug

1.3. 编写代码

主要完成秒杀商品的添加、查询相关的数据库操作。

1.3.1. 创建实体类

代码语言:javascript
复制
package com.example.demo.model;
import java.io.Serializable;
import java.util.Date;
/**
 * 商品类
 * @author caicai
 */
public class ScProduct implements Serializable {
    private Integer id;
    private String name;
    private String productImg;
    private Integer number;
    private Date startTime;
    private Date endTime;
    private Date createTime;
    // 提供 setter、getter 方法
}

1.3.2. 创建Dao

代码语言:javascript
复制
package com.example.demo.dao;
import com.example.demo.model.ScProduct;
import org.apache.ibatis.annotations.Mapper;

@Mapper
public interface ScProductDao {
    /** 查询指定编号的商品信息*/
    ScProduct findByProductId(Integer id);
    /** 保存商品信息*/
    int insert(ScProduct scProduct);
}
  • @Mapper 注解,MyBatis 会根据接口定义与 Mapper 文件中的 SQL 语句动态创建接口的实现。

1.3.3. 创建 Service 接口

代码语言:javascript
复制
package com.example.demo.service;
import com.example.demo.model.ScProduct;

public interface ScProductService {
    /** 查询指定编号的商品信息*/
    ScProduct findByProductId(Integer id);
    /** 保存商品信息*/
    int save(ScProduct scProduct);
}

1.3.4. 创建 Service 实现类

代码语言:javascript
复制
package com.example.demo.service.impl;
import com.example.demo.dao.ScProductDao;
import com.example.demo.model.ScProduct;
import com.example.demo.service.ScProductService;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;

@Service
public class ScProductServiceImpl implements ScProductService {

    @Resource
    ScProductDao scProductDao;

    @Override
    public ScProduct findByProductId(Integer id) {
        return scProductDao.findByProductId(id);
    }
    
    @Override
    public int save(ScProduct scProduct) {
        int saveRes = scProductDao.insert(scProduct);
        System.out.println("向数据库插入:" + scProduct);
        return saveRes;
    }
}

1.3.5. 创建 Mapper 文件

代码语言:javascript
复制
<?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.demo.dao.ScProductDao">

    <resultMap id="BaseResultMap" type="com.example.demo.model.ScProduct">
        <id column="id" property="id" jdbcType="INTEGER"/>
        <result column="name" property="name" jdbcType="VARCHAR"/>
        <result column="number" property="number" jdbcType="INTEGER"/>
        <result column="start_time" property="startTime" jdbcType="TIMESTAMP"/>
        <result column="end_time" property="endTime" jdbcType="TIMESTAMP"/>
        <result column="create_time" property="createTime" jdbcType="TIMESTAMP"/>
        <result column="product_img" property="productImg" jdbcType="VARCHAR"/>
    </resultMap>

    <select id="findByProductId" resultMap="BaseResultMap">
          select * from sc_product where id = #{id,jdbcType=INTEGER}
     </select>

    <insert id="insert" parameterType="com.example.demo.model.ScProduct">
          insert into sc_product (id, name, number,start_time,end_time,create_time,product_img)
          values (#{id,jdbcType=INTEGER}, #{name,jdbcType=VARCHAR},#{number,jdbcType=INTEGER},
          #{startTime,jdbcType=TIMESTAMP}, #{endTime,jdbcType=TIMESTAMP},#{createTime,jdbcType=TIMESTAMP},
          #{productImg,jdbcType=VARCHAR})
     </insert>
</mapper>
  • 标签介绍

1.4. 测试集成

代码语言:javascript
复制
package com.example.demo;
import com.example.demo.model.ScProduct;
import com.example.demo.service.ScProductService;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import javax.annotation.Resource;
import java.util.Date;

@SpringBootTest
public class MyBatisTests {

    @Resource
    ScProductService scProductService;

    @Test
    public void saveTest() {
        ScProduct sp = new ScProduct();
        sp.setId(6);
        sp.setName("ihpone 16 512g 玫瑰红");
        sp.setStartTime(new Date());
        sp.setEndTime(new Date());
        sp.setCreateTime(new Date());
        sp.setNumber(100);
        sp.setProductImg("./ihpne16.png");
        int saveRes = scProductService.save(sp);
        System.out.println("向数据库添加商品结果:" + (saveRes == 1 ? "成功" : "失败"));
    }

    @Test
    public void findByProductId() {
        Integer id = 6;
        ScProduct sp = scProductService.findByProductId(id);
        System.out.println("编号为 " + id + "的商品信息为:" + sp);
    }
}

执行 saveTest 单元验证,输出如下:

执行 findByProductId 单元验证,输出如下:

至此,Spring Boot 与 MyBatis 便集成完毕。不过那多 Mpper 文件、那么多实体、那么多 Service 等要编写代码,也挺繁琐,其实这些都是可以自动生成的,不过不是本文的分享重点(捂嘴笑),接下来谈谈 Spring Boot 事务的支持。

2. Spring Boot 事务的支持

Spring Boot 开启事务的方式很简单,只需要一个注解 @Transactional 就轻松搞定,此注解可以用在类上,也可以用在方法上。

  • @Transactional 若注解在类上,那么此类的所有 public 方法都是开启事务的。
  • @Transactional 若注解在方法上面,那么方法级别的注解会覆盖类级别注解。

接下来基于上面第 1 章节的代码稍作改动,便可验证 Spring Boot 事务的支持。

思考:如果保存商品时出现了异常,看看保存的商品能否添加成功?

此刻,在保存商品时,需要人为制造一个空指针异常。

2.1. 不加 @Transactional 注解

代码语言:javascript
复制
@Override
public int save(ScProduct scProduct) {
    int saveRes = scProductDao.insert(scProduct);
    System.out.println("向数据库插入:" + scProduct);
    
    // 制造一个异常,看看事务是否回滚
    String str = null;
    System.out.println(str.length());

    return saveRes;
}

为了方便观察结果,执行单元测试之前,需要从库中把 Id 为 6 的商品给删除掉,然后执行单元测试。

执行 saveTest 单元测试,向数据库保存 Id 为 6 的商品,执行结果如下:

执行 findByProductId 单元测试,查询 Id 为 6 的商品是否存在,执行结果如下:

很显然,不是预期的效果,如果保存商品 service 出现了异常,不应该保存成功才对,那就需要配置 @Transactional 注解。

2.2. 添加 @Transactional 注解

代码语言:javascript
复制
@Transactional
@Override
public int save(ScProduct scProduct) {
    int saveRes = scProductDao.insert(scProduct);
    System.out.println("向数据库插入:" + scProduct);

    // 制造一个异常,看看事务是否回滚
    String str = null;
    System.out.println(str.length());

    return saveRes;
}

为了方便观察结果,执行单元测试之前,需要从库中把 Id 为 6 的商品给删除掉,然后执行单元测试。

执行 saveTest 单元测试,向数据库保存 Id 为 6 的商品,执行结果如下:

执行 findByProductId 单元测试,查询 Id 为 6 的商品是否存在,执行结果如下:

很显然,当保存商品的 service 方法执行出现异常时,商品添加失败,符合心理预期。

@Transactional 注解在类上,那么此类的所有 public 方法都是开启事务的,对于本文的效果是一样的,不再赘述。

3. Spring Boot 集成 JPA

JPA 是 Java Persistence API 的简称,中文名 Java 持久层 API,是 JDK 5.0 注解或 XML 描述对象-关系表的映射关系,并将运行期的实体对象持久化到数据库中。Sun引入新的 JPA ORM 规范出于两个原因:其一,简化现有 Java EE 和 Java SE 应用开发工作;其二,Sun 希望整合 ORM 技术,实现天下归一。 JPA 实现:Hibernate3.2+、TopLink 10.1.3 以及 OpenJPA。

Spring Data JPA 简化数据层的代码,进而让研发人员更加专注业务逻辑的实现。若要在 SpringBoot 中使用 Spring Data JPA,需要如下简单几步便可集成。

3.1. 引入依赖

代码语言:javascript
复制
<!-- 引入 JPA 依赖-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>

3.2. 添加配置

代码语言:javascript
复制
### JPA 配置
# 开启控制台 SQL 输出
spring.jpa.show-sql=true
# 开启格式化 SQL 输出
spring.jpa.properties.hibernate.format_sql=true

3.3. 编写代码

主要完成秒杀商品的查询相关的数据库操作。

3.3.1. 建立 ScProductRepository 接口

代码语言:javascript
复制
package com.example.demo.repository;
import com.example.demo.model.ScProduct;
import org.springframework.data.jpa.repository.JpaRepository;

/**
 * 商城商品Repository
 */
public interface ScProductRepository extends JpaRepository<ScProduct, Integer> {
}

3.3.2. 创建实体类

代码语言:javascript
复制
@Entity
@Table(name="sc_product")
public class ScProduct implements Serializable {

    @Id
    private Integer id;
    @Column
    private String name;
    @Column
    private String productImg;
    @Column
    private Integer number;
    @Column
    private Date startTime;
    @Column
    private Date endTime;
    @Column
    private Date createTime;
    
    // setter/ getter 方法
}
  • @Entity:在类的定义中使用 @Entity 注解来进行声明是一个实体 Bean。
  • @Table:声明此对象映射到数据库的数据表,非必须。
  • @Id:指定表的主键。

3.3.3. 编写测试类

代码语言:javascript
复制
package com.example.demo;
import com.example.demo.model.ScProduct;
import com.example.demo.repository.ScProductRepository;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import javax.annotation.Resource;
import java.util.Date;
import java.util.Optional;

@SpringBootTest
public class JpaRepositoryTest {

    @Resource
    ScProductRepository scProductRepository;

    @Test
    public void jpaTest() {
        // 测试向数据库插入记录
        ScProduct scProduct = new ScProduct();
        scProduct.setId(4);
        scProduct.setName("iphone 13 256g 玫瑰金");
        scProduct.setNumber(99);
        scProduct.setProductImg("./iphone13_256g.png");
        scProduct.setCreateTime(new Date());
        scProduct.setEndTime(new Date());
        scProduct.setStartTime(new Date());
        scProduct = scProductRepository.save(scProduct);
        System.out.println("商品编号为:" + scProduct.getId());

        // 测试从数据库查询记录
        Integer id = 4;
        Optional<ScProduct> sp = scProductRepository.findById(id);
        System.out.println("商品编号为" + id + " 的商品信息为:" + sp.get());
    }
}

3.4. 测试集成

执行单元测试,控制台输出如下。

至此,Spring Boot 与 JPA 集成完毕。

回头捋捋,若要在 SpringBoot 中使用 Spring Data JPA,貌似就只用声明持久层的接口,其它的都交给 Spring Data JPA 来完成了,可谓快哉。

4. 例行回顾

本文是 Spring Boot 项目集成持久层组件篇的讲解,主要分享了如下部分:

  • Spring Boot 项目如何集成 MyBatis?
  • Spring Boot 项目如何集成 JPA?
  • Spring Boot 对于事务的支持

玩转 Spring Boot 集成持久层组件就写到这里,希望大家能够喜欢。

追逐技术的道路上,发扬袋鼠精神「从不后退、永远前行」。停止精神消耗,做有价值的事;希望做的事情如钉钉子,钉一个是一个,实实在在。

一起聊技术、谈业务、喷架构,少走弯路,不踩大坑,会持续输出更多精彩分享

参考资料:

https://spring.io/

https://start.spring.io/

https://spring.io/projects/spring-boot

https://github.com/spring-projects/spring-boot

https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/

https://stackoverflow.com/questions/tagged/spring-boot

《Spring Boot从入门到实战》《深入浅出Spring Boot 2.x》

《一步一步学Spring Boot:微服务项目实战(第二版)》

《Spring Boot揭秘:快速构建微服务体系》

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2022-01-26,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 一猿小讲 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
云数据库 MySQL
腾讯云数据库 MySQL(TencentDB for MySQL)为用户提供安全可靠,性能卓越、易于维护的企业级云数据库服务。其具备6大企业级特性,包括企业级定制内核、企业级高可用、企业级高可靠、企业级安全、企业级扩展以及企业级智能运维。通过使用腾讯云数据库 MySQL,可实现分钟级别的数据库部署、弹性扩展以及全自动化的运维管理,不仅经济实惠,而且稳定可靠,易于运维。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档