零基础学习SpringBoot(二)--集成MyBatis以及事务开启

262 阅读4分钟

这是我参与更文挑战的第2天,活动详情查看: 更文挑战

1. SpringBoot集成MyBatis

1.1 准备数据库

  • 启动数据库,并通过Navicat连接

image-20210616103618973.png

  • 创建新的数据库springboot,指定数据库字符编码为utf-8, 并创建表t_person

image-20210616104206327.png

  • 向表中插入几条数据

image-20210616104506171.png

1.2 创建新项目

image-20210616105101249.png

1.3 在pom.xml中添加相关jar依赖

<!--MyBatis 整合SpringBoot的起步依赖-->
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.1.3</version>
        </dependency>
        
        <!--MySql 的驱动依赖-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>

1.4 在SpringBoot的核心配置文件application.properties中配置数据源

根据自己数据库的信息修改以下内容

server:
  # 配置内嵌Tomcat端口号
  port: 8888
  servlet:
    # 配置项目上下文根
    context-path: /springboot-web
    
# 配置数据源
spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/springboot?useUnicode=true&characterEncoding=UTF-8&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=GMT%2B8
    username: root
    password: 12345678

1.5 代码实现

1.5.1 使用Mybatis反向工程生成接口、映射文件以及实体Bean
  • 创建Mybatis反向工程配置文件 GeneratorMapper.xml
  • 在pom.xml文件中添加反向工程依赖
<!--Mybatis 代码动生成插件-->
            <plugin>
                <groupId>org.mybatis.generator</groupId>
                <artifactId>mybatis-generator-maven-plugin</artifactId>
                <version>1.3.6</version>
                <configuration>
                    <!--配置文件的位置-->
                    <configurationFile>GeneratorMapper.xml</configurationFile>
                    <verbose>true</verbose>
                    <overwrite>true</overwrite>
                </configuration>
            </plugin>
  • 修改GeneratorMapper.xml配置
<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE generatorConfiguration
        PUBLIC  "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
        "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
<generatorConfiguration>

    <!--指定连接数据库的JDBC驱动包所在位置, 指定到你本地的完整路径-->
    <classPathEntry location="/Volumes/developer/workspace/java-workspace/springboot/springboot-web/mysql-connector-java-8.0.25.jar"/>
    <!-- 配置 table 表信息内容体,targetRuntime 指定采用 MyBatis3 的版本 -->
    <context id="tables" targetRuntime="MyBatis3">
        <!-- 抑制生成注释,由于生成的注释都是英文的,可以不让它生成 -->
        <commentGenerator>
            <property name="suppressAllComments" value="true" />
        </commentGenerator>

        <!-- 配置数据库连接信息 -->
        <jdbcConnection driverClass="com.mysql.cj.jdbc.Driver"
                        connectionURL="jdbc:mysql://localhost:3306/springboot?useUnicode=true&amp;characterEncoding=UTF-8&amp;useJDBCCompliantTimezoneShift=true&amp;useLegacyDatetimeCode=false&amp;serverTimezone=GMT%2B8&amp;nullCatalogMeansCurrent=true"
                        userId="root"
                        password="12345678">
        </jdbcConnection>
        <!-- 生成 model 类,targetPackage 指定 model 类的包名, targetProject 指定生成的 model 放在 哪个工程下面-->
        <javaModelGenerator targetPackage="com.mufeng.springbootweb.model"
                            targetProject="src/main/java">
            <property name="enableSubPackages" value="false" />
            <property name="trimStrings" value="false" />
        </javaModelGenerator>

        <!-- 生成 MyBatis 的 Mapper.xml 文件,targetPackage 指定 mapper.xml 文件的包名, targetProject 指定生成的 mapper.xml 放在哪个工程下面 -->
        <sqlMapGenerator targetPackage="com.mufeng.springbootweb.mapper"
                         targetProject="src/main/java">
            <property name="enableSubPackages" value="false" />
        </sqlMapGenerator>

        <!-- 生成 MyBatis 的 Mapper 接口类文件,targetPackage 指定 Mapper 接口类的包名, targetProject 指定生成的 Mapper 接口放在哪个工程下面 -->
        <javaClientGenerator type="XMLMAPPER"
                             targetPackage="com.mufeng.springbootweb.mapper" targetProject="src/main/java">
            <property name="enableSubPackages" value="false" />
        </javaClientGenerator>

        <!-- 数据库表名及对应的 Java 模型类名 -->
        <table tableName="t_person" domainObjectName="Person"
               enableCountByExample="false"
               enableUpdateByExample="false"
               enableDeleteByExample="false"
        enableSelectByExample="false"
        selectByExampleQueryId="false"/>
    </context>

</generatorConfiguration>
  • 运行插件,生成相关文件

image-20210616133944318.png

image-20210616134015116.png

1.5.2 在controller包下创建PersonController并编写代码
package com.mufeng.springbootweb.controller;

import com.mufeng.springbootweb.model.Person;
import com.mufeng.springbootweb.services.PersonService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

@Controller
public class PersonController {

    @Autowired
    private PersonService personService;

    @RequestMapping(value = "/springboot/getPersonDetails")
    @ResponseBody
    public Object getPersonDetails(){
        Person person = personService.findPersonById(1);
        return person;
    }

}
1.5.3 在Service包下创建service接口并编写代码
package com.mufeng.springbootweb.services;

import com.mufeng.springbootweb.model.Person;

public interface PersonService {

    /**
     * 根据用户标识获取用户详情
     * @param id
     * @return
     */
    Person findPersonById(Integer id);

}
1.5.4 在 service.impl 包下创建 service 接口并编写代码
package com.mufeng.springbootweb.services.impl;

import com.mufeng.springbootweb.mapper.PersonMapper;
import com.mufeng.springbootweb.model.Person;
import com.mufeng.springbootweb.services.PersonService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class PersonServiceImpl implements PersonService {

    @Autowired
    private PersonMapper personMapper;

    @Override
    public Person findPersonById(Integer id) {
        return personMapper.selectByPrimaryKey(id);
    }
}

1.5.5 在 Mybatis 反向工程生成的 StudentMapper 接口上加一个 Mapper 注解

@Mapper 作用:mybatis 自动扫描数据持久层的映射文件及 DAO 接口的关系

package com.mufeng.springbootweb.mapper;

import com.mufeng.springbootweb.model.Person;
import org.apache.ibatis.annotations.Mapper;

@Mapper
public interface PersonMapper {
    int deleteByPrimaryKey(Integer id);

    int insert(Person record);

    int insertSelective(Person record);

    Person selectByPrimaryKey(Integer id);

    int updateByPrimaryKeySelective(Person record);

    int updateByPrimaryKey(Person record);
}

注意:默认情况下,MyBatis的xml映射文件不会编译到target的class目录下,所以我们需要在pom.xml中配置resource

        <resources>
            <resource>
                <directory>src/main/java</directory>
                <includes>
                    <include>
                        **/*.xml
                    </include>
                </includes>
            </resource>
        </resources>

1.6 启动Application应用,访问测试

image-20210616135744017.png

1.7 在运行的主类上添加mapper扫描注解

注释掉PersonMapper接口上的@Mapper注解

package com.mufeng.springbootweb.mapper;

import com.mufeng.springbootweb.model.Person;
import org.apache.ibatis.annotations.Mapper;

//@Mapper
public interface PersonMapper {
    int deleteByPrimaryKey(Integer id);

    int insert(Person record);

    int insertSelective(Person record);

    Person selectByPrimaryKey(Integer id);

    int updateByPrimaryKeySelective(Person record);

    int updateByPrimaryKey(Person record);
}

在运行主类Application上加@MapperScan("com.mufeng.springbootweb.mapper")

package com.mufeng.springbootweb;

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

@SpringBootApplication
@MapperScan("com.mufeng.springbootweb.mapper")
public class SpringbootWebApplication {

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

}
	

运行测试结果

image-20210616140212734.png

1.8 接口和映射文件分开

  • 修改GeneratorMapper.xml 文件中Mapper映射文件生成目标位置, 目标指向resources下的mapper文件夹下
        <!-- 生成 MyBatis 的 Mapper.xml 文件,targetPackage 指定 mapper.xml 文件的包名, targetProject 指定生成的 mapper.xml 放在哪个工程下面 -->
        <sqlMapGenerator targetPackage="mapper"
                         targetProject="src/main/resources">
            <property name="enableSubPackages" value="false" />
        </sqlMapGenerator>
  • 重新运行代码生成插件

image-20210616140740062.png

把之前生成的映射文件删除掉

  • 在application.yml配置文件中需要指定映射文件的位置,这个配置只有接口和映射文件不再同一个包的情况下,才需要指定
mybatis:
  mapper-locations: classpath:mapper/*.xml

2. SpringBoot的事务支持

SpringBoot 使用事务非常简单,底层依然采用的是Spring本身提供的事务管理

  • 在入口类中使用注解@EnableTransactionManagement 开启事务支持
  • 在访问数据库的Service方法上添加注解@Transactional即可
在PersonController中添加更新用户的方法
    @RequestMapping(value = "/springboot/updatePersonById")
    @ResponseBody
    public Object updatePersonById(){
        int count = 0;
        try{
            Person person = new Person();
            person.setId(1);
            person.setName("MuFeng_update");
            person.setAge(20);
            count = personService.updatePersonById(person);
        }catch (Exception e){
            e.printStackTrace();
            return "fail";
        }
        return count;
    }
在PersonService接口中添加更新用户的方法
    /**
     * 根据用户id更新用户信息
     * @param person
     * @return
     */
    int updatePersonById(Person person);
在PersonServiceImpl接口实现类中更新用户信息方法进行实现,并主动制造一个异常抛出
    @Override
    public int updatePersonById(Person person) {
        int updateCount = personMapper.updateByPrimaryKeySelective(person);
        System.out.println("更新结果:" + updateCount);
        //在此构造一个除数为 0 的异常,测试事务是否起作用
        int a = 10/0;
        return updateCount;
    }
运行并测试未开启事务的情况

image-20210616143035012.png

image-20210616143116964.png

抛出了异常但是数据修改成功了,我们需要做的是在动作出现异常时,所有的增删改的操作都要回滚

在PersonServiceImpl更新的实现方法上添加@Transactional注解
    @Override
    @Transactional
    public int updatePersonById(Person person) {
        int updateCount = personMapper.updateByPrimaryKeySelective(person);
        System.out.println("更新结果:" + updateCount);
        //在此构造一个除数为 0 的异常,测试事务是否起作用
        int a = 10/0;
        return updateCount;
    }
在Application类上加@EnableTransactionManagement注解开始事务支持
@SpringBootApplication
@MapperScan("com.mufeng.springbootweb.mapper")
@EnableTransactionManagement
public class SpringbootWebApplication {

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

}

@EnableTransactionManagement是可选的,但是业务方法上必须添加@Transactional事务才生效

修改Controller的修改用户信息的代码
@RequestMapping(value = "/springboot/updatePersonById")
    @ResponseBody
    public Object updatePersonById(){
        int count = 0;
        try{
            Person person = new Person();
            person.setId(1);
            person.setName("MuFeng_update");
            person.setAge(25);
            count = personService.updatePersonById(person);
        }catch (Exception e){
            e.printStackTrace();
            return "fail";
        }
        return count;
    }
运行测试

image-20210616144107690.png

image-20210616144137310.png

同样抛出了异常,但是数据库的年龄信息还是之前的20,并没有被修改,说明我们的事务开启成功了