目录
2.2构建SqlSessionFactory,获得SqlSession
2.2.2构建SqlSessionFactory,从SqlSessionFactory中获得SqlSession
2.3.5将mapper.xml文件在mybatis-config.xml文件中注册
3.2.2mybatis-config.xml文件中配置添加 db.properties的引用标签,并对数据库连接相关的代码进行修改
1.简介
MyBatis 是一款优秀的持久层框架,它支持自定义 SQL、存储过程以及高级映射。MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。。
2.入门
2.1安装
使用maven导入项目依赖
<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.22</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.6</version>
</dependency>
2.2构建SqlSessionFactory,获得SqlSession
SqlSession相当于jdbc中的Connection,用来对数据库进行处理,通过 SqlSessionFactory获得。SqlSessionFactory可以通过XML文件获得。
2.2.1配置mybatis-config.xml
mybatis-config.xml配置文件中包含了对 MyBatis 系统的核心设置,包括获取数据库连接实例的数据源(DataSource)以及决定事务作用域和控制方式的事务管理器(TransactionManager)。
<?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 default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis?serverTimezone=GMT&useUnicode=true&characterEncoding=utf-8&allowMultiQueries=true"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</dataSource>
</environment>
</environments>
</configuration>
2.2.2构建SqlSessionFactory,从SqlSessionFactory中获得SqlSession
public class MybatisUtils {
private static SqlSessionFactory sqlSessionFactory;
static {
try{
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
}catch (Exception e){
e.printStackTrace();
}
}
public static SqlSession getSqlSession(){
return sqlSessionFactory.openSession();
}
}
2.3映射sql语句
2.3.1准备数据库
2.3.2根据表编写实体类
public class User {
private int id;
private String name;
private String pwd;
public User(){}
public User(int id, String name, String pwd){
this.id = id;
this.name = name;
this.pwd = pwd;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPwd() {
return pwd;
}
public void setPwd(String pwd) {
this.pwd = pwd;
}
public String toString(){
return "User" + "["
+"id: " + id +", name: " + name +" pwd: " + pwd + "]";
}
}
2.3.3 编写mapper接口
public interface UserMapper {
// 接口方法,方法名与mapper.xml文件中sql映射的id相同,
// 接口名与mapper.xml文件中的namespace相同
public List<User> getUserList();
}
2.3.4在mapper.xml文件中映射sql语句
<?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="com.plane.mapper.UserMapper">
<select id="getUserList" resultType="com.plane.pojo.User">
select * from user
</select>
</mapper>
2.3.5将mapper.xml文件在mybatis-config.xml文件中注册
<mappers>
<mapper resource="UserMapper.xml"/>
</mappers>
2.4 测试
public class Test {
public static void main(String[] args) {
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
List<User> userList = userMapper.getUserList();
for (User user : userList) {
System.out.println(user);
}
}
}
3.优化
3.1使用别名
在UserMapper.xml文件中,在使用到实体类的时候,必须把包名也带上,这样会显得代码很长。可以通过使用别名,来为实体类命名一个更好的名称
在mybatis-config.xml中加入 typeAliases标签
<typeAliases>
<typeAlias type="com.plane.pojo.User" alias="User"></typeAlias>
</typeAliases>
3.2用外部文件来配置数据库的连接
3.2.1准备一个db.properties文件
driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/mybatis?serverTimezone=GMT&useUnicode=true&characterEncoding=utf-8&allowMultiQueries=true
username=root
password=root
3.2.2mybatis-config.xml文件中配置添加 db.properties的引用标签,并对数据库连接相关的代码进行修改
<properties resource="db.properties">
</properties>
<dataSource type="POOLED">
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
3.3另外,附上crud操作的数据库映射代码
public interface UserMapper {
// 接口方法,方法名与mapper.xml文件中sql映射的id相同,
// 接口名与mapper.xml文件中的namespace相同
public List<User> getUserList();
public User getUserById(int id);
public int modifyUser(User user);
public int deleteUser(int id);
public int addUser(User user);
}
<mapper namespace="com.plane.mapper.UserMapper">
<select id="getUserList" resultType="User">
select * from user
</select>
<select id="getUserById" resultType="User">
select * from user where id=#{id}
</select>
<update id="modifyUser" parameterType="User">
update user set name=#{name}, pwd=#{pwd} where id=#{id}
</update>
<delete id="deleteUser" parameterType="int">
delete from user where id=#{id}
</delete>
<insert id="addUser" parameterType="User">
insert into user(id, name, pwd) VALUES (#{id}, #{name}, #{pwd})
</insert>
</mapper>
4 一对多和多对一的数据处理
对象关系很多时候都会是比较复杂的,比如学校里的老师和学生,假设一个简单的场景:一位老师教授多位学生,多位学生由一位老师教授。那么,对于老师来说,他和学生是一对多的关系。对于学生们来说,他们和老师是多对一的关系。
mysql中处理这样的关系一般有两种方法:连表查询和子查询。那么,在mybatis中该如何处理这样的关系呢?这一章将解决这个问题。
数据库中新建的两张表
4.1处理多对一的关系
4.1.1编写如下的学生实体类和老师实体类
@Data
public class Student {
private int id;
private String name;
private Teacher teacher;
}
@Data
public class Teacher {
private int id;
private String name;
}
对于学生们来说,他们和老师就是多对一的关系。在前面的文章讲述的查询中,查询结果的属性都只是基本类型,而在这个例子里,查询结果学生的一个属性是对象。
这种情况下,直接使用简单的select语句得到的student结果他的teacher属性会为null。当实体类的属性为对象的时候,需要使用到 association 标签
4.1.2按查询嵌套处理
<select id="getStudentList" resultMap="StudentMap">
select * from student
</select>
<resultMap id="StudentMap" type="Student">
<association property="teacher" column="tid" javaType="Teacher" select="getTeacher">
</association>
</resultMap>
<select id="getTeacher" resultType="Teacher">
select * from teacher where id = #{tid}
</select>
4.1.3按结果嵌套处理
<select id="getStudentList2" resultMap="StudentMap2">
select s.id sid, s.name sname, t.name tname
from student as s, teacher as t
where t.id = s.tid
</select>
<resultMap id="StudentMap2" type="Student">
<result property="id" column="sid"></result>
<result property="name" column="sname"></result>
<association property="teacher" javaType="Teacher">
<result property="name" column="tname"></result>
</association>
</resultMap>
4.2 处理一对多的关系
4.2.1编写如下学生实体类和老师实体类
@Data
public class Student2 {
int id;
String name;
int tid;
}
public class Teacher2 {
private int id;
private String name;
private List<Student2> student2s;
}
一对多便是一位老师教授多位学生的关系。与多对一类似,一对多也需要使用标签——需要使用collectiong标签。
4.2.2按查询嵌套处理
<select id="getTeacher" resultMap="TeacherMap">
select * from teacher
</select>
<resultMap id="TeacherMap" type="Teacher2">
<collection property="student2s" column="id" javaType="ArrayList" ofType="Student2" select="getStudent2sByTeacher"></collection>
</resultMap>
<select id="getStudent2sByTeacher" resultType="Student2">
select * from student where tid = #{id}
</select>
4.2.3按结果嵌套处理
<select id="getTeacher2" resultMap="TeacherMap2">
select t.id tid, t.name tname, s.name sname, s.id sid
from teacher as t, student as s
where s.tid = t.id
</select>
<resultMap id="TeacherMap2" type="Teacher2">
<result property="id" column="tid"></result>
<result property="name" column="tname"></result>
<collection property="student2s" ofType="Student2">
<result property="id" column="sid"></result>
<result property="name" column="sname"></result>
<result property="tid" column="tid"></result>
</collection>
</resultMap>
对比可以看出,按查询嵌套处理和按结果嵌套处理稍有差异。
ofType属性声明了它所标记的类型是一个泛型:集合,或者列表等。
5 动态sql
5.1简介
什么是动态sql?举例来说,在一个员工管理系统中,员工有工号、姓名以及职务,用户可以通过输入这三个条件来查询员工,但是输入的可能性是无法预测的,也就倒置了sql的查询语句会发生变化。在jdbc中,可以通过Java代码来对输入进行判断,进而拼接sql语句。在mybatis中,则用动态sql来直接在非Java代码层面实现。
准备如下数据库表以及实体类
@Data
@AllArgsConstructor
public class Blog {
private String id;
private String title;
private String author;
private Date createTime;
private int views;
public Blog(){};
}
5.2. 常用标签
5.2.1 if和where标签
<where> <if test="判断条件"> if中的sql语句 </if> </where>
在这样的标签中,只有当where内部的if标签存在判断条件为真的情况下, 才会将where以及满足判断条件的if中的sql语句拼接在sql语句中,并且如果这样导致where后面的第一个单词就是and或者or的话,会自动删除and或者or
看下面的测试代码
<select id="selectBlog" parameterType="map" resultType="Blog">
select * from blog
<where>
<if test="id!=null"> id=#{id} </if>
<if test="title!=null"> and title=#{title}</if>
<if test="author!=null"> and author=#{author}</if>
<if test="createTime!=null"> and creatTime=#{creatTime}</if>
<if test="views!=null"> and views=#{views}</if>
</where>
</select>
public static void main(String[] args) {
SqlSession sqlSession = SessionFactory.getSqlSession();
BlogMapper blogMapper = sqlSession.getMapper(BlogMapper.class);
Map map = new HashMap();
map.put("id", "1");
map.put("title", "测试1");
map.put("author", "author");
map.put("views", "1000");
List<Blog> blogList = blogMapper.selectBlog(map);
for (Blog blog : blogList) {
System.out.println(blog);
}
sqlSession.close();
}
输出信息:
==> Preparing: select * from blog WHERE id=? and title=? and author=? and views=?
==> Parameters: 1(String), 测试1(String), author(String), 1000(String)
将map.put("id", "1")和map.put("title", "测试1")那行注释掉, 再看输出信息
==> Preparing: select * from blog WHERE author=? and views=?
==> Parameters: author(String), 1000(String)
最后将map的put全部注释掉,再看输出信息
==> Preparing: select * from blog
==> Parameters:
5.2.2choose标签
<where> <choose> <when test="判断条件1"> sql语句1 </when> <when test="判断条件2"> sql语句2 </when> <otherwise> 其它情况下的sql语句 </otherwise> </choose> </where>
choose标签和普通编程语句的switch语句一般,when相当于case,otherwise中的sql语句则是在所有when都不匹配的情况下进入。其拼接规则同5.2.1
5.2.3set标签
<set> <if test="判断语句1">sql语句1</if> <if test="判断语句2">sql语句2</if> <if test="判断语句3">sql语句3</if> <if test="判断语句4">sql语句4</if> </set>
在判断语句存在为真情况下,才会将set以及sql与之前的sql语句进行拼接,并且会把set语句多出来的最后那个逗号自动删除
测试代码
<update id="updateBlog" parameterType="map">
update blog
<set>
<if test="title!=null"> title=#{title}, </if>
<if test="author!=null"> author=#{author}, </if>
<if test="createTime!=null"> createTime=#{creatTime}, </if>
<if test="views!=null"> views=#{views}, </if>
</set>
</update>
SqlSession sqlSession = SessionFactory.getSqlSession();
BlogMapper blogMapper = sqlSession.getMapper(BlogMapper.class);
Map map = new HashMap();
map.put("id", "1");
map.put("title", "测试set");
map.put("author", "authorset");
map.put("views", "1000");
blogMapper.updateBlog(map);
sqlSession.commit();
sqlSession.close();
输出信息
==> Preparing: update blog SET title=?, author=?, views=?
==> Parameters: 测试set(String), authorset(String), 1000(String)
可以看到,在views的if标签中,后面那个特意加上的逗号被自动去掉了。
5.2.4foreach标签
<select id="selectPostIn" resultType="domain.blog.Post"> SELECT * FROM POST P WHERE ID in <foreach item="item" index="index" collection="list" open="(" separator="," close=")"> #{item} </foreach> </select>
在这个例子中,foreach标签会从list的集合中遍历元素,得到每个item,并且有open定义(开头,close定义)结束,将每个item包起来,并由separator定义 , 分隔。
测试代码
<select id="selectBlogForeach" parameterType="map" resultType="Blog">
select * from blog
where id in
<foreach collection="list" item="item" open="(" close=")" separator=",">
#{item}
</foreach>
</select>
SqlSession sqlSession = SessionFactory.getSqlSession();
BlogMapper blogMapper = sqlSession.getMapper(BlogMapper.class);
Map map = new HashMap();
List list = new ArrayList();
list.add("1");
list.add(2);
list.add("3");
map.put("list", list);
List<Blog> blogList = blogMapper.selectBlogForeach(map);
for (Blog blog : blogList) {
System.out.println(blog);
}
sqlSession.commit();
sqlSession.close();
输出信息
==> Preparing: select * from blog where id in ( ? , ? , ? )
==> Parameters: 1(String), 2(Integer), 3(String)
6 缓存
mybatis有一级缓存和二级缓存,一级缓存默认是开启的,二级缓存通过配置开启。
在查询数据库时,根据 二级缓存---->一级缓存---->数据库 的顺序进行查找数据
一级缓存生命周期从sqlSession打开到关闭,sqlSession关闭后,一级缓存的数据提交到二级缓存中。
二级缓存是全局缓存,在缓存wei被更新期间,整个Mapper的二级缓存都会存在
- 映射语句文件中的所有 select 语句的结果将会被缓存。
- 映射语句文件中的所有 insert、update 和 delete 语句会刷新缓存。
- 缓存会使用最近最少使用算法(LRU, Least Recently Used)算法来清除不需要的缓存。
- 缓存不会定时进行刷新(也就是说,没有刷新间隔)。
- 缓存会保存列表或对象(无论查询方法返回哪种)的 1024 个引用。
- 缓存会被视为读/写缓存,这意味着获取到的对象并不是共享的,可以安全地被调用者修改,而不干扰其他调用者或线程所做的潜在修改。