写本篇博客主要是因为我刚刚学完前端Vue技术,再回看后端内容的时候发现已经忘得差不多了,故以此篇巩固知识、也方便大家复习。
一、搭建maybatis环境
第一步:创建maven工程,然后一直点下一步就可以了
第二步:引入pom依赖
<dependencies>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.2</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.31</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.24</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>RELEASE</version>
<scope>compile</scope>
</dependency>
</dependencies>
第三步:创建mybatis核心文件
<?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>
<!--
mybatis核心配置文件中的标签必须按照指定的顺序进行配置:
properties typeAliases environments mappers
-->
<properties resource="jdbc.properties"/>
<typeAliases>
<!--
type:设置需要起别名的类型
alias:设置某个类型的别名
-->
<!--
<typeAlias type="" alias=""></typeAlias>
如果不设置alias,当前类型拥有默认的别名,即类名,且不区分大小写
-->
<!--
通过包来设置别名,指定包下所有类型将全部拥有默认的别名,且为类名
-->
<package name="pojo"></package>
</typeAliases>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"></transactionManager>
<dataSource type="POOLED">
<property name="driver" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</dataSource>
</environment>
</environments>
<!--引入mybatis的映射文件-->
<mappers>
<!--
<mappers resource="mappers/UserMapper.xml"/>
-->
<!--
以包的方式引入映射文件,但是必须满足两个条件:
1.mapper接口和映射文件所在的包必须一致
2.mapper接口的名字和映射文件的名字必须一致
-->
<package name="mapper"></package>
</mappers>
</configuration>
第四步:配置连接数据库的核心文件并连接数据库
注意url中/db1是我自己的数据库,大家记得修改为你要连接的数据库
jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/db1?serverTimezone=UTC
jdbc.username=root
jdbc.password=root
创建数据库的时候,只需要点击+号--->数据源--->mysql即可
点击进来以后,需要手动填写的是用户、密码、数据库三个部分,然后测试连接,连接通过后就可点击应用了
第五步:建表
use db1;
create table users(
uid int not null primary key auto_increment,
uname varchar(10) not null,
uage int not null
);
insert into users(uname,uage) values ('张三',20),('李四',16);
接下来就是创建实体类、编写mybatis代码以及测试了
这是我创建的文件目录,大家可以照着创建
第六步:创建实体类
package pojo;
import lombok.*;
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
@ToString
public class User {
private int uid;
private String uname;
private int uage;
public User(String uname,int uage){
this.uname=uname;
this.uage=uage;
}
}
第七步:
编写mapper接口以及对应的xml脚本
接口:
public interface UserMapper {
User getUserById(int id);
}
xml:注意命名空间是接口的全限定名
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="mapper.UserMapper">
<!--查(单个用户)-->
<select id="getUserById" parameterType="int" resultType="user">
select uid,uname,uage
from users
where uid=#{uid}
</select>
</mapper>
第八步:编写测试代码
import mapper.UserMapper;
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.After;
import org.junit.Before;
import org.junit.Test;
import pojo.User;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
public class test1 {
SqlSession sqlSession;
@Before
public void openSqlSession() throws IOException {
InputStream resourceAsStream = Resources.getResourceAsStream("MybatisConfig.xml");
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(resourceAsStream);
sqlSession=factory.openSession();
}
@After
public void closeSqlSession(){
sqlSession.close();
}
@Test
public void testGetUserById(){
UserMapper uMapper = sqlSession.getMapper(UserMapper.class);
User user = uMapper.getUserById(2);
System.out.println(user);
}
}
运行截图:
至此,mybatis基本环境已经搭建好了。
二、基础---增删改查
2.1 增
2.1.1 单个增加
接口:
/*单个增加*/
void addUser(User user);
xml:
<!--增(单个增加)-->
<insert id="addUser" parameterType="user">
insert into users(uname,uage) values (#{uname},#{uage})
</insert>
测试:注意,因为jdbc提交事务的时候将autocommit设置为false,所以如果想将数据插入到数据库而不被回滚,可以手动提交事务;也可以在打开sqlSession的时候传入参数true
@Test
public void testAddUser(){
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
User user=new User("王五",42);
mapper.addUser(user);
sqlSession.commit();
}
批注:
MyBatis事务:
openSession():默认开启事务,进行增删改操作后需要使用sqlSession.commit();手动提交事务
openSession(true):可以设置为自动提交事务(关闭事务)
2.1.2 单个增加并返回主键
接口:注意返回值是操作行成功的数量,而不是返回的主键。
/*单个增加但返回主键*/
void addUserReturnKey(User user);
xml:设置useGeneratedKeys="true",并用keyProperty接收返回的主键
<!--增(单个增加但返回主键)-->
<insert id="addUserReturnKey" parameterType="user" useGeneratedKeys="true" keyProperty="uid">
insert into users(uname,uage) values (#{uname},#{uage})
</insert>
测试:通过user.getUid()得到返回的主键
@Test
public void testAddUserReturnKey(){
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
User user=new User("柳七",24);
mapper.addUserReturnKey(user);
sqlSession.commit();
System.out.println(user.getUid());
}
2.1.3 批量添加
接口:
/*批量增加*/
void addUsers(List<User> users);
xml:
<!--增(批量增加)-->
<insert id="addUsers" parameterType="user">
insert into users(uname,uage) values
<foreach collection="list" item="user" separator=",">
(#{user.uname},#{user.uage})
</foreach>
</insert>
测试:
@Test
public void testAddUsers(){
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
User user1 = new User("李信", 34);
User user2 = new User("芈月", 28);
User user3 = new User("孙策", 41);
List<User> list=new ArrayList<>();
list.add(user1);list.add(user2);list.add(user3);
mapper.addUsers(list);
sqlSession.commit();
}
批注:
标签参数详解:
collection:用来指定入参的类型
如果是List集合,则为list
如果是Map集合,则为map
如果是数组,则为array
item:每次循环遍历出来的值或对象
separator:多个值或对象或语句之间的分隔符
open:整个循环外面的前括号
close:整个循环外面的后括号
2.2 改
2.2.1 修改全部字段
接口:
/*修改全部字段*/
void updateAllById(User user);
xml:
<!--改(修改全部字段)-->
<update id="updateAllById" parameterType="user">
update users
set
uname=#{uname},
uage=#{uage}
where uid=#{uid}
</update>
测试:
@Test
public void testUpdateAllById(){
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
mapper.updateAllById(new User(4,"韩信",25));
sqlSession.commit();
}
2.2.2 动态修改字段
接口:
/*动态修改字段*/
void updateDynamically(User user);
xml:注意,if语句中只有字符串类型需要额外判空(是否为空字符串),其他类型则不需要。
但是我在建表的时候设置了uage为not null,也就是说如果不设置uage会填充默认值,所以这里判断了uage是否为0(不设置uage默认为0)
if里的修改语句每一行后加上逗号,别忘了!
<!--改(动态修改字段)-->
<update id="updateDynamically" parameterType="user">
update users
<set>
<if test="uname != null and uname != ''">
uname=#{uname},
</if>
<if test="uage != null and uage != 0">
uage=#{uage},
</if>
</set>
where uid=#{uid}
</update>
测试:
@Test
public void testUpdateDynamically(){
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
User user=new User();
user.setUid(1);
user.setUname("张斯瑞");
mapper.updateDynamically(user);
sqlSession.commit();
}
2.3 删
2.3.1 单个删除
接口:
/*单个删除*/
void deleteUserById(int uid);
xml:
<!--删(单个删除)-->
<delete id="deleteUserById" parameterType="int">
delete from users where uid=#{uid}
</delete>
测试:
@Test
public void testDeleteById(){
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
mapper.deleteUserById(1);
sqlSession.commit();
}
2.3.2 批量删除
接口:
/*批量删除*/
void deleteBatch(int[] uids);
xml:
<!--删(批量删除)-->
<delete id="deleteBatch">
delete from users where uid in
<foreach collection="array" item="uid" open="(" close=")" separator=",">
#{uid}
</foreach>
</delete>
测试:
@Test
public void testDeleteBatch(){
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
int[] uids=new int[]{2,4,5};
//也可以用下面的语句简单的初始化数组
//int[] test={2,4,5};
mapper.deleteBatch(uids);
sqlSession.commit();
}
2.4 查
2.4.1 单个查询
接口:
/*查询单个用户*/
User getUserById(int id);
xml:
<!--查(单个用户)-->
<select id="getUserById" parameterType="int" resultType="user">
select uid,uname,uage
from users
where uid=#{uid}
</select>
测试:
@Test
public void testGetUserById(){
UserMapper uMapper = sqlSession.getMapper(UserMapper.class);
User user = uMapper.getUserById(7);
System.out.println(user);
}
2.4.2 批量查询
接口:
/*动态查询用户*/
List<User> getUserDynamically(int[] uids);
xml:
<sql id="allColumns">
uid,uname,uage
</sql>
<!--查(动态查询用户)-->
<select id="getUserDynamically" resultType="user">
select <include refid="allColumns"/>
from users
where uid in
<foreach collection="array" item="uid" open="(" close=")" separator=",">
#{uid}
</foreach>
</select>
测试:
@Test
public void testGetUserDynamically(){
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
int[] uids={6,7,8};
List<User> list = mapper.getUserDynamically(uids);
list.forEach(System.out::println);
}
运行截图:
批注:
动态sql:
可以定义代码片段,可以进行逻辑判断,可以进行循环处理(批量处理),使条件判断更为简单
1):用来定义代码片段,可以将所有的列名,或复杂的条件定义为代码片段,供使用时调用
2):用来引用定义的代码片段
定义的时候:
<sql id="allColumns"> uid,uname,uage </sql>
引用的时候:
<include refid="allColumns"/>
2.4.3 全部查询
接口:
/*查询所有用户*/
List<User> getAllUsers();
xml:
<!--查(所有用户)-->
<select id="getAllUsers" resultType="user">
select <include refid="allColumns"/> from users;
</select>
测试:
@Test
public void testGetAllUsers(){
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
List<User> users = mapper.getAllUsers();
System.out.println(users);
}
至此,基本的增删查改就结束了。
三、进阶---复杂条件处理
3.1 传入多个参数
可以通过#{arg0}~#{argX} 指定参数名称
如果入参是多个,实体类包不住,但可以通过指定参数位置进行传参。
因为实体类只能封装住成员变量的条件,如果某个成员变量要有区间范围内的判断,或者有两个值进行处理,则实体类包不住
接口:
/*查询年龄区间*/
List<User> getRangeAge(int startAge,int endAge);
xml:不用指定parameterType,因为它把握不住。直接使用arg0~argX
<!--查(指定区间)-->
<select id="getRangeAge" resultType="user">
select <include refid="allColumns"/> from users
where uage between #{arg0} and #{arg1}
</select>
测试:
@Test
public void testGetRangeAge(){
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
List<User> list = mapper.getRangeAge(25, 35);
list.forEach(System.out::println);
}
运行截图:
3.2 入参是map
如果入参超过一个以上,使用map封装查询条件,更有语义,查询条件更加明确
接口:
/*查询年龄区间以外的用户*/
List<User> getNotRangeAge(Map map);
xml:
<!--查(指定区间以外的用户)-->
<select id="getNotRangeAge" resultType="user">
select <include refid="allColumns"/> from users
where uage not between #{startAge} and ${endAge}
</select>
测试:
@Test
public void testGetNotRangeAge(){
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
Map map=new HashMap();
map.put("startAge",25);
map.put("endAge",35);
List<User> list = mapper.getNotRangeAge(map);
list.forEach(System.out::println);
}
3.3 @Param指定参数名称
@Param("columnName"):这里定义的columnName的名称是要在xml文件中的${引用定义的名称}
接口:
/*查询指定列值*/
List<User> getByColumn(@Param("columnName")String columnName,@Param("columnValue")int columnValue);
xml:注意列名要用${}拼接,而列值无论是不是String类型,都可以用#{}拼接
<!--查询指定列值-->
<select id="getByColumn" resultType="user">
select <include refid="allColumns"/> from users
where ${columnName}=#{columnValue};
</select>
测试:
@Test
public void testGetByColumn(){
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
List<User> list = mapper.getByColumn("uage", 41);
list.forEach(System.out::println);
}
运行截图:
/*返回值是map(一行)*/
Map getReturnMap(int uid);
3.4 返回值是map
3.4.1 一行
如果返回的数据实体类无法包含,可以使用map返回多张表中的若干数据,返回后这些数据之间没有任何关系,就是Object类型;返回的map的key就是列名或别名
接口:
/*返回值是map(一行)*/
Map getReturnMap(int uid);
xml:
<!--查(返回值是单行map)-->
<select id="getReturnMap" parameterType="int" resultType="map">
select uname,uage
from users
where uid=#{uid}
</select>
测试:
@Test
public void testGetReturnMap(){
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
Map map = mapper.getReturnMap(8);
System.out.println(map);
System.out.println(map.get("uname"));
System.out.println(map.get("uage"));
}
运行截图:
3.4.2 多行
接口:
/*返回值是map(多行)*/
List<Map> getReturnMaps();
xml:
<!--查(返回值是多行map)-->
<select id="getReturnMaps" resultType="map">
select uname,uage from users
</select>
测试:
@Test
public void testGetReturnMaps(){
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
List<Map> list = mapper.getReturnMaps();
list.forEach(System.out::println);
}
运行截图:
3.5 列名与类中成员变量名称不一致
为了说清楚具体的解决方案,另建表
create table book(
bookid int(11) not null primary key auto_increment,
bookname varchar(32) not null comment '图书名称'
);
insert into book(bookid, bookname) values(1,'java基础'),(2,'sql基础');
建实体类:
package pojo;
import lombok.*;
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
@ToString
public class User {
private int uid;
private String uname;
private int uage;
public User(String uname,int uage){
this.uname=uname;
this.uage=uage;
}
}
3.5.1 解决方案一:使用别名
使用列的别名,别名与类中的成员变量名一样,即可完成注入
接口:
package mapper;
import pojo.Book;
import java.util.List;
public interface BookMapper {
List<Book> getAllBooks();
}
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.BookMapper">
<!--
mapper接口和映射文件要保证两个一致:
1.mapper接口的全类名和映射文件的namespace一致
2.mapper接口中的方法的方法名要和映射文件中的sql的id保持一致
-->
<select id="getAllBooks" resultType="book">
select bookid bookId,bookname bookName from book
</select>
</mapper>
测试:注意在创建SqlSessionFactoryBuilder()以后调用的build()函数里传入的是mybatis的核心配置文件
import mapper.BookMapper;
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.After;
import org.junit.Before;
import org.junit.Test;
import pojo.Book;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
public class test2 {
SqlSession sqlSession;
@Before
public void openSqlSession() throws IOException {
InputStream is = Resources.getResourceAsStream("MyBatisConfig.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
sqlSession = sqlSessionFactory.openSession();
}
@After
public void closeSqlSession(){
sqlSession.close();
}
@Test
public void testGetAllBooks(){
BookMapper mapper = sqlSession.getMapper(BookMapper.class);
List<Book> books = mapper.getAllBooks();
books.forEach(System.out::println);
}
}
运行截图:
3.5.2 解决方案二:使用resultMap
使用标签进行映射,只需要修改xml文件即可
注意主键用id标签,非主键用result标签
<resultMap id="bookmap" type="book">
<id property="bookId" column="bookid"/>
<result property="bookName" column="bookname"/>
</resultMap>
<select id="getAllBooks" resultMap="bookmap">
select bookid,bookname from book
</select>
四、深入---一对多、多对一关系处理
首先需要说明的是:
复杂的属性,我们需要单独处理:
对象:association
集合:collection
建表:
create table stu(
id int primary key auto_increment,
name varchar(20),
gender varchar(2)
);
create table class(
id int primary key,
stuId int,
classId int,
constraint fk_stu_id foreign key (stuId) references stu (id)
);
insert into stu(id,name,gender) values(1001,'李信','男'),(1002,'安琪拉','女'),(1003,'李白','男'),(1004,'澜','男');
insert into class(id,stuId,classId) values(1,1001,1),(2,1002,2),(3,1003,2),(4,1004,1);
创建实体类:
Stu:
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Stu {
private int id;
private String name;
private String gender;
private Class aClass;
}
Class:
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Class {
private int id;
private int stuId;
private int classId;
private List<Stu> stus;
}
4.1 一对多关系处理
接口:
public interface ClassMapper {
List<Class> getClassById(int classId);
}
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.ClassMapper">
<!--
mapper接口和映射文件要保证两个一致:
1.mapper接口的全类名和映射文件的namespace一致
2.mapper接口中的方法的方法名要和映射文件中的sql的id保持一致
-->
<resultMap id="classmap" type="Class">
<id property="id" column="id"/>
<result property="stuId" column="stuId"/>
<result property="classId" column="classId"/>
<collection property="stus" ofType="stu">
<id property="id" column="id"/>
<result property="name" column="name"/>
<result property="gender" column="gender"/>
</collection>
</resultMap>
<select id="getClassById" resultMap="classmap" parameterType="int">
select class.id,class.stuId,class.classId,stu.id,stu.name,stu.gender from class left outer join stu on class.stuId=stu.id where classId=#{classId}
</select>
</mapper>
测试:
@Test
public void testGetClassById(){
ClassMapper mapper = sqlSession.getMapper(ClassMapper.class);
List<Class> list = mapper.getClassById(1);
list.forEach(System.out::println);
}
运行截图:
总结:
一对多关系使用collection标签,ofType属性
4.2 多对一关系处理
接口:
public interface StuMapper {
Stu getStuById(int id);
}
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.StuMapper">
<!--
mapper接口和映射文件要保证两个一致:
1.mapper接口的全类名和映射文件的namespace一致
2.mapper接口中的方法的方法名要和映射文件中的sql的id保持一致
-->
<resultMap id="stumap" type="Stu">
<id property="id" column="id"/>
<result property="name" column="name"/>
<result property="gender" column="gender"/>
<association property="aClass" javaType="Class">
<id property="id" column="id"/>
<result property="stuId" column="stuId"/>
<result property="classId" column="classId"/>
</association>
</resultMap>
<select id="getStuById" resultMap="stumap" parameterType="int">
select stu.id,stu.name,stu.gender,class.id,class.classId from stu left outer join class on stu.id=class.stuId where stu.id=#{id}
</select>
</mapper>
测试:
@Test
public void testGetStuById(){
StuMapper mapper = sqlSession.getMapper(StuMapper.class);
Stu stu = mapper.getStuById(1001);
System.out.println(stu);
}
运行截图:
至此,mybatis大部分场景你都可以轻松应对了,恭喜~~❀❀