当前位置:首页 > Java技术 > SpringBoot2.x(八)整合Mybatis和事务讲解

SpringBoot2.x(八)整合Mybatis和事务讲解

2022年09月16日 21:40:39Java技术8

持久化数据方式介绍

  • 原始java访问数据库:开发流程麻烦

    • ​ 1、注册驱动/加载驱动:Class.forName("com.mysql.jdbc.Driver")

    • ​ 2、建立连接

      Connection con = DriverManager.getConnection("jdbc:mysql://localhost:3306/dbname","root","root");

       

    • ​ 3、创建Statement

    • ​ 4、执行SQL语句

    • ​ 5、处理结果集

    • ​ 6、关闭连接,释放资源

  • apache dbutils框架:比上一步简单点

  • jpa框架

    • spring-data-jpa
    • jpa复杂查询的时候性能不是很好
  • Hiberante (ORM:对象关系映射Object Relational Mapping)

    • 企业大都喜欢使用hibernate(OA、CRM)
  • Mybatis框架

    • 互联网行业通常使用mybatis
    • 不提供对象和关系模型的直接映射,半ORM

SpringBoot2.x整合Mybatis3.x

spring initializer-mybatis

如果你使用的IDE是IDEA,那么你可以通过IDEA集成的 spring initializer来快速创建一个 mybatis项目:

SpringBoot2.x(八)整合Mybatis和事务讲解 _ JavaClub全栈架构师技术笔记

接着填写 Group IDArtifact ID点击 Next。接着选择项目用到的技术

SpringBoot2.x(八)整合Mybatis和事务讲解 _ JavaClub全栈架构师技术笔记

我这里选择了 mybatisdevtoolslombok(不熟悉请自行google)

如果你用的不是IDEA,那么也可以在创建一个maven项目后引入如下依赖:

<dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
    <version>1.3.2</version>
</dependency>

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-devtools</artifactId>
    <scope>runtime</scope>
</dependency>
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <optional>true</optional>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
</dependency>

 

引入mysql驱动和数据源

<!-- MySQL的JDBC驱动包	-->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <scope>runtime</scope>
</dependency>
<!-- 引入第三方数据源 -->
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid</artifactId>
    <version>1.1.6</version>
</dependency>

 

添加springboot与mybatis的整合配置

resources下创建 application.properties并添加如下内容

#mybatis.type-aliases-package=top.zhenganwen.springboot-mybatis.domain
#数据库驱动,可以省略(springboot会自动检测)
#spring.datasource.driver-class-name =com.mysql.jdbc.Driver

spring.datasource.url=jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf-8
spring.datasource.username =root
spring.datasource.password =root
#如果不使用默认的数据源 (com.zaxxer.hikari.HikariDataSource)
spring.datasource.type =com.alibaba.druid.pool.DruidDataSource

 

建表

DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
  `id` bigint(20) NOT NULL,
  `name` varchar(255) DEFAULT NULL,
  `age` int(11) DEFAULT NULL,
  `phone` varchar(255) DEFAULT NULL,
  `create_time` datetime DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

 

domain

package top.zhenganwen.springbootmybatis.domain;

import lombok.Data;

import java.util.Date;

@Data
public class User {

    private Long id;
    private String name;
    private int age;
    private Date createTime;
    private String phone;
}

 

mapper

创建mapper接口对应实体类属性名称和数据库字段名称操作数据库

package top.zhenganwen.springbootmybatis.mapper;

import org.apache.ibatis.annotations.Insert;
import top.zhenganwen.springbootmybatis.domain.User;

public interface UserMapper {

    @Insert("insert into user " +
            "(id,name,age,phone,create_time) " +
            "values (#{id},#{name},#{age},#{phone},#{createTime})")
    int insert(User user);
}

 

值得注意的是 #可以用 $代替。但两者作用不同:#表示占位,会作为?被预编译到sql语句中;而 $表示拼接,相当于 ""+xxx+"",会导致sql不被预编译。建议尽量使用 #替代 $,因为 $会引起 sql注入 的风险。

@MapperScan(value=xx)

在启动类上添加 @MapperScan注解,value 扫描指定包下的 mapper接口(生成代理类)

package top.zhenganwen.springbootmybatis;

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
@MapperScan(value = "top.zhenganwen.springbootmybatis.mapper")
public class SpringbootMybatisApplication {

   public static void main(String[] args) {
      SpringApplication.run(SpringbootMybatisApplication.class, args);
   }
}

 

测试

package top.zhenganwen.springbootmybatis;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import top.zhenganwen.springbootmybatis.domain.User;
import top.zhenganwen.springbootmybatis.mapper.UserMapper;

import java.util.Date;

@RunWith(SpringRunner.class)
@SpringBootTest
public class SpringbootMybatisApplicationTests {

   @Autowired
   private UserMapper userMapper;

   @Test
   public void contextLoads() {
      User user = new User();
      user.setAge(18);
      user.setId(2018L);
      user.setCreateTime(new Date());
      user.setPhone("3456");
      user.setName("tony");
      userMapper.insert(user);
   }
}

 

单元测试禄条,查看 user表发现数据插入成功。

获取自增id

如果将主键 id设置为自增长型,我们可以通过添加@Options注解在插入记录后返回该记录的 id

public interface UserMapper {

    @Insert("insert into user " +
            "(id,name,age,phone,create_time) " +
            "values (#{id},#{name},#{age},#{phone},#{createTime})")
    @Options(useGeneratedKeys = true,keyProperty = "id",keyColumn = "id")
    int insert(User user);
}

 

再次测试:

@Test
public void contextLoads() {
    User user = new User();
    user.setAge(18);
    user.setCreateTime(new Date());
    user.setPhone("3456");
    user.setName("jack");
    userMapper.insert(user);
    System.out.println(user.getId());	//2019
}

 

  • useGeneratedKeys表示将自增生成的id设置到对象中
  • keyProperty对应实体类的属性名称,自增生成的id将被复值给该属性
  • keyColumn对应数据库表的主键名称

参考资料


增删改查和打印SQL语句

打印SQL语句

需要在 application.properties中增加如下配置:

#增加打印sql语句,一般用于本地开发测试
mybatis.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl

 

这样每次 jdbcmysql发送的sql语句都会在控制台输出

JDBC Connection [com.mysql.jdbc.JDBC4Connection@1d2644e3] will not be managed by Spring
==>  Preparing: insert into user (id,name,age,phone,create_time) values (?,?,?,?,?) 
==> Parameters: null, tom(String), 18(Integer), 3456(String), 2018-07-20 13:40:29.836(Timestamp)
<==    Updates: 1
Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@2aa27288]

 

CURD

select *

@Select("select * from user")
@Results({
    @Result(column = "create_time", property = "createTime")
})
List<User> findAll();

 

@Result用来协调属性名称和表字段名不一致的问题。List<User>中的泛型不可省。

@Test
public void testFindAll() {
    List userList = userMapper.findAll();
    System.out.println(userList);
}
[User(id=2018, name=tony, age=18, createTime=Fri Jul 20 11:59:29 CST 2018, phone=3456), User(id=2019, name=jack, age=18, createTime=Fri Jul 20 13:10:31 CST 2018, phone=3456)]

where

 @Select("select * from user where id=#{id}")
@Results({
    @Result(column = "create_time", property = "createTime")
})
User findById(Long id);
@Test
public void testFindById() {
   User user = userMapper.findById(2018L);
   System.out.println(user);
}
User(id=2018, name=tony, age=18, createTime=Fri Jul 20 11:59:29 CST 2018, phone=3456)

 

delete

@Delete("delete from user where id=#{id}")
void delbyId(Long id);
@Test
public void testDelById() {
    userMapper.delbyId(2019L);
    System.out.println(userMapper.findAll());
}
[User(id=2018, name=tony, age=18, createTime=Fri Jul 20 11:59:29 CST 2018, phone=3456)]

 

update

@Update("update user set name=#{name},age=#{age} where id=#{id}")
void update(User user);
@Test
public void testUpdateById() {
    User user = userMapper.findById(2018L);
    user.setName("new name");
    user.setAge(99);
    userMapper.update(user);
    System.out.println(userMapper.findById(2018L));
}
User(id=2018, name=new name, age=99, createTime=Fri Jul 20 11:59:29 CST 2018, phone=3456)

 

事务介绍、常见的隔离级别、传播行为

事务

传统的单机事务

SpringBoot2.x(八)整合Mybatis和事务讲解 _ JavaClub全栈架构师技术笔记

以转账为例,一次业务涉及数据库表的两次更改,为确保业务完整性需要求这两次更改要么都成功要么都失败。

分布式事务

SpringBoot2.x(八)整合Mybatis和事务讲解 _ JavaClub全栈架构师技术笔记

以电商为例,用户支付订单费用之后需要更新位于三台不同主机上的数据库表(微服务),这时 mysql内置实现的单机事务机制已然无用。这时就用到了分布式事务解决方案,如:二次提交、最终一致性(消息中间件)

隔离级别

  • Serializable: 最严格,串行处理,消耗资源大
  • Repeatable Read:保证了一个事务不会修改已经由另一个事务读取但未提交(回滚)的数据
  • Read Committed:大多数主流数据库的默认事务等级
  • Read Uncommitted:保证了读取过程中不会读取到非法数据。

传播行为

  • PROPAGATION_REQUIRED--支持当前事务,如果当前没有事务,就新建一个事务,最常见的选择。
  • PROPAGATION_SUPPORTS--支持当前事务,如果当前没有事务,就以非事务方式执行
  • PROPAGATION_MANDATORY--支持当前事务,如果当前没有事务,就抛出异常。
  • PROPAGATION_REQUIRES_NEW--新建事务,如果当前存在事务,把当前事务挂起, 两个事务之间没有关系,一个异常,一个提交,不会同时回滚
  • PROPAGATION_NOT_SUPPORTED--以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
  • PROPAGATION_NEVER--以非事务方式执行,如果当前存在事务,则抛出异常

SpringBoot2.x整合Mybatis之事务处理

数据准备

在上节创建的 user表中添加 account字段,并添加一条 id2019的记录,将 20182019两个用户的 account设置为 100

改写 update方法:

@Update("update user set account=#{account} where id=#{id}")
void update(User user);

 

创建Service层

public interface UserService {
    void transferAccount();
}
@Service
public class UserServiceImpl implements UserService {

    @Autowired
    private UserMapper userMapper;

    @Override
    public void transferAccount() {
        User a = userMapper.findById(2018L);
        a.setAccount(a.getAccount() - 100);
        userMapper.update(a);
        int i = 1 / 0;
        User b = userMapper.findById(2019L);
        b.setAccount(b.getAccount() + 100);
        userMapper.update(b);
    }
}

无事务测试

@Autowired
private UserService userService;
@Test
public void testTransferAccount() {
   userService.transferAccount();
}

 

查看表发现 2018account变成了 0,而 2019的仍为 100

添加事务

我们需要在需要事务的方法上添加 @Transactional注解,并通过 rollbackForpropagation指定回滚触发条件和事务机制

@Transactional(rollbackFor = Exception.class,propagation = Propagation.REQUIRED)
@Override
public void transferAccount() {
    User a = userMapper.findById(2018L);
    a.setAccount(a.getAccount() - 100);
    userMapper.update(a);
    int i = 1 / 0;
    User b = userMapper.findById(2019L);
    b.setAccount(b.getAccount() + 100);
    userMapper.update(b);
}

 

  • rollbackFor = Exception.class表示触发回滚的条件是抛出异常
  • propagation = Propagation.REQUIRED是默认的事务机制,若当前有事务则支持当前事务,否则新建事务
  • @Transactional也可以加在类上,则该类所有方法都遵循该注解配置

2018account改为 100并再次测试,发现转账时抛出异常,两人 account不变。

 

更多学习资料请到白玉搜一搜搜索下载

作者:it_haha
来源链接:https://blog.csdn.net/xvhongliang/article/details/100518132

版权声明:
1、JavaClub(https://www.javaclub.cn)以学习交流为目的,由作者投稿、网友推荐和小编整理收藏优秀的IT技术及相关内容,包括但不限于文字、图片、音频、视频、软件、程序等,其均来自互联网,本站不享有版权,版权归原作者所有。

2、本站提供的内容仅用于个人学习、研究或欣赏,以及其他非商业性或非盈利性用途,但同时应遵守著作权法及其他相关法律的规定,不得侵犯相关权利人及本网站的合法权利。
3、本网站内容原作者如不愿意在本网站刊登内容,请及时通知本站(javaclubcn@163.com),我们将第一时间核实后及时予以删除。


本文链接:https://www.javaclub.cn/java/42185.html

分享给朋友:

“SpringBoot2.x(八)整合Mybatis和事务讲解” 的相关文章

SpringBoot事务简单操作及手动回滚

本节部分内容摘自:https://blog.csdn.net/zzhongcy/article/details/102893309 一、引入依赖 <!-- 核心启动器, 包括auto-configuration、logging and YAML -->...

spring boot配置事务(事务回滚)

spring boot配置事务(事务回滚)

在业务层(service)中难免存在要先后操作多张表的业务逻辑,如前端完成一个操作,后台需要对数据库三张表进行操作,如若任意一个操作事务,这几个操作都应该不允许被实现,即回到操作前,解决方法是开启事务,让其在出错时实现事务回滚作用。 如:有以下三个...

SpringBoot2.x学习-事务管理

一、事务介绍 1、数据库事务:是数据库管理系统执行过程中的一个逻辑单位,由一个有限的数据库操作序列构成。这些操作要么全部执行成功提交(commit),要么全部中止失败(abort,rollback)。就是在数据库执行多条SQL语句,要么都执行成功,...

springboot MongoDB 事务

前言 有玩过mongodb的朋友大概会知道mongodb4.0版本已经可以支持多文档副本集事务。而最新版本4.2更是支持分片事务,即真正的支持分布式事务。不过当时我使用mongodb,其最新版本为4.10,4.2版本还没发布,因此本文还是以4.0版本的副本集事务来讲解。...

SpringBoot多数据源配置事务

在多数据源中配置事务,其实对于SpringBoot来很简单,当然这个的前提是首先把多数据源都配好的情况下,如果不会多数据源配置,请看该系列 SpringBoot整合多数据源 首先在启动类配置 @SpringBootApplication @E...

SpringBoot jpa事务注解详解

SpringBoot jpa事务注解详解

@Transactional spring 事务注解 1.简单开启事务管理 @EnableTransactionManagement // 启注解事务管理,等同于xml配置方式的 <tx:annotation-driven /> 2.事务注解详...

浅谈SpringBoot事务处理

浅谈SpringBoot事务处理

什么是事务 所有数据访问技术都有事务机制,这些技术提供了API来开启事务、提交事务完成数据操作,或者在发生错误的时候回滚数据。 Spring采用统一的机制来处理不同的数据访问技术的事务,Spring的事务提供一个 PlatformTransactionManager...

springBoot service 事务注解@Transactional不起作用的解决

在springBoot使用事物时,发现事务并没有正常执行,没有进行回滚 @Transactional public void add(String companyName,String name) throws MyException{ compan...

SpringBoot非官方教程 | 第七篇:springboot开启声明式事务

SpringBoot非官方教程 | 第七篇:springboot开启声明式事务

转载请标明出处: http://blog.csdn.net/forezp/article/details/70833629 本文出自方志朋的博客 springboot开启事务很简单,只需要一个注解@Transactional 就可以了。因为...

springboot 事务应用

springboot 事务应用

Spring 提供一个 @EnableTranscationManagement 注解在配置类上来开启声明式事务的支持。使用了 @EnableTranscationManagement 后,Spring 容器会自动扫描注解 @Transactional...

发表评论

访客

◎欢迎参与讨论,请在这里发表您的看法和观点。