一、MyBatis简介
1.1 框架概念
框架,就是软件的半成品,完成了软件开发过程中通用操作,码农只需很少或者不用加工就能实现特定的功能,从而简化开发人员在软件开发中的步骤,提高开发效率。
1.2 常用框架
- MVC框架:简化了Servlet的开发步骤,负责前端交换操作,常用:Struts2、SpringMVC
- 持久层框架:完成数据库操作的框架,简化了JDBC的开发步骤,常用:apache DBUtils、Hibernate、Spring JPA、MyBatis
- 胶水框架:Spring
SSM:Spring、SpringMVC、MyBatis
SSH:Spring、Struts2、Hibernate
1.3 MyBatis介绍
MyBatis是半自动的ORM框架,灵活度高。
ORM(Object Relational Mapping)对象关系映射,将Java的一个对象与数据库表中一行记录一一对应。
ORM框架提供了实体类与数据表的映射关系,通过映射文件的配置,实现对象的持久化。
- MyBatis的前身是iBatis,iBatis是Apache软件基金会提供的一个开源项目,2010年iBatis迁移到Google code,正式更名为MyBatis。
- MyBatis特点:
(1)支持自定义SQL、存储过程;
(2)对原有的JDBC进行了封装,几乎消除了所有JDBC代码,让开发者只关注SQL本身;
(3)支持XML和注解配置方式自动完成ORM操作,实现结果映射(对象属性和数据表列之间);
二、MyBatis框架部署
框架部署,就是将框架引入到我们的项目中。
2.1 创建Maven项目
- Java工程
- Web工程
2.2 在项目中添加MyBatis依赖
(1)在pom.xml中添加依赖
- mybatis
- mysql driver
<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.27</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.6</version>
</dependency>
2.3 创建MyBatis配置文件
- 创建自定义模板,选中File->Settings
<?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中创建名为mybatis-config.xml文件
- 在mybatis-config.xml文件中配置数据库连接信息
<?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>
<!-- 在environments配置数据库连接信息 -->
<!-- 在environments标签中可以定义多个environment标签,每个environment标签可以定义一套连接配置 -->
<!-- default属性,用来指定使用哪个environment标签 -->
<environments default="mysql">
<environment id="mysql">
<!-- transactionManager标签用于配置数据库管理方式 -->
<transactionManager type="JDBC">
</transactionManager>
<!-- dataSource 标签用于配置数据库连接信息 -->
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/crud?characterEncoding=utf-8"/>
<property name="username" value="root"/>
<property name="password" value="1234"/>
</dataSource>
</environment>
</environments>
</configuration>
三、MyBatis框架使用
案例:学生信息的数据库操作
3.1 创建数据库表
create table tb_students(
sid int primary key auto_increment,
stu_num char(5) not null unique,
stu_name varchar(20) not null,
stu_gender char(2) not null,
stu_age int not null
);
3.2 创建实体类
package com.xiaochen.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;
@Data
@AllArgsConstructor
@NoArgsConstructor
@ToString
public class Student {
private int stuId;
private String stuNum;
private String stuName;
private String stuGender;
private int stuAge;
}
3.3 创建DAO接口,定义操作方法
package com.xiaochen.dao;
import com.xiaochen.pojo.Student;
public interface StudentDAO {
public int insertStudent(Student student);
public int deleteStudent(String stuNum);
}
3.4 创建DAO接口的映射文件
- 在resources目录下,新建名为mappers文件夹
- 在mappers中新建名为StudentMapper.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="">
</mapper>
- 在映射文件中对DAO中定义的方法进行实现:
<?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文件相当于DAO接口的实现类,namespace属性指定实现DAO接口的权限定名 -->
<mapper namespace="com.xiaochen.dao.StudentDAO">
<!-- id代表调用哪个方法 -->
<insert id="insertStudent">
insert into tb_students(stu_num,stu_name,stu_gender,stu_age)
values(#{stuNum},#{stuName},#{stuGender},#{stuAge})
</insert>
<delete id="deleteStudent">
delete from tb_students where stu_num=#{stuNum}
</delete>
</mapper>
3.5 将映射文件添加到主配置文件mybatis-config.xml
<?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>
<!-- 在environments配置数据库连接信息 -->
<!-- 在environments标签中可以定义多个environment标签,每个environment标签可以定义一套连接配置 -->
<!-- default属性,用来指定使用哪个environment标签 -->
<environments default="mysql">
<environment id="mysql">
<!-- transactionManager标签用于配置数据库管理方式 -->
<transactionManager type="JDBC">
</transactionManager>
<!-- dataSource 标签用于配置数据库连接信息 -->
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/crud?characterEncoding=utf-8"/>
<property name="username" value="root"/>
<property name="password" value="1234"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="mappers/StudentMapper.xml"/>
</mappers>
</configuration>
四、单元测试
4.1 添加单元测试依赖
<!-- Junit -->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>5.8.1</version>
<scope>test</scope>
</dependency>
4.2 创建单元测试类
在被测试类名后面alt+insert---选择Test
public interface StudentDAO {
public int insertStudent(Student student);
public int deleteStudent(String stuNum);
}
import com.xiaochen.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 java.io.IOException;
import java.io.InputStream;
class StudentDAOTest {
@org.junit.jupiter.api.Test
void insertStudent() {
try{
//加载mybatis配置文件,从流中获取
InputStream is= Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactoryBuilder builder=new SqlSessionFactoryBuilder();
//会话工厂
SqlSessionFactory factory=builder.build(is);
//会话连接
SqlSession sqlSession=factory.openSession();
//通过会话获取DAO对象
StudentDAO studentDAO=sqlSession.getMapper(StudentDAO.class);
//测试StudentDAO的方法
int i=studentDAO.insertStudent(new Student(0,"10001","张三","男",21));
//需要手动提交
sqlSession.commit();
System.out.println(i);
} catch (IOException e){
e.printStackTrace();
}
}
@org.junit.jupiter.api.Test
void deleteStudent() {
}
}
五、MyBatis的增删改查操作
案例:学生信息的增删查改
5.1 添加操作
省略
5.2 删除操作
根据学号删除一个学生信息
- 在StudentDAO定义删除方法
package com.xiaochen.dao;
import com.xiaochen.pojo.Student;
public interface StudentDAO {
public int insertStudent(Student student);
public int deleteStudent(String stuNum);
}
- 在StudentMapper.xml中对接口方法进行"实现"
<delete id="deleteStudent">
delete from tb_students where stu_num=#{stuNum}
</delete>
- 测试:在StudentDAO的测试类中测试方法
@org.junit.jupiter.api.Test
public void deleteStudent() {
try {
// 获取输入流
InputStream read=Resources.getResourceAsStream("mybatis-config.xml");
//SqlSessionFactoryBuilder
SqlSessionFactoryBuilder builder=new SqlSessionFactoryBuilder();
//SqlSessionFactory表示MyBatis的会话工厂
SqlSessionFactory factory=builder.build(read);
//SqlSession表示MyBatis与数据库之间的会话,通过工厂方法设计模式
SqlSession sqlSession=factory.openSession();
//通过SqlSession对象调用getMapper方法获取DAO接口对象
StudentDAO studentDAO = sqlSession.getMapper(StudentDAO.class);
//System.out.println(studentDAO);
int i=studentDAO.deleteStudent("10001");
System.out.println(i);
sqlSession.commit();
}catch (IOException e){
e.printStackTrace();
}
}
5.3 修改操作
public int updateStudent(Student student);
- 在StudentMapper.xml中对接口方法进行"实现"
<update id="updateStudent">
update tb_students set stu_name=#{stuName},stu_gender=#{stuGender},stu_age=#{stuAge}
where stu_num=#{stuNum}
</update>
- 测试:在StudentDAO的测试类中测试方法
public void UpdateStudent() {
try {
// 获取输入流
InputStream read=Resources.getResourceAsStream("mybatis-config.xml");
//SqlSessionFactoryBuilder
SqlSessionFactoryBuilder builder=new SqlSessionFactoryBuilder();
//SqlSessionFactory表示MyBatis的会话工厂
SqlSessionFactory factory=builder.build(read);
//SqlSession表示MyBatis与数据库之间的会话,通过工厂方法设计模式
SqlSession sqlSession=factory.openSession();
//通过SqlSession对象调用getMapper方法获取DAO接口对象
StudentDAO studentDAO = sqlSession.getMapper(StudentDAO.class);
//System.out.println(studentDAO);
int i=studentDAO.updateStudent(new Student(0,"10001","张三","女",21));
assertEquals(1,i);
sqlSession.commit();
}catch (IOException e){
e.printStackTrace();
}
}
5.4 查询操作-查询所有
public List<Student> listStudents();
- 在StudentMapper.xml中对接口方法进行"实现"
<!-- resultType指定查询结果封装的对象实体类 -->
<!-- resultSets指定当前操作返回的集合类型(可省略) -->
<!-- sid stuId 代表映射关系 -->
<!-- <select id="listStudents" resultType="com.xiaochen.pojo.Student" resultSets="java.util.list">-->
<!-- select sid stuId,stu_num stuNum,stu_name stuName,stu_gender stuGender,stu_age stuAge-->
<!-- from tb_students-->
<!-- </select>-->
<!-- 第二种映射关系,用的比较多 -->
<!-- resultMap标签用于定义实体类与数据表的映射关系(ORM)-->
<resultMap id="studentMap" type="com.xiaochen.pojo.Student">
<id column="sid" property="stuId"/>
<result column="stu_num" property="stuNum"/>
<result column="stu_name" property="stuName"/>
<result column="stu_gender" property="stuGender"/>
<result column="stu_age" property="stuAge"/>
</resultMap>
<!-- resultMap 用于引用一个实体的映射关系,当配置了resultMap之后resultType就可以省略 -->
<select id="listStudents" resultMap="studentMap">
select sid,stu_num,stu_name,stu_gender,stu_age from tb_students
</select>
- 测试:在StudentDAO的测试类中测试方法
public void SelectStudents(){
try {
// 获取输入流
InputStream read=Resources.getResourceAsStream("mybatis-config.xml");
//SqlSessionFactoryBuilder
SqlSessionFactoryBuilder builder=new SqlSessionFactoryBuilder();
//SqlSessionFactory表示MyBatis的会话工厂
SqlSessionFactory factory=builder.build(read);
//SqlSession表示MyBatis与数据库之间的会话,通过工厂方法设计模式
SqlSession sqlSession=factory.openSession();
//通过SqlSession对象调用getMapper方法获取DAO接口对象
StudentDAO studentDAO = sqlSession.getMapper(StudentDAO.class);
List<Student> list=studentDAO.listStudents();
//assertNotNull(list);
for(Student student:list){
System.out.println(student);
}
}catch (IOException e){
e.printStackTrace();
}
}
5.5 查询操作-查询一条记录
根据学号查询一个学生
-
在StudentDAO接口中定义方法
public Student queryStudent(String stuNum);
- 在StudentMapper.xml中对接口方法进行"实现"
<select id="queryStudent" resultMap="studentMap">
select sid,stu_num,stu_name,stu_gender,stu_age from tb_students
where stu_num=#{stuNum}
</select>
- 测试:在StudentDAO的测试类中测试方法
@Test
public void testQueryStudent(){
try {
InputStream read=Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactoryBuilder builder=new SqlSessionFactoryBuilder();
SqlSessionFactory factory=builder.build(read);
SqlSession sqlSession=factory.openSession();
StudentDAO studentDAO = sqlSession.getMapper(StudentDAO.class);
Student student=studentDAO.queryStudent("10001");
System.out.println(student);
}catch (IOException e){
e.printStackTrace();
}
}
5.6 查询操作-多条件查询
分页查询(参数 start,pageSize)
在MyBatis进行查询操作:
- 如果操作方法只有一个简单类型或者字符串类型的参数,在Mapper配置中可以直接通过#{key},key为任意字符串,包括自定义;
- 如果操作方法有一个对象类型的参数,在Mapper配置中可以直接通过#{attrName}获取对象的指定属性值,attrName必须是对象的属性。
- 如果操作方法有一个Map类型的参数,在Mapper配置中可以直接通过#{key}对应的value
- 在StudentMapper.xml中对接口方法进行"实现"
<select id="listStudentsByPage" resultMap="studentMap">
select sid,stu_num,stu_name,stu_gender,stu_age from tb_students
limit #{start},#{pageSize}
</select>
比如:
public List<Student> listStudentsByPage(HashMap<String,Integer> map);
- 分页查询通过map类型
@Test
public void testListStudentByPage(){
try {
InputStream read=Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactoryBuilder builder=new SqlSessionFactoryBuilder();
SqlSessionFactory factory=builder.build(read);
SqlSession sqlSession=factory.openSession();
StudentDAO studentDAO = sqlSession.getMapper(StudentDAO.class);
HashMap<String,Integer> map=new HashMap<String,Integer>();
map.put("start",1);
map.put("pageSize",3);
List<Student> Students=studentDAO.listStudentsByPage(map);
for(Student student:Students) {
System.out.println(student);
}
}catch (IOException e){
e.printStackTrace();
}
}
通常情况下,采用参数的方式,
- 在StudentDAO中定义操作方法,如果方法有多个参数,使用@param注解声明参数的别名,
public List<Student> listStudentsByPage(@Param("start") int start,@Param("pageSize") int pageSize);
- 在StudentMapper.xml配置sql时,使用#{别名}获取指定的参数
<select id="listStudentsByPage" resultMap="studentMap">
select sid,stu_num,stu_name,stu_gender,stu_age from tb_students
limit #{start},#{pageSize}
</select>
<!-- limit #{arg0},#{arg1}或者limit #{param},#{param} -->
注意:如果DAO操作方法没有通过@param指定参数别名,在SQL中也可以通过arg0,arg1...或者param1,param2...获取参数
5.7 查询操作-查询总记录数
- 定义方法
public int getCount();
- 配置文件,通过resultType指定当前操作的返回类型为int,
<select id="getCount" resultType="int">
select count(1) from tb_students
</select>
- 单元测试
@Test
public void testGetCount(){
try {
InputStream read=Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactoryBuilder builder=new SqlSessionFactoryBuilder();
SqlSessionFactory factory=builder.build(read);
SqlSession sqlSession=factory.openSession();
StudentDAO studentDAO = sqlSession.getMapper(StudentDAO.class);
int count=studentDAO.getCount();
System.out.println(count);
}catch (IOException e){
e.printStackTrace();
}
}
5.8 添加操作回填生成的主键
- 在StudengMapper.xml的添加操作标签-insert
<!-- useGeneratedKeys设置添加操作是否需要回填生成的主键 -->
<!-- keyProperty设置回填的主键值赋值到参数对象的哪个属性 -->
<insert id="insertStudent" useGeneratedKeys="true" keyProperty="stuId">
insert into tb_students(stu_num,stu_name,stu_gender,stu_age)
values(#{stuNum},#{stuName},#{stuGender},#{stuAge})
</insert>
六、MyBatis工具类封装
package com.xiaochen.utils;
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 java.io.IOException;
import java.io.InputStream;
/*
* @ClassName: MyBatisUtil
* @Author: XiaoChen
* @Date: 2021/12/15 星期三 19:40
*/
public class MyBatisUtil {
private static SqlSessionFactory factory;
private static final ThreadLocal<SqlSession> local=new ThreadLocal<SqlSession>();
static {//单例模式
try {
InputStream read= Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactoryBuilder builder=new SqlSessionFactoryBuilder();
factory=builder.build(read);
}catch (IOException e){
e.printStackTrace();
}
}
public static SqlSession getSqlSession() {
//加载mybatis配置文件
SqlSession sqlSession=local.get();
if(sqlSession==null){
sqlSession=factory.openSession();
local.set(sqlSession);
}
return sqlSession;
}
public static <T extends Object> T getMapper(Class<T> c){
SqlSession sqlSession=getSqlSession();
return sqlSession.getMapper(c);
}
}
七、事务管理
SqlSession对象
- getMapper(DAO.class):获取Mapper(DAO接口的实例)
- 事务管理
7.1 手动提交事务
- sqlSession.commit( ):提交事务
- sqlSession.rollback():事务回滚
@Test
public void insertStudent() {
SqlSession sqlSession=MyBatisUtil.getSqlSession();
//1.当获取sqlSession对象时,就默认开启了事务
try {
StudentDAO studentDAO = sqlSession.getMapper(StudentDAO.class);
//测试StudentDAO的方法
Student student = new Student(0, "10007", "小华", "男", 21);
int i = studentDAO.insertStudent(student);
//需要手动提交
sqlSession.commit();
System.out.println(student);
}catch (Exception e){
//当操作出现异常,调用rollback进行回滚
sqlSession.rollback();
}
}
7.2 自动提交事务
通过SqlSessionFactory调用openSession方法获取SqlSession对象时,可以设置参数是否自动提交。如果参数设置为true,表示自动提交事务;设置为false或者不设置,表示手动提交。
sqlSession=factory.openSession(true);
public static SqlSession getSqlSession(boolean isAutoCommit) {
//加载mybatis配置文件
SqlSession sqlSession=local.get();
if(sqlSession==null){
//通过SqlSessionFactory调用openSession方法获取SqlSession对象时,可以设置参数是否自动提交
sqlSession=factory.openSession(isAutoCommit);
local.set(sqlSession);
}
return sqlSession;
}
public static <T extends Object> T getMapper(Class<T> c){
SqlSession sqlSession=getSqlSession(true);
return sqlSession.getMapper(c);
}
MyBatisUtil代码优化
package com.xiaochen.utils;
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 java.io.IOException;
import java.io.InputStream;
/*
* @ClassName: MyBatisUtil
* @Author: XiaoChen
* @Date: 2021/12/15 星期三 19:40
*/
public class MyBatisUtil {
private static SqlSessionFactory factory;
private static final ThreadLocal<SqlSession> local=new ThreadLocal<SqlSession>();
static {//单例模式
try {
InputStream read= Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactoryBuilder builder=new SqlSessionFactoryBuilder();
factory=builder.build(read);
}catch (IOException e){
e.printStackTrace();
}
}
private static SqlSession getSqlSession(boolean isAutoCommit) {
//加载mybatis配置文件
SqlSession sqlSession=local.get();
if(sqlSession==null){
//通过SqlSessionFactory调用openSession方法获取SqlSession对象时,可以设置参数是否自动提交
sqlSession=factory.openSession(isAutoCommit);
local.set(sqlSession);
}
return sqlSession;
}
//手动事务提交
public static SqlSession getSqlSession(){
return getSqlSession(false);
}
//自动事务提交
public static <T extends Object> T getMapper(Class<T> c){
SqlSession sqlSession=getSqlSession(true);
return sqlSession.getMapper(c);
}
}
八、MyBatis主配置文件
mybatis-config.xml是MyBatis框架的主配置文件,只要用于配置MyBatis数据源及属性
8.1 properties
用于设置键值对,或者加载属性文件
- 在resources目录下创建jdbc.properties文件,配置键值对的如下:
mysql_driver=com.mysql.jdbc.Driver
mysql_url=jdbc:mysql://localhost:3306/crud?characterEncoding=utf-8
mysql_username=root
mysql_password=1234
- 在mybatis-config.xml中通过properties标签引用jdbc.properties文件,引入之后,在配置enviroment时可以直接使用jdbc.properties的key获取对应的value。
8.2 settings标签
<!-- 设置mybatis属性 -->
<settings>
<!-- 启动二级缓存 -->
<setting name="cacheEnabled" value="true"/>
<!-- 启动延迟加载-->
<setting name="lazyLoadingEnabled" value="true"/>
</settings>
8.3 typeAliases标签
<!-- typeAliases标签用于给实体类取别名,在映射文件中可以直接使用别名来替代实体类的全限定名 -->
<typeAliases>
<typeAlias type="com.xiaochen.pojo.Student" alias="Student"> </typeAlias>
<typeAlias type="com.xiaochen.pojo.Book" alias="Book"> </typeAlias>
</typeAliases>
8.4 plugins标签
<!-- plugins标签用于配置MyBatis插件(例如分页插件) -->
<plugins>
<plugin interceptor=""></plugin>
</plugins>
8.5 environments标签
<!-- 在environments配置数据库连接信息 -->
<!-- 在environments标签中可以定义多个environment标签,每个environment标签可以定义一套连接配置 -->
<!-- default属性,用来指定使用哪个environment标签 -->
<environments default="mysql">
<!-- environment标签用于配置数据库连接信息 -->
<environment id="mysql">
<!-- transactionManager标签用于配置数据库管理方式
type="JDBC" 可以进行事务的提交和回滚操作
type="MANAGED" 依赖容器完成事务管理,本身不进行事务的提交和回滚操作
-->
<transactionManager type="JDBC"> </transactionManager>
<!-- dataSource 标签用于配置数据库连接信息 -->
<dataSource type="POOLED">
<property name="driver" value="${mysql_driver}"/>
<property name="url" value="${mysql_url}"/>
<property name="username" value="${mysql_username}"/>
<property name="password" value="${mysql_password}"/>
</dataSource>
</environment>
</environments>
8.6 mappers标签
加载映射配置(映射文件、DAO注解)
<!-- mappers标签用于载入映射文件 -->
<mappers>
<mapper resource="mappers/StudentMapper.xml"/>
</mappers>
九、映射文件
9.1 MyBatis Mapper初始化
省略
9.2 mapper根标签
mapper文件相当于DAO接口的实现类,namespace属性要指定指定实现DAO的全限定名
9.3 insert 标签
声明添加操作(sql:insert...)
常用属性:
- id属性,绑定对应DAO接口中的方法
- parameterType属性,用于指定接口中对应方法的参数类型(可省略)
- useGeneratedKeys属性,设置添加操作是否需要回填生成的主键
- keyProperty属性,指定回填的id设置到参数对象中的哪个属性
- timeout属性,设置此操作的超时时间,如果不设置则一直等待
主键回填
<insert id="insertStudent" useGeneratedKeys="true" keyProperty="stuId">
insert into tb_students(stu_num,stu_name,stu_gender,stu_age)
values(#{stuNum},#{stuName},#{stuGender},#{stuAge})
</insert>
9.4 delete标签
声明删除操作
9.5 update标签
声明修改操作
9.6 select标签
声明查询操作
- id属性,指定绑定方法的方法名
- parameterType属性,设置参数类型
- resultType属性,指定当前sql返回数据封装的对象类型(实体类)
- resultMap属性,指定从数据表到实体类的字段和属性的对应关系
- useCache属性,指定此查询操作是否需要缓存
- timeout属性,设置超时时间
9.7 resultMap标签
<!-- 第二种映射关系,用的比较多 -->
<!-- resultMap标签用于定义实体类与数据表的映射关系(ORM)-->
<resultMap id="studentMap" type="com.xiaochen.pojo.Student">
<id column="sid" property="stuId"/>
<result column="stu_num" property="stuNum"/>
<result column="stu_name" property="stuName"/>
<result column="stu_gender" property="stuGender"/>
<result column="stu_age" property="stuAge"/>
</resultMap>
9.8 cache标签
设置当前DAO进行数据库操作时的缓存属性设置
<cache type="" size="" readOnly="false"/>
9.9 sql/include标签
<sql id="wanglao">sid,stu_num,stu_name,stu_gender,stu_age</sql>
<!-- resultMap 用于引用一个实体的映射关系,当配置了resultMap之后resultType就可以省略 -->
<select id="listStudents" resultMap="studentMap">
select <include refid="wanglao"/> from tb_students
</select>
十、分页插件
分页插件是一个独立于MyBatis框架之外的第三方插件
10.1 添加分页插件的依赖
<!-- pagehelper分页插件 -->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>5.2.0</version>
</dependency>
10.2 配置插件
在mybatis的主配置文件mybatis-config.xml中通过plugins标签进行配置
<!-- plugins标签用于配置MyBatis插件(例如分页插件) -->
<plugins>
<plugin interceptor="com.github.pagehelper.PageInterceptor"></plugin>
</plugins>
10.3 分页实例
对学生信息进行分页
@Test
public void testListStudentByPage(){
StudentDAO studentDAO = MyBatisUtil.getMapper(StudentDAO.class);
PageHelper.startPage(1,3);
List<Student> students=studentDAO.listStudents();
//PageInfo包含了数据以及信息
PageInfo<Student> pageInfo=new PageInfo<Student>(students);
List<Student> list=pageInfo.getList();
for(Student student:list) {
System.out.println(student);
}
}
带条件分页
查找女性的信息
public List<Student> listStudentsByGender(String gender);
<select id="listStudentsByGender" resultMap="studentMap">
select <include refid="wanglao"/> from tb_students where stu_gender=#{gender}
</select>
@Test
public void testListStudentByPage(){
StudentDAO studentDAO = MyBatisUtil.getMapper(StudentDAO.class);
PageHelper.startPage(1,3);
List<Student> students=studentDAO.listStudentsByGender("女");
//PageInfo包含了数据以及信息
PageInfo<Student> pageInfo=new PageInfo<Student>(students);
List<Student> list=pageInfo.getList();
for(Student student:list) {
System.out.println(student);
}
}
十一、关联映射
11.1 实体关系
实体--数据实体,实体关系指的是数据与数据之间的关系
例如:用户和角色、房屋和楼栋、订单和商品
实体关系分为以下四种:
(1)一对一关联
- 公民和身份证、学生和学生证
数据表关系:
- 主键关联(用户表主键和详情表主键相同时,表示匹配的数据)
用户基本信息表 | 用户详情表 |
用户ID,账号,密码,姓名,最后登录时间 | 详情ID,手机... |
1.张三 | 1.14432049984 |
2.王五 | 2.17865436578 |
3.李四 | 3.18909876543 |
- 唯一外键关联
用户基本信息表 | 用户详情表 |
用户ID,账号,密码,姓名,最后登录时间 | 详情ID,手机... uid(外键,唯一) |
1.张三 | 1.14432049984 2 |
2.王五 | 2.17865436578 3 |
3.李四 | 3.18909876543 1 |
(2)一对多关联、多对一关联
实例:
- 一对多:班级和学生、类别和商品
- 多对一:学生和班级、商品和类别
数据表关联:
- 在多个一端添加外键和一的一端进行关联
(3)多对多关联
实例:
- 用户和角色、角色和权限、房屋和业主、学生和社团、订单和商品
数据表关系:
- 建立第三张关系表添加两个外键分别与两张表主键进行关联
用户(user_id)、用户角色(uid、rid)、角色(role_id)
11.2 创建项目,部署MyBatis框架
- 创建web项目(maven)
<!-- 添加web依赖 -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jsp-api</artifactId>
<version>2.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
<scope>provided</scope>
</dependency>
- 部署MyBatis框架
(1)添加依赖
<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.6</version>
</dependency>
<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.22</version>
</dependency>
(2)配置文件
(3)帮助类
package com.xcsh.utils;
/*
* @ClassName: MyBatisUtil
* @Author: XiaoChen
* @Date: 2021/12/16 星期四 16:24
*/
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 java.io.IOException;
import java.io.InputStream;
public class MyBatisUtil {
private static SqlSessionFactory factory;
private static final ThreadLocal<SqlSession> local=new ThreadLocal<>();
static {
try {
InputStream is=Resources.getResourceAsStream("mybatis-config.xml");
factory=new SqlSessionFactoryBuilder().build(is);
} catch (IOException e) {
e.printStackTrace();
}
}
public static SqlSessionFactory getSqlSessionFactory(){
return factory;
}
public static SqlSession getSqlSession(boolean isAutoCommit){
SqlSession sqlSession=local.get();
if(sqlSession==null){
sqlSession=factory.openSession(isAutoCommit);
local.set(sqlSession);
}
return sqlSession;
}
public static SqlSession getSqlSession(){
return getSqlSession(false);
}
public static<T extends Object> T getMapper(Class<T> c){
SqlSession sqlSession=getSqlSession(true);
return sqlSession.getMapper(c);
}
}
11.3 一对一关联
实例:用户--详情
(1)创建数据表
--用户信息表
create table users(
user_id int primary key auto_increment,
user_name varchar(20) not null unique,
user_pwd varchar(20) not null,
user_realname varchar(20) not null,
user_img varchar(100) not null
);
--用户详情表
create table details(
detail_id int primary key auto_increment,
user_addr varchar(50) not null,
user_tel char(11) not null,
user_desc varchar(200),
uid int not null unique
);
(2)创建实体类
- User
@Data
@NoArgsConstructor
@AllArgsConstructor
@ToString
public class User {
private int userId;
private String userName;
private String userPwd;
private String userRealName;
private String userImg;
}
- Detail
@Data
@NoArgsConstructor
@AllArgsConstructor
@ToString
public class Detail {
private int detailId;
private String userAddr;
private String userTel;
private String userDesc;
private int userId;
}
(3)添加操作(事务)
- 测试代码
(4)关联查询
在查询用户的同时关联查询出与之对应的详情
- 实体
User | Detail |
@Data @NoArgsConstructor @AllArgsConstructor @ToString public class User { private int userId; private String userName; private String userPwd; private String userRealName; private String userImg; private Detail detail; } | @Data @NoArgsConstructor @AllArgsConstructor @ToString public class Detail { private int detailId; private String userAddr; private String userTel; private String userDesc; private int userId; } |
映射文件
- 连接查询
- 子查询
11.4 一对多关联
案例:班级-学生
1)创建数据表
-- 创建班级信息表
create table classes(
cid int primary key auto_increment,
cname varchar(30) not null unique,
cdesc varchar(100)
);
-- 创建学生信息表
create table students(
sid char(5) primary key,
sname varchar(20) not null,
sage int not null,
scid int not null
);
2)创建实体类
Clazz | Student |
@Data @NoArgsConstructor @AllArgsConstructor @ToString public class Clazz { private int classId; private String className; private String classDesc; private List<Student> stus; // 存储当前班级下的学生信息 } | @Data @NoArgsConstructor @AllArgsConstructor @ToString public class Student { private String stuId;//学号 private String stuName; private int stuAge; private int stuCid;//学生所在班级的id } |
3)关联查询
当查询一个班级的时候,要关联查询出这个班级下的所有学生
- 连接查询
- 子查询
11.5 多对一关联
实例:学生(n)—班级(1)
当查询一个学生的时候,关联查询这个学生所在的班级信息
1)创建实体类
Student | Clazz |
@Data @NoArgsConstructor @AllArgsConstructor @ToString public class Student { private String stuId;//学号 private String stuName; private int stuAge; private Clazz clazz; } | @Data @NoArgsConstructor @AllArgsConstructor @ToString public class Clazz { private int classId; private String className; private String classDesc; } |
2)关联查询
- 连接查询
<resultMap id="studentMap" type="Student">
<id column="sid" property="stuId" />
<result column="sname" property="stuName" />
<result column="sage" property="stuAge" />
<result column="cid" property="clazz.classId" />
<result column="cname" property="clazz.className" />
<result column="cdesc" property="clazz.classDesc" />
</resultMap>
<select id="queryStudentBySid" resultMap="studentMap">
select sid,sname,sage,scid,cid,cname,cdesc
from students s INNER JOIN classes c
ON s.scid=c.cid
where s.sid=#{sid}
</select>
- 子查询
11.5 多对多关联
案例:学生(m)—课程(n)
1)创建数据表
-- 学生信息表(同上)
-- 课程信息表
create table courses(
course_id int primary key auto_increment,
course_name varchar(50) not null
);
-- 选课信息表/成绩表(学号、课程号、成绩)
create table grades(
sid char(5) not null,
cid int not null,
score int not null
);
2)关联查询
- 查询学生时,同时查询学生选择的课程
Student | Course |
@Data @NoArgsConstructor @AllArgsConstructor @ToString public class Student { private String stuId;//学号 private String stuName; private int stuAge; private List<Course> courses;//学生选择的课程 } | @Data @NoArgsConstructor @AllArgsConstructor @ToString public class Course { private int courseId; private String courseName; } |
- 根据课程编号查询课程时,同时查询选择了这门课程的学生
Student | Course |
@Data @NoArgsConstructor @AllArgsConstructor @ToString public class Student { private String stuId;//学号 private String stuName; private int stuAge; } | @Data @NoArgsConstructor @AllArgsConstructor @ToString public class Course { private int courseId; private String courseName; private List<Student> students; } |
- 连接查询的映射配置
<resultMap id="courseMap" type="Course">
<id column="course_id" property="courseId" />
<result column="course_name" property="courseName" />
<collection property="students" ofType="Student">
<result column="sid" property="stuId" />
<result column="sname" property="stuName" />
<result column="sage" property="stuAge" />
</collection>
</resultMap>
<select id="queryCourseById" resultMap="courseMap">
select course_id,course_name,s.sid,sname,s.sage
from courses c INNER JOIN grades g INNER JOIN students s
ON c.course_id=g.cid and g.sid=s.sid
WHERE c.course_id=#{courseId}
</select>
- 子查询映射配置
<!-- 根据课程id查询选择了这门课的学生信息 -->
<resultMap id="studentMap2" type="Student">
<id column="sid" property="stuId" />
<result column="sname" property="stuName" />
<result column="sage" property="stuAge" />
</resultMap>
<select id="queryStudentsByCourseId" resultMap="studentMap2">
select s.sid,sname,sage from students s INNER JOIN grades g
ON s.sid=g.sid where g.cid=#{courseId};
</select>
十二、动态SQL
交友网:珍爱网、百合网 筛选心仪对象—性别、年龄、城市、身高
- 张三 性别:女 select * from members where gender='女'
- 李四 性别:女 年龄:18-23 select * from members where gender='女' and age>=18 and age<=23
- 王五 年龄 城市 select * from members where age>=18 and age<=23 and city='广州'
电商:淘宝、京东 筛选商品—羽毛球拍、品牌、价格
用户筛选条件不同,我们完成筛选执行的SQL也不一样,可以通过穷举来一一完成不同条件的筛选,但是这种思路过于繁琐和复杂,但mybatis提供了动态sql的配置方式来实现多条件查询。
12.1 什么是动态SQL?
- 根据查询条件动态完成SQL的拼接
12.2 动态SQL使用案例
案例:心仪对象搜索
1)创建数据表
create table members(
member_id int primary key auto_increment,
member_nick varchar(20) not null unique,
member_gender char(2) not null,
member_age int not null,
member_city varchar(30) not null
);
2)创建实体类
@Data
@NoArgsConstructor
@AllArgsConstructor
@ToString
public class Member {
private int memberId;
private String memberNick;
private String memberGender;
private int memberAge;
private String memberCity;
}
3)创建DAO接口
在DAO接口中定义一个多条件查询的方法
public interface MemberDAO {
//多条件查询下,若查询条件不确定,可以直接使用hashmap作为参数
public List<Member> searchMember(HashMap<String,Object> params);
}
4)映射配置文件
- if标签
- where标签用法
- trim标签
可以添加前后缀
- foreach标签
十三、模糊查询
案例:根据昵称查询会员信息(模糊匹配 like)
13.1 模糊查询实现
模糊查询需使用${}取值,与SQL进行拼接。在使用${}时,即使只有一个参数@Param注解声明参数key(非String对象参数可以不进行声明)
1)DAO
public interface MemberDAO {
//根据昵称查询用户信息-模糊查询
//方式一
//public List<Member> searchMemberByNick(HashMap<String,Object> params);
//方式二
public List<Member> searchMemberByNick(@Param("keyWord") String keyWord);
}
2)映射配置文件
- ${ }和#{ }的区别
${key}表示获取参数,先获取参数值并拼接到SQL语句中,再编译执行SQL语句,可能引起SQL注入问题;
#{key}表示获取参数,先完成SQL编译(预编译),再将获取的参数设置到SQL,能够避免SQL注入问题;
<!-- SQL注入就是参数的值导致SQL的原意发生改变 -->
3)测试
@Test
public void testSearchMemberByNick() {
//HashMap<String,Object> params=new HashMap<String,Object>();
//params.put("keyWord","花");
MemberDAO memberDAO=MyBatisUtil.getMapper(MemberDAO.class);
//List<Member> members=memberDAO.searchMemberByNick(param);
List<Member> members=memberDAO.searchMemberByNick("花");
for(Member m:members){
System.out.println(m);
}
}
十四、常用注解
14.1 接口类的注解
1)@Repository
- @Repository 是 Spring 的注解,用于声明一个 Bean。@Repository单独使用没用。可以这样理解,注解放在接口上本来就没有意义,spring中在mapper接口上写一个@Repository注解,只是为了标识,要想真正是这个接口被扫描,必须使用@MapperScannerConfigurer
@Repository
public interface UserMapper {
List<User> getAllUsers();
int updateUserAgeById(Integer id);
}
<!-- 配置 Mapper 扫描器 -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.xsbc.mapper"/>
</bean>
这段配置会扫描com.xsbc.mapper包下所有的接口,然后创建各自的动态代理类。
与spring集成可分三个步骤:
- 把java类对应的Mapper接口类纳入spring总的IOC容器;
- 把Java类对应的XML命名空间添加到Mybatis中的Configuration类中的mapperRegistry(用于管理Mybatis的Mapper);
- 使用spring中的IOC容器拓展FactoryBean获取到Mapper的实例。(第一步纳入spring只是接口);
2)@Mapper
- @Mapper是mybatis自身带的注解。在spring程序中,mybatis需要找到对应的mapper,在编译时生成动态代理类,与数据库进行交互,这时需要用到@Mapper注解。
@Mapper
public interface UserMapper {
List<User> getAllUsers();
int updateUserAgeById(Integer id);
}
- 但是有时候当我们有很多mapper接口时,就需要写很多@Mappe注解,这样很麻烦,有一种简便的配置化方法便是在启动类上使用@MapperScan注解,而不需要添加@Mapper注解。
@SpringBootApplication
@MapperScan("com.xsbc.mapper")
public class SpringbootMysqlRedisApplication {
public static void main(String[] args) {
SpringApplication.run(SpringbootMysqlRedisApplication.class, args);
}
}
14.2 实体类注解
若使用Spring Data JPA方式,则需引入依赖!可以省略建表的流程
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
- @Entity:标注用于实体类声明语句之前,指出该Java 类为实体类,将映射到指定的数据库表;
- @Table:当实体类与其映射的数据库表名不同名时需要使用 @Table 标注说明,该标注与 @Entity 标注并列使用,置于实体类声明语句之前,可写于单独语句行,也可与声明语句同行。@Table 标注的常用选项是 name,用于指明数据库的表名 @Table标注还有一个两个选项 catalog 和 schema 用于设置表所属的数据库目录或模式,通常为数据库名;
- @Id:标注用于声明一个实体类的属性映射为数据库的主键列。该属性通常置于属性声明语句之前,可与声明语句同行,也可写在单独行上。
- @GeneratedValue:用于标注主键的生成策略,通过 strategy 属性指定。
- @Column:当实体的属性与其映射的数据库表的列不同名时需要使用@Column 标注说明,该属性通常置于实体的属性声明语句之前,还可与 @Id 标注一起使用。标注的常用属性是 name,用于设置映射数据库表的列名。此外,该标注还包含其它多个属性,如:unique 、nullable、length 等。