事务隔离机制
事务的隔离级别描述的是多个事务之间的可见性问题。比如一个事务还未提交时,其他事务是否能看到被未提交事务修改的数据。
幻读(虚读)
select 某记录是否存在,不存在,准备插入此记录,但执行 insert 时发现此记录已存在,无法插入。或不存在,执行delete删除,却发现删除成功。
在一个事务的多次查询之间, 允许另一个事务进行插入、删除操作
不可重复读取
事务1读取记录时,事务2更新了记录并提交,事务1再次读取时可以看到事务2修改后的记录; 在一个事务处理过程中读取了另一个事务中修改并已提交的数据, 导致两次查询结果不一致
在一个事务的多次查询之间, 允许另一个事务进行更新操作
脏读
事务1更新了记录,但没有提交,事务2读取了更新后的行,然后事务T1回滚,现在T2读取无效。 通俗的说,脏读就是指在一个事务处理过程中读取了另一个未提交的事务中的数据 , 导致两次查询结果不一致
一个事务在更新未提交时, 允许另一个事务读取
事务隔离级别
读未提交(read uncommitted )
读未提交(read uncommitted ):事务中的修改,即使没有提交,对其它事务也是可见的。当前事务可以读取其他事务未提交的数据,也称之为脏读。这个场景很容易理解,不再赘述。
读已提交(read committed )
读已提交(read committed ):只能读取到其他事务已经提交的数据,可以避免脏读。读已提交不能保证可重复读,也就是说,前后两次读取,会获取到不同的结果集。
可重复读 (repeatable read)
可重复读 (repeatable read):可重复读。在同一个事务内的查询都是事务开始时刻一致的,InnoDB默认级别。会出现幻读的情况,所谓的幻读,就是前后两次读取到的记录行数不一致。事务A在读取某些记录时,事务B又在事务A内插入了一条新记录,当事务A再次读取该范围的记录时,发现前后两次读取的结果集不是完全一致。
串行读(Serializable)
串行读(Serializable):是最高的隔离级别,通过强制的事务串行执行,避免了前面说的幻读问题。简单的来说,Serializable会在读取的每张表上加锁,所以可能导致大量的超时和锁竞争的问题。实际应用中很少用到这一级别。
Mysql默认隔离级别:REPEATABLE-READ
Oracle默认隔离级别:Read committed
Spring隔离级别可选值
隔离级别 | 说明 |
---|---|
ISOLATION_DEFAULT(默认级别) | 这是一个PlatfromTransactionManager默认的隔离级别,使用数据库默认的事务隔离级别. |
ISOLATION_READ_UNCOMMITTED(读未提交) | 这是事务最低的隔离级别,它允许另外一个事务可以看到这个事务未提交的数据。 这种隔离级别会产生脏读,不可重复读和幻像读。 |
ISOLATION_READ_COMMITTED(读已提交) | 保证一个事务修改的数据提交后才能被另外一个事务读取。另外一个事务不能读取该事务未提交的数据。 这种事务隔离级别可以避免脏读出现,但是可能会出现不可重复读和幻像读(因为在事务提交前允许别的事务更新和插入操作)。 |
ISOLATION_REPEATABLE_READ(可重复读) | 它保证了一个事务不能修改已经由另一个事务读取但还未提交的数据,也就是说同一个事务内读取的数据都是一致的。这种事务隔离级别可以防止脏读、不可重复读。但是可能出现幻像读(一个事务在提交前不允许别的事务更新,但是允许插入)。 |
ISOLATION_SERIALIZABLE(串行化) | 这是花费最高代价但是最可靠的事务隔离级别。事务被处理为顺序执行。 除了防止脏读,不可重复读外,还避免了幻像读(一个事务在提交前不允许别的事务读取、更新、插入)。 |
spring如何设置隔离级别
注解
@Transactional(
isolation = Isolation.REPEATABLE_READ,
propagation = Propagation.REQUIRED,
timeout = 5000,
readOnly = true,
rollbackFor = Exception.class,
noRollbackFor = NullPointerException.class)
示例代码:
@RequestMapping("/transfer")
public Object transfer(Integer fromId, Integer toId, Double money){
//接收数据,传递数据到业务层
boolean flag = accountService.transfer(fromId, toId, money);
//返回结果给客户端
return flag;
}
@RequestMapping("/findById")
public void findAccountById(Integer id){
accountService.query(id);
}
public interface AccountService {
public boolean transfer(Integer fromId, Integer toId, double money);
public void query(Integer id);
}
package com.hl.springboot4.service;
import com.hl.springboot4.dao.AccountDao;
import com.hl.springboot4.pojo.Account;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
/*
@EnableTransactionManagement 开启全局事务支持
@Transactional 使用事务
readOnly = true, 只读事务 当前事务中查询出的数据,不能用户更新操作
rollbackFor = Exception.class 当遇到特定异常,执行rollback回滚
noRollbackFor = {} 当发生某种异常,不回滚
timeout=数值 超时时间
propagation = 事务的传播机制 a--调用-->b b是否能够使用事务的问题
Propagation.REQUIRED
isolation = 事务的隔离行为 两个方法在使用事务时,对方操作的数据是否可见
*/
@Service
@Transactional(isolation = Isolation.SERIALIZABLE)
public class AccountServiceImpl implements AccountService{
@Autowired
private AccountDao accountDao;
@Override
// @Transactional//事务注解
public boolean transfer(Integer fromId, Integer toId, double money) {
//转出方法
int num1 = accountDao.transferOut(fromId,money);
// System.out.println(1/0);
//转入方法
int num2 = accountDao.transferIn(toId,money);
return num1==num2&&num1>0 ? true : false;
}
/*
事务隔离机制:
isolation = Isolation.READ_UNCOMMITTED
READ_UNCOMMITTED 读未提交 读取到其他事务修改但是尚未提交的数据 可能出现脏数据
READ_COMMITTED 读已提交 读取其他事务已经提交的数据
REPEATABLE_READ 可重复读 一个事务在执行期间,不受其他会话影响,多次读取结果保持一致
SERIALIZABLE 可串行化 锁表 第一个事务commit提交后,第二个事务才能执行
*/
@Override
public void query(Integer id) {
Account account = accountDao.getAccountById(id);
System.out.println(account);
Account account2 = accountDao.getAccountById(id);
System.out.println(account);
System.out.println(account2);
}
}
package com.hl.springboot4.dao;
import com.hl.springboot4.pojo.Account;
public interface AccountDao {
//转出
public int transferOut(Integer fromId,Double money);
//转入
public int transferIn(Integer toId,Double money);
//查询
public Account getAccountById(Integer id);
}
package com.hl.springboot4.dao;
import com.hl.springboot4.pojo.Account;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;
import org.springframework.web.bind.annotation.RequestMapping;
import java.util.List;
@Repository
public class AccountDaoImpl implements AccountDao{
@Autowired
private JdbcTemplate jdbcTemplate;
@Override
public int transferOut(Integer fromId, Double money) {
String sql = "update account set money = money - ? where id = ?";
return jdbcTemplate.update(sql, money, fromId);
}
@Override
public int transferIn(Integer toId, Double money) {
String sql = "update account set money = money + ? where id = ?";
return jdbcTemplate.update(sql, money, toId);
}
@Override
public Account getAccountById(Integer id) {
String sql = "select * from account where id = ?";
List<Account> list = jdbcTemplate.query(sql,new BeanPropertyRowMapper<Account>(Account.class),id);
return list!=null ? list.get(0) : null;
}
}
@SpringBootTestjava
class Springboot4ApplicationTests {
@Autowired
private AccountController accountController;
@Test
void tes1() {
accountController.findAccountById(1);
}
@Test
void tes2() {
accountController.transfer(1,2,300.0);
}
}
Mybatis
什么是 MyBatis?
MyBatis 是一款优秀的持久层框架,它支持自定义 SQL、存储过程以及高级映射。MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。
框架:半成品,填充代码
支持自定义sql: 可以写sql语句
ORM思想
映射: 对象关系映射 ORM Object-Relational-Mapping 解决了java类和mysql数据库表的映射问题。
Hibernate
SSH框架 :Spring、Struts2、HIbernate
全自动的ORM框架(不需要写sql)
HIbernateUtil.query(User.class); 查询表中所有数据
HIbernateUtil.query(user); 查询当前对象相关的数据
@TableName("user")
class User{
@Column("id");
private Integer id;
@One
private Role role;
@Many
private List<Account> list;
}
开发效率高,运行效率低。
Mybatis(ibatis)
SSM框架:Spring、Springmvc、Mybatis
半自动的ORM框架(需要写sql语句,但是不需要手动的给参数赋值以及手动的封装结果集)
持久层技术
JDBC HIbernate Mybatis
底层 框架 框架
运行效率最高 最低 中间
开发效率最低 最高 中间
mybatis操作流程
1、引入jar包
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.17</version>
</dependency>
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
2、mybatis核心配置文件
数据库环境配置
sqlmapper文件位置
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"https://mybatis.org/dtd/mybatis-3-config.dtd">
<!--配置-->
<configuration>
<!--引入外部属性配置文件-->
<properties resource="db.properties"></properties>
<!--数据库连接环境配置 当前使用的默认环境-->
<environments default="development">
<!--单独的环境 id="环境名 唯一标识"-->
<environment id="development">
<!--事务管理 type="jdbc" 使用jdbc的事务管理机制-->
<transactionManager type="JDBC"/>
<!--数据源配置 type="POOLED|UNPOOLED" 使用连接池|不使用连接池-->
<dataSource type="POOLED">
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
</environment>
<!--多个环境-->
<environment id="test">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
</environment>
</environments>
<!--映射器 指定sqlMapper文件位置-->
<mappers>
<mapper resource="mapper/AccountMapper.xml"/>
</mappers>
</configuration>
3、java文件和sql文件相分离
sqlMapper映射文件
@Data
public class Account {
private int id;
private String name;
private Double money;
}
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--命名空间 唯一标识-->
<mapper namespace="com.hl.mybatis01.mapper">
<!--id=“sql语句唯一标识” resultType="返回结果集类型"-->
<select id="findAccount" resultType="com.hl.mybatis01.pojo.Account">
select * from account where id = #{id}
</select>
</mapper>
4、mybatis框架核心类,内部执行过程
@Test
void query() throws IOException {
//1、加载mybatis核心配置文件
InputStream in = Resources.getResourceAsStream("mybatis-config.xml");
//2、创建SqlSessionFactory
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
//3、基于工厂获取会话连接
SqlSession session = factory.openSession();
//4、获取SqlMapper文件
//5、执行sql语句
Account account = session.selectOne("com.hl.mybatis01.mapper.findAccount", 1);
System.out.println(account);
//6、关闭流程
session.close();
}
mybatis面向接口开发
@Data
public class Account {
private int id;
private String name;
private Double money;
}
package com.hl.mybatis01.mapper;
import com.hl.mybatis01.pojo.Account;
public interface AccountMapper {
Account findAccount(int id);
}
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--命名空间 唯一标识 命名空间值=接口的全路径 -->
<mapper namespace="com.hl.mybatis01.mapper.AccountMapper">
<!--id=“sql语句唯一标识 等与 方法名” resultType="返回结果集类型 和方法声明的返回值类型保持一致 "-->
<select id="findAccount"
resultType="com.hl.mybatis01.pojo.Account">
select * from account where id = #{id}
</select>
</mapper>
搭建环境
1、引入jar包
mysql.jar
mybatis.jar
2、补全结构
com.hl.pojo
com.hl.mapper
mybatis-config.xml
sqlMapper.xml
3、测试类
加载myabtis核心配置文件,得到IO流 Resources
创建sqlSessionFactory SqlsessionFactoryBuilder
SqlSession session = sqlSessionFactory.openSession()
//获取mapper接口
调用mapper方法
//关闭流
核心配置文件
<Configuration>
//1、
<properties resource="db.properties"></properties>
//2、
<environments default="test">
<environment>
<transactionManager type="jdbc"/>
<dataSource type="POOLED">
<property name="url" value="" ></property>
username
password
driver
</dataSource>
</environment>
</environments>
//3、
<mappers>
<mapper resource="com/hl/mybatis01/mapper/AccountMapper.xml"/>
</mappers>
</Configuration>
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"https://mybatis.org/dtd/mybatis-3-config.dtd">
<!--配置-->
<configuration>
<!--引入外部配置文件-->
<properties resource="db.properties"></properties>
<!--设置-->
<settings>
<!--下划线到驼峰式命名法自动映射-->
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
<!--类型起别名-->
<typeAliases>
<!--type="类型" alias="别名"-->
<!--针对每一个javabean分别起别名-->
<!-- <typeAlias type="com.hl.mybatis02.pojo.Student" alias="student"></typeAlias>-->
<!--统一起别名 别名默认为类名 不区分大小写-->
<package name="com.hl.mybatis02.pojo" />
</typeAliases>
<!--数据库环境-->
<environments default="dev">
<environment id="dev">
<transactionManager type="jdbc"></transactionManager>
<dataSource type="POOLED">
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
</environment>
</environments>
<!--mappers映射器-->
<mappers>
<!--sqlMapper文件位置-->
<!-- <mapper resource="com/hl/mybatis02/mapper/GoodsMapper.xml"></mapper>-->
<!--指向mapper接口位置-->
<!-- <mapper class="com.hl.mybatis02.mapper.GoodsMapper"></mapper>-->
<!-- <mapper class="com.hl.mybatis02.mapper.ItemMapper"></mapper>-->
<!--指向mapper包的位置,统一扫描该包下所有接口-->
<package name="com.hl.mybatis02.mapper"/>
</mappers>
</configuration>
核心类 (测试类)
package com.hl.mybatis02;
import com.hl.mybatis02.mapper.StudentMapper;
import com.hl.mybatis02.pojo.Student;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
class Mybatis02ApplicationTests {
@Test
void contextLoads() throws IOException {
InputStream in = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
SqlSession session = factory.openSession();
StudentMapper studentMapper = session.getMapper(StudentMapper.class);
Student student = new Student(0,"admin",
"男","2020-1-5",1001,
null,null,null);
int num = studentMapper.insertStudent(student);
System.out.println("受影响的数据行数:"+num);
System.out.println("自增主键值为:"+student.getStudentId());
session.commit();
session.close();
}
@Test
void query() throws IOException {
InputStream in = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
SqlSession session = factory.openSession();
StudentMapper studentMapper = session.getMapper(StudentMapper.class);
List<Student> list = studentMapper.listAll();
System.out.println(list);
session.commit();
session.close();
}
}
缓存机制
缓存:多次执行相同dql,第一次走数据库表查询,第二次不再执行sql语句,直接从缓存区中获取数据。
一级缓存:
默认已开启,可以直接使用,属于sqlSession级别的缓存。
二级缓存
默认没有开启,需要手动在mybatis配置文件中开启,属于sqlSessionFactory级别的缓存。
<!--开启当前命名空间的二级缓存-->
<cache></cache>
清理缓存
java代码清理缓存
session.clearCache();
sqlMapper文件清理缓存
<!-- //查询 下划线到驼峰式命名法的映射-->
<!-- public List<Student> listAll();
useCache="false" 禁用当前sql语句的二级缓存
flushCache="true" 清空缓存区数据 导致一级缓存和二级缓存失效
-->
<select id="listAll" resultType="student" useCache="false">
select * from student
</select>
示例:
@Test
void firstCache() throws IOException {
InputStream in = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
SqlSession session = factory.openSession();
StudentMapper studentMapper = session.getMapper(StudentMapper.class);
List<Student> list = studentMapper.listAll();
System.out.println(list);
//清理缓存
// session.clearCache();
List<Student> list2 = studentMapper.listAll();
System.out.println(list2);
session.commit();
session.close();
}
@Test
void secondCache() throws IOException {
InputStream in = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
SqlSession session = factory.openSession();
StudentMapper studentMapper = session.getMapper(StudentMapper.class);
List<Student> list = studentMapper.listAll();
System.out.println(list);
session.close();
SqlSession session2 = factory.openSession();
StudentMapper studentMapper2 = session2.getMapper(StudentMapper.class);
List<Student> list2 = studentMapper2.listAll();
System.out.println(list2);
session2.close();
SqlSession session3 = factory.openSession();
StudentMapper studentMapper3 = session3.getMapper(StudentMapper.class);
List<Student> list3 = studentMapper3.listAll();
System.out.println(list3);
session3.close();
}
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--命名空间 唯一标识-->
<mapper namespace="com.hl.mybatis02.mapper.StudentMapper">
<!--开启当前命名空间的二级缓存-->
<cache></cache>
sqlMapper文件
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--命名空间 唯一标识-->
<mapper namespace="com.hl.mybatis02.mapper.StudentMapper">
<!-- 测试自增主键的生成
useGeneratedKeys="true" 获取自动生成的主键
keyProperty="studentId" 主键对应的属性名
-->
<!-- public int insertStudent(Student student);-->
<insert id="insertStudent" useGeneratedKeys="true" keyProperty="studentId">
insert into student(name,gender,birth_date,class_id,enrollment_date)
values (#{name},#{gender},#{birthDate},#{classId},now())
</insert>
<!-- //查询 下划线到驼峰式命名法的映射-->
<!-- public List<Student> listAll(); -->
<select id="listAll" resultType="student">
select * from student
</select>
</mapper>
单参数传递和多参数
//测试自增主键的生成
public int insertStudent(@Param("stu") Student student);
//查询 下划线到驼峰式命名法的映射
public List<Student> listAll();
//多个参数
public List<Student> findStudents(@Param("name") String name,
@Param("phone") String phone);
//单个参数
public Student findStudentById(@Param("id") int id);
<!--命名空间 唯一标识-->
<mapper namespace="com.hl.mybatis02.mapper.StudentMapper">
<!-- 测试自增主键的生成
useGeneratedKeys="true" 获取自动生成的主键
keyProperty="studentId" 主键对应的属性名
-->
<!-- public int insertStudent(Student student);-->
<insert id="insertStudent" useGeneratedKeys="true" keyProperty="studentId">
insert into student(name,gender,birth_date,class_id,enrollment_date)
values (#{stu.name},#{stu.gender},#{stu.birthDate},#{stu.classId},now())
</insert>
<!-- //查询 下划线到驼峰式命名法的映射-->
<!-- public List<Student> listAll(); -->
<select id="listAll" resultType="student">
select * from student
</select>
<!-- //多个参数
public List<Student> findStudents(String name,String phone);
-->
<select id="findStudents" resultType="Student">
select * from student where name=#{name} and phone=#{phone}
</select>
<!--public Student findStudentById(int id);-->
<select id="findStudentById" resultType="Student">
select * from student where student_id=#{id}
</select>
</mapper>
mybatis中Statement和PreparedStatement
statementType="PREPARED" 默认 使用预编译 STATEMENT sql字符串拼接 CALLABLE 调用存储过程
<!-- //多个参数
public List<Student> findStudents(String name,String phone);
statementType="PREPARED" 默认 使用预编译
STATEMENT sql字符串拼接
CALLABLE 调用存储过程
-->
<select id="findStudents" resultType="Student">
select * from student where name like concat('%',#{name},'%') or phone=#{phone}
</select>
<!--public Student findStudentById(int id);-->
<select id="findStudentById" resultType="Student" statementType="STATEMENT">
select * from student where student_id=${id}
</select>
springboot整合mybatis
1、接收请求
2、接收数据
3、事务
4、(创建SqlSessionFactory,SqlSession,UserMapper,)调用方法
配置文件 application.yml application.properties (mybatis-config.xml )
搭建环境
1、引入jar包
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--mybatis 提供的和boot整合的jar包-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.3.0</version>
</dependency>
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
2、配置文件
server:
port: 8080
spring:
datasource:
url: jdbc:mysql://localhost:3306/yan9
driver-class-name: com.mysql.cj.jdbc.Driver
username: root
password: 123456
mybatis:
configuration:
map-underscore-to-camel-case: true #开启下划线到驼峰式命名法自动映射
type-aliases-package: com.hl.mybatis03.pojo #类型起别名
mapper-locations: classpath:mappers/*.xml
logging:
level:
com.hl.mybatis03.mapper: debug
3、准备控制层、业务层、持久层
@RestController
@RequestMapping("/grade")
public class GradeController {
@Autowired
private GradeService gradeService;
//搜索 动态查询
@RequestMapping("search")
public List<Grade> searchGrade(Grade grade){
return gradeService.searchGrade(grade);
}
// 批量保存
@RequestMapping("saveBatch")
public int saveBatch(List<Grade> grades) {
return gradeService.saveBatch(grades);
}
}
public interface GradeService {
//搜索 动态查询
List<Grade> searchGrade(Grade grade);
// 批量保存
int saveBatch(List<Grade> grades);
}
@Mapper
public interface GradeMapper {
//搜索 动态查询
List<Grade> searchGrade(Grade grade);
// 批量保存
int saveBatch(List<Grade> grades);
}
4、SQLMapper文件
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.hl.mybatis03.mapper.GradeMapper">
<!-- //搜索 动态查询-->
<!-- List<Grade> searchGrade(Grade grade);-->
<select id="searchGrade" resultType="Grade">
select * from grade
<where>
<if test="gradeName != null and gradeName !='' ">
grade_name like concat('%',#{gradeName},'%')
</if>
<if test="address != null and address !='' ">
and address=#{address}
</if>
</where>
</select>
<!-- // 批量保存-->
<!-- int saveBatch(List<Grade> grades);-->
</mapper>
动态sql
<where> 添加where关键字,去除紧邻的 and 或者 or
<if> 动态判断 test=""
<foreach> 循环 collection=“list” item="obj" index="index"
<trim> 代替where和set prefix="where" prefixOverrides="and|or" prefix="set" suffixOverrides=","
<set> 动态更新 去除最后的 ,
<choose> <when> <otherwise> case-when 选择一个条件执行
//搜索 动态查询
@RequestMapping("search")
public List<Grade> searchGrade(Grade grade){
return gradeService.searchGrade(grade);
}
// 批量保存
@RequestMapping("saveBatch")
public int saveBatch(@RequestBody List<Grade> grades) {
return gradeService.saveBatch(grades);
}
@Mapper
public interface GradeMapper {
//搜索 动态查询
List<Grade> searchGrade(Grade grade);
// 批量保存
int saveBatch(@Param("list") List<Grade> grades);
}
<!-- //搜索 动态查询-->
<!-- List<Grade> searchGrade(Grade grade);-->
<select id="searchGrade" resultType="Grade">
select * from grade
<where>
<if test="gradeName != null and gradeName !='' ">
grade_name like concat('%',#{gradeName},'%')
</if>
<if test="address != null and address !='' ">
and address=#{address}
</if>
</where>
</select>
<!-- // 批量保存-->
<!-- int saveBatch(@Param("list")List<Grade> grades);-->
<insert id="saveBatch">
insert into grade(grade_name,address)
values
<foreach collection="list" item="obj" separator=",">
(#{obj.gradeName},#{obj.address})
</foreach>
</insert>
分页
物理分页
每次从数据库表中查询指定条数的数据,返回给前端。
select * from grade limit 10; //第一页
select * from grade limit 11,10; //第二页
逻辑分页
一次性从表中查询所有数据,在客户端分页。
1、引入分页插件
pageHelper-spring-boot-starter.jar
2、使用分页插件
//搜索 动态查询+分页
@RequestMapping("search")
public PageInfo searchGrade(Grade grade,
@RequestParam(defaultValue = "1") Integer pageNum,
@RequestParam(defaultValue = "5") Integer pageSize){
//开启分页插件 只能对紧邻的第一个sql语句进行分页
PageHelper.startPage(pageNum,pageSize);
//调用目标方法
List<Grade> list = gradeService.searchGrade(grade);
//封装分页对象
PageInfo pageInfo = new PageInfo(list);
return pageInfo;
}
http://localhost:8080/grade/search?pageNum=2&pageSize=3
事务
编程式事务
connnect.setAutoCommit(false);
connnect.startTransaction();
dml;
connection.commit();|| rollback();
声明式事务
@Transactional
在对应的类上或者方法上声明,即可使用事务。
多表关联映射
结果集映射:
resultType :自动结果集映射 列名和属性名自动映射(相同或者满足驼峰式命名法)。
一般应用于单表操作。
resultMap: 手动结果集映射 手动的一个个指定列名和属性名的映射关系。
一般用于多表关联映射。
<!--结果集手动映射-->
<!--手动指令列名和属性名的映射关系 type="java中的数据类型,目标数据类型"-->
<resultMap id="baseMap" type="Grade">
<!--主键字段映射-->
<id property="id" column="id"></id>
<!--非主键字段-->
<result property="gradeName" column="grade_name"></result>
<result property="address" column="address"></result>
</resultMap>
<select id="searchGrade" resultMap="baseMap">
select * from grade
<trim prefix="where" prefixOverrides="and |or">
<if test="gradeName != null and gradeName !='' ">
grade_name like concat('%',#{gradeName},'%')
</if>
<if test="address != null and address !='' ">
and address=#{address}
</if>
</trim>
</select>
一对多映射 collection
select grade.*,student.*
from grade join student
on grade.id = student.class_id
Grade{
//一对多
List<Student> list;
}
Student{
//一对一
Grade grade;
}
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Student {
private Integer studentId;
private String name;
private Integer classId;
}
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Grade {
private int id;
private String gradeName;
private String address;
//一对多关联
private List<Student> list;
}
//一对多映射
public List<Grade> query();
<!--
//一对多映射
public List<Grade> query();
-->
<select id="query" resultMap="map2">
select grade.id,grade.grade_name,grade.address,
student.student_id,student.name,student.class_id
from grade join student
on grade.id = student.class_id
</select>
<resultMap id="map2" type="Grade">
<!--手动指定列名和属性名的映射关系-->
<!--主键-->
<id property="id" column="id"></id>
<!--非主键 普通属性-->
<result property="gradeName" column="grade_name"></result>
<result property="address" column="address"></result>
<!--集合属性 一对多映射 -->
<collection property="list" ofType="Student" autoMapping="true">
<id column="student_id" property="studentId"></id>
</collection>
</resultMap>
一对一 association
多表关联
1、数据库表结构
create table king(
id int primary key auto_increment,
name char(32)
);
create table queen(
id int primary key auto_increment,
name char(32),
k_id int
);
create table consort(
id int primary key auto_increment,
name char(32),
k_id int
);
insert into king(name) values ('拉玛十世'),('乾隆');
insert into queen(name,k_id) values ('苏提达',1),('富察氏',2);
insert into consort(name,k_id) values
('诗妮娜1号',1),('诗妮娜2号',1),('令妃',2),('香妃',2);
select * from queen;
select * from consort;
2、javaBean类
@Data
public class King {
private Integer id;
private String name;
//一对一映射
private Queen queen;//王后对象
//一对多映射
private List<Consort> list;//妃子集合
}
3、mapper接口
@Mapper
public interface KingMapper {
public List<King> getKings();
}
4、sqlMapper文件
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.hl.mybatis03.mapper.KingMapper">
<!-- public List<King> getKings();-->
<select id="getKings" resultMap="map1">
select king.*,
queen.id as qid,queen.name as qname,queen.k_id,
c.id as cid,c.name as cname,c.k_id as ck_id
from king
join queen on king.id = queen.k_id
join consort c on king.id = c.k_id
</select>
<!--type="预期的最终返回值类型" autoMapping="true"开启结果集自动映射-->
<resultMap id="map1" type="King" autoMapping="true">
<id property="id" column="id"></id>
<!--手动结果集映射-->
<!-- <result property="name" column="name"></result>-->
<!--一对一映射-->
<association property="queen" javaType="Queen">
<!--给Queen类的属性赋值-->
<id property="id" column="qid"></id>
<result property="name" column="qname"></result>
<result property="kId" column="k_id"></result>
</association>
<!--一对多映射 autoMapping="true"自动映射 columnPrefix="c" 针对列名添加前缀-->
<collection property="list" ofType="Consort" autoMapping="true" columnPrefix="c">
<id property="id" column="id"></id>
<!-- <id property="id" column="cid"></id>-->
<!-- <result property="name" column="cname"></result>-->
<!-- <result property="kId" column="ck_id"></result>-->
</collection>
</resultMap>
</mapper>
5、测试
延迟加载
多张表相关联
1、解决什么问题
延迟加载主要用于解决嵌套查询的效率问题。
只针对嵌套查询。
2、嵌套查询
一个查询调用另一个查询。
3、延迟加载
当我们不需要另一个查询时,该查询先不执行。
当我们需要另一个查询的数据时,再执行该查询。
需要??:当我们访问这个关联属性时,进行查询;不访问关联属性时,不执行查询。
select="sql语句唯一标识" 嵌套查询
fetchType="eager" 立即加载
fetchType="lazy" 延迟加载
@Data
@JsonIgnoreProperties({"handler", "hibernateLazyInitializer"})
public class Queen implements Serializable {
private Integer id;
private String name;
private Integer kId;//国王id
//一对一
private King king;
//间接 一对多
private List<Consort> list;
}
@Mapper
public interface QueenMapper {
public List<Queen> getQueens();
}
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.hl.mybatis03.mapper.QueenMapper">
<!--public List<Queen> getQueens();-->
<!--查询王后信息-->
<select id="getQueens" resultMap="map1">
select * from queen
</select>
<!-- fetchType="eager" 数据的抓取策略:
eager立即加载
lazy延迟加载 : 使用数据时查询(比如return返回时json序列化,或者debug模式看数据内容)
-->
<resultMap id="map1" type="Queen" autoMapping="true">
<id property="id" column="id"></id>
<association property="king" javaType="King"
fetchType="lazy"
select="findKing" column="k_id"></association>
<collection property="list" ofType="Consort"
fetchType="lazy"
select="selectConsort" column="k_id"></collection>
</resultMap>
<!--根据王后表中的国王id,找到国王信息-->
<select id="findKing" resultType="King">
select * from king where id = #{id}
</select>
<!--根据国王id,找到妃子集合-->
<select id="selectConsort" resultType="Consort">
select * from consort where k_id = #{kId}
</select>
</mapper>
逆向工程MybatisX插件
1、下载插件
下载和安装 MybatisX 的地址和方法:
1. 通过 JetBrains 插件市场下载(推荐)
在 IntelliJ IDEA 中直接安装:
打开 IDEA,进入
File
→Settings
→Plugins
。搜索
MybatisX
,点击Install
安装。重启 IDEA 生效。
JetBrains 插件市场地址: MybatisX on JetBrains Marketplace
2. 手动下载(备用)
如果无法通过 IDEA 直接安装,可以从 JetBrains 插件市场下载
.jar
文件:访问上述链接,点击
Download
获取最新版本的.jar
。在 IDEA 的
Plugins
界面选择Install Plugin from Disk
,上传下载的.jar
文件。
SQL注解
@Insert
@Options
@Update
@Delete
@Select
@Results
@Result
@One
@Many
package com.hl.mybatis03.mapper;
import com.hl.mybatis03.pojo.Consort;
import com.hl.mybatis03.pojo.King;
import org.apache.ibatis.annotations.*;
import java.util.List;
@Mapper
public interface ConsortMapper {
@Select("select * from consort")
public List<Consort> listAll();
@Insert("insert into consort(name,k_id) values (#{name},#{kId})")
//返回自增主键
@Options(useGeneratedKeys = true,keyProperty = "id",keyColumn = "id")
public int save(Consort consort);
@Update("update consort set name=#{name},k_id=#{kId} where id=#{id}")
public int update(@Param("id") Integer id, @Param("name") String name, @Param("kId") Integer kId);
@Delete("delete from consort where id=#{id}")
public int delete(Integer id);
//关联妃子和王国
@Select("select * from consort")
@Results(value = {
@Result(id = true,property = "id",column = "id"),
@Result(property = "name",column = "name"),
@Result(property = "king",javaType = King.class, column = "k_id",
one=@One(select = "com.hl.mybatis03.mapper.ConsortMapper.getKingById"))
})
public List<Consort> list();
@Select("select * from king where id=#{id}")
public King getKingById(Integer id);
}