Mybatis
基础概念
Mybatis是一个针对数据库连接的持久化框架,根据之前学习的JDBC代码,mybatis就是对应的连接Dao层,即持久化层。
具体实现步骤:
- Dao接口:为了创建具体方法
- User实现类:实现了是为了写出具体要使用的属性,重写setting等方法
- 配置类mybatis-config.xml:配置环境(连接那个数据库)、注册mapper(连接到那个映射类)
- 映射类userMapper.xml:写具体的sql语句,连接到Dao接口
- 工具类mybatisUtils:获取SqlSessionFactroy仓库,创建getSqlSession方法,可以通过该方法实现sqlSession对象的创建
- 测试类:使用junit,测试mybatis的具体实现
创建第一个mybatis
- 创建工具类mybatisUtils
java
public class MybatisUtils {
//提升SqlSessionFactroy的作用域
Private static SqlSessionFactory sqlSessionFactory;
Static {
try {
//获取sqlSessionfactory对象
//从类路径配置类获取数据
String resource = "mybatis-config.xml";
InputStream inputStream = Resource.getResourceAsStream(resource);
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
} catch (Exception e){
e.printStackTrace();
}
}
//创建getSqlSession方法,可以通过该方法获取SqlSession对象
public Static SqlSession getSqlSession() {
return sqlSessionfactory.openSession();
}
}
- 创建配置类Mybatis-config.xml,配置中一些顺序setting(日志和一些规则,如驼峰等)-typeAliases(别名优化)- environment(环境配置)
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>
<properties resource="db.properties"/>
<!--db是外部配置文件,里面主要包含数据库的具体信息,使用了db,下面就可以直接引用-->
<!-- 加入日志,LOG4J要另外引入maven依赖,后面那个是驼峰配置-->
<settings>
<setting name="logImpl" value="LOG4J"/>
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
<!--别名优化-->
<typeAliases>
<typeAlias type="com.huang.pojo.User" alias="User"/>
<!-- 包构筑,只需要写user或者User即可-->
<!-- <package name="com.huang.pojo"/>-->
</typeAliases>
<!--环境配置,可以设定多个环境,但sqlsessionfactory只能选择一种-->
<!--default=d2 这里选择了第二个环境,因为引入了db资源-->
<environments default="d2">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=false&useUnicode=true&characterEncoding=UTF-8"/>
<property name="username" value="root"/>
<property name="password" value="213214"/>
</dataSource>
</environment>
<environment id="d2">
<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>
<!-- 每一个Mapper.xml都需要在Mybatis核心配置文件中注册-->
<mappers>
<mapper resource="com/huang/dao/UserMapper.xml"/>
<!-- 通过class查找映射-->
<!-- <mapper class="com.huang.dao.UserDao"/>-->
<!-- <package name="com.huang.dao">-->
</mappers>
</configuration>
</configuration>
- User实现类,用来写出属性
java
public class User {
public int id;
public String name;
public String pwd;
//忽略setting、getting、tostring及有参无参构造
}
- UserDao接口类
java
public interface UserDao {
//查看全部数据
List<User> getUserList();
//根据id查看对应用户数据
User getUserById(int id);
//增加数据
int addUser(User user);
//通过map添加用户
int addUser2(Map<String, Object> map);
//删除用户
int deleteUser(int id);
//修改用户
int updateUser(User user);
}
- UserMapper.xml映射类,写sql具体语句
xml
<!--其实就是代替了JDBC中的UserServicelmpl实现类-->
<?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.huang.dao.UserDao">
<!--
id:对应UserDao中对应的方法
resultType:sql语句执行的返回值
paramterType:参数类型
-->
<select id="getUserList" resultType="user">
select * from mybatis.user
</select>
<select id="getUserById" parameterType="int" resultType="user">
select * from mybatis.user where id = #{id}
</select>
<insert id="addUser" resultType="user">
insert into mybatis.user (id, name, pwd) values (#{id},#{name},#{pwd})
</insert>
<insert id="addUser2" resultType="user">
insert into mybatis.user (id, name, pwd) values (#{userid},#{username},#{password})
</insert>
<delete id="deleteUser" resultType="user">
delete from mybatis.user where id = #{id}
</delete>
<update id="updateUser" resultType="user">
update mybatis.user
set name=#{name}, pwd=#{pwd}
where id = #{id}
</update>
</mapper>
- mytest测试类
java
public class MyTest {
//获取全部数据
@Test
public void getUserList() {
//获取sqlsession对象
SqlSession sqlSession = MybatisUtils.getSqlSession();
//配置动态实现类
UserDao userDao = sqlSession.getMapper(UserDao.class);
//创建list表单,并查询所有数据
List<User> userList = userDao.getUserList();
for (User user : userList) {
System.out.println(user);
}
}
sqlSession.close();
//根据id查用户
@Test
public void getUserById() {
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserDao userDao = sqlSession.getMapper(UserDao.class);
User user = userDao.getUserById(1);
System.out.println(user);
}
sqlSession.close();
//增加用户
@Test
public void addUser() {
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserDao userDao = sqlSession.getMapper(UserDao.class);
int res = userDao.addUser(new User(4, carlos, 123321));
if (res > 0) {
System.out.println("添加成功");
}
//增删改要提交事务才能起效
sqlSession.commit();
sqlSession.close();
}
//通过map添加用户,这里由于是通过哈希表添加,所以用户的属性可以自己定义
@Test
public void addUser2() {
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserDao userDao = sqlSession.getMapper(UserDao.class);
//建立map
Map<String, Object> map = new HashMap<String, Object>();
map.put("userid", 5);
map.put("username", "carlos2");
map.put("password", "123321123");
userDao.addUser2(map);
//增删改要提交事务才能起效
sqlSession.commit();
sqlSession.close();
}
//删除用户
@Test
public void deleteUser() {
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserDao userDao = sqlSession.getMapper(UserDao.class);
int res = userDao.deleteUser(5);
sqlSession.commit();
sqlSession.close();
}
//修改用户
@Test
public void updateUser() {
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserDao userDao = sqlSession.getMapper(UserDao.class);
userDao.updateUser(new User(4, "huang", "321123"));
sqlSession.commit();
sqlSession.close();
}
}
以上就是我们构建的第一个mybatis,具体流程如下:
- 首先通过mybatis-config.xml实现jdbc中的连接池作用,用于连接数据库
- 通过mybatisUtils工具类调用配置类,完成sqlSessionFactory的创建,并创建getSqlSession方法用来创建sqlsession对象
- 建立User实现类,声明属性并重写方法,后续可以直接导入lombok依赖包,直接使用注解实现setting等方法的重写
- 建立UserDao接口类、UserMapper.xml数据库sql语句、test测试类,其中接口用来接入具体方法,xml用来写sql语句,test测试
映射sql和实现分页
- 映射sql,以上我们是直接通过数据库中的表名进行命名的,但如果数据库中的表名和我们实际中的名字不同,该如何,比如:数据库中为id,实际为userid,使用映射resultMap
- 实现分页:之前使用JDBC实现分页,是通过dao层连接数据,service层中的userService定义方法,userServicelmpl层收入sql语句并实现分页,步骤繁琐,且都处于java代码中,比较复杂。这里可以通过mybatis实现分页,只需要在UserMapper.xml中实现sql语句即可
java
public class User {
private int uid;
private String uname;
private String password;
//省略方法
}
java
public interface UserDao {
List<User> getUserList();
//实现分页
List<User> getPage(Map<String, Integer> map)
}
xml
<!--其实就是代替了JDBC中的UserServicelmpl实现类-->
<?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.huang.dao.UserDao">
<!--
先创建映射
-->
<!--这里,property是实体类中的字段,column是数据库中字段-->
<resultMap id="UserMap" type="User">
<result column="id" property="uid"/>
<result property="uname" column="name"/>
<result property="password" column="pwd"/>
</resultMap>
<select id="getUserList" resultMap="UserMap">
select * from mybatis.user where id = #{id}
</select>
<select id="getPage" resultType="UserMap">
select * from mybatis.user limit #{startPage}, #{pageSize}
</select>
</mapper>
java
//测试
public class Test{
@Test
public void getUserList() {
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserDao userDao = sqlSession.getMapper(UserDao.class);
List<User> userList = userDao.getUserList();
for (User user : userList) {
System.out.println(userList);
}
sqlSession.close();
}
//分页
@Test
public void getPage() {
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserDao userDao = sqlSession.getMapper(UserDao.class);
Map<String, Integer> map = new HashMap<>();
map.put("stratPag", 0);
map.put("pageSize", 2);
List<User> mapList = userDao.getPage(map);
for (User user : mapList){
System.out.println(mapList);
}
sqlsession.close();
}
}
使用注解实现开发
为了避免重复重写setting、getting、toString及有参无参构造,可以在pom中导入lombok依赖,通过注解实现重写,其次,对于sql语句,我们也可以通过注解实现,放弃在xml配置类中输入sql语句。
- User实现类
java
//其中,@Data是实现了无参构造、getting、setting、toStrin等的注解
//@AllArgsConstructor则是实现有参构造,同时如果Data同时存在,会自动覆盖无参构造
//@NoArgsConstructor是再次实现无参构造,这时有参和无参才会同时存在
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User{
public int id;
public String name;
public String pwd;
}
- UserMapper接口类,在这里通过注解实现本来在UserMapper.xml中的sql语句输入
java
public interface UserMapper {
@Select("select * from mybatis.user")
List<User> getUserList();
@Select("select * from mybatis.user where id = #{id}")
User getUserById(@Param("id") int id);
@Insert("insert into mybatis.user (id,name,pwd) value (#{id},#{name},#{pwd})")
int addUser(User user);
@Delete("delete from mybatis.user where id = #{id}")
int deleteUser(@Param("id") int id);
@Update("update mybatis.user set name = #{name}, pwd = #{pwd} where id = #{id}")
int updateUser(User user);
@Insert("insert into mybatis.user (id,name,pwd) value (#{userid},#{username},#{password})")
int addUser2(Map<String, Object> map);
}
其余的部分与上面大差不差,这里要注意的是,注解开发既有好处也有坏处:
- 好处是注解开发可以极大的省略开发部件,提高开发效率,减少代码量
- 坏处是注解开发不可以映射sql,不能实现同步数据库和实体类中的属性映射,同时可读性对于初学者不友好
多对一关系
在数据库中我们经常要使用联表查询,那如何通过java对其进行查询,就先要明确联表之间的关系。
- 多对一和一对多关系是基于不同视角进行评判的,比如一个老师教多个学生,在学生视角就是多对一,即多个学生被一个老师教。而在老师视角,就是一个老师教多个学生
- 在进行案例说明之前,先在数据库中创建出student和teacher表,并插入多个学生和一个老师
sql
create table teacher (
`id` int(20) Not Null Primary Key,
`name` varcher(30) default Null,
) Engine=InnoDB Default charset = utf8
Insert into teacher values (1,"黄老师");
create table student (
`id` int(20) Not Null Primary Key,
`name` varcher(30) Default Null,
constraint `ftid` Foreign key `tid` reference `teacher` (`tid`)
)Engine=InnoDB Default charset=utf8
Insert into stduent values
(1,"小米"),
(2,"小何"),
(3,"小黑");
- 现在创建出两个表,其中student表中有一个tid外键,用来连接teacher表,即从学生视角出发,多对一。
- 创建实体类
java
//Student
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Student {
private int id;
private String name;
//多个学生对应一个老师association,创建一个teacher属性,用来连接老师表
private Teacher teacher;
}
//Teacher
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Teacher {
private int id;
private String name;
}
- 创建接口类
java
//StudentMapper
public interface StudentMapper {
//联表查询1:先查询学生信息,再通过映射子查询老师信息
List<Student> getUser();
//联表查询2:先查询,后统一映射
List<Student> getUser();
}
- 配置类Mybatis-config.xml
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>
<properties resource="db.properties"/>
<!-- 加入日志-->
<settings>
<setting name="logImpl" value="LOG4J"/>
</settings>
<!-- 别名优化-->
<typeAliases>
<typeAlias type="com.huang.pojo.Student" alias="Student"/>
<typeAlias type="com.huang.pojo.Teacher" alias="Teacher"/>
</typeAliases>
<!--环境配置,可以设定多个环境,但sqlsessionfactory只能选择一种-->
<environments default="d2">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=false&useUnicode=true&characterEncoding=UTF-8"/>
<property name="username" value="root"/>
<property name="password" value="213214"/>
</dataSource>
</environment>
<environment id="d2">
<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>
<!-- 每一个Mapper.xml都需要在Mybatis核心配置文件中注册-->
<mappers>
<mapper class="com.huang.dao.TeacherMapper"/>
<mapper class="com.huang.dao.StudentMapper"/>
</mappers>
</configuration>
主要是修改这一段注册,注册到那个接口中
xml
<mappers>
<mapper class="com.huang.dao.TeacherMapper"/>
<mapper class="com.huang.dao.StudentMapper"/>
</mappers>
- 创建sql语句输入的xml文件这个是重点
xml
<!--其实就是代替了JDBC中的UserServicelmpl实现类-->
<?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.huang.dao.StudentMapper">
<!--
联表查询1
-->
<select id ="getUser" resultType="st">
select * from student
</select>
<resultMap id="st" type="Student">
<!--相同可以省略-->
<result property="id" column="id"/>
<result property="name" column="name"/>
<!--其中,assciation是对象的意思,这里我们要子查询teacher,多个学生对应一个老师,那该 老师就可以看成一个对象,即为association
再后面的collection是集合,当一个老师对应多个学生,那多个学生就要看成一个集合
property=teacher 这个是我们在student实现类中的老师属性,对应数据库中的tid-->
<association property="teacher" column="tid" javaType="Teacher" select="getTeacher"/>
</resultMap>
<select id="getTeacher" resultType="Teacher">
select * from teacher where id=#{id}
</select>
<!--
联表查询2
-->
<select id="getUser2" resultMap="st2">
select s.id sid, s.name sname, t.id tid, t.name tname from student s, teacher t where s.tid = t.id;
</select>
<resultMap id="st2" type="Student">
<!--映射学生-->
<result property="id" column="sid"/>
<result property="name" column="sname"/>
<!--映射老师-->
<association property="teacher" javaType="Teacher">
<result property="id" column="tid"/>
<result property="name" column="tname"/>
</association>
</resultMap>
</mapper>
- test
java
public class test {
@Test
Public void getUser() {//修改成getUser2即可,不另外写
SqlSession sqlSession = MybatisUtils.getSqlSession();
StudentMapper studentMapper = sqlSession.getMapper(UserDao.class);
List<Student> studentList = studentMapper.getUset();
for (Student student : studentList) {
System.out.println(student);
}
sqlSession.close();
}
}
以上就是两种联表查询的方法:
- 第一种要求sql低,但逻辑性要好,要找到怎么子查询,子查询那个,比如多对一,要先查学生信息,在通过子查询去查询老师信息
- 第二种则是对sql要求高,查询比较简单,就是先查询学生,在通过association对象查询老师,进行联表
一对多
从老师视角进行联表查询,即从数据量少的表连接到数据量多的表
- teacher实现类
java
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Teacher {
private int id;
private String name;
//一个老师对应多个学生,这里要使用列表收集学生
private List<Student> students;
}
- TeacherMapper接口类
java
public interface TeacherMapper {
//根据id获取老师
Teacher getTeacher(@Param("tid") int id);
//方法二
Teacher getTeacher2(@Param("id") int id)
}
- TeacherMapper.xml输入sql语句
xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.huang.dao.TeacherMapper">
<!--方法一-->
<select id="getTeacher" resultMap="ts">
select * from teacher
</select>
<resultMap id="ts" type="Teacher">
<!--这里javaType是指属性类型,ofType是指泛型类型,column="id"是指要将id列的值作为参数传递给getStudent进行子查询-->
<collection property="students" javaType="ArrayList" ofType="Student" select="getStudent" column="id">
</resultMap>
<select id="getStudent" resultType="Student">
select * from student where tid = #{tid}
</select>
<!--方法二-->
<select id="getTeacher2" resultMap="ts2">
select s.id sid, s.name sname, t.id tid, t.name tname from student s teacher t where t.id=s.tid;
</select>
<resultMap id="ts2" type="Teacher">
<result property="id" column="tid"/>
<result property="name" column="tname"/>
<collection property="students" ofType="Student">
<result property="id" column="sid"/>
<result property="name" column="sname"/>
</collection>
</resultMap>
</mapper>
collection和association之间的区别在于:
- collection是集合,用于一对多的关系,因为一个老师要对应多个学生,所以只能用集合collection,在collection中,要用javaType指出属性,ofType指出泛型
- association是对象,用于多对一的关系,因为多个学生对应一个老师,所以要用对象association,在association中,使用javaType指出属性,而collection是使用ofType
动态sql
在使用JDBC编写sql语句时,面对分页,我们通常要对sql语句进行拼写,当满足一部分时,加入对应的sql语句,满足另外一部分时,加入另外对应的一部分。这对于程序员来说,代码量太大,而且很麻烦,这里mybatis就提供了通过xml文件动态sql拼接
if
xml
<!--1.if语句:如果是,就执行-->
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.huang.dao.BlogMapper">
<select id="getUser" parameterType="map" resultType="blog">
select * from blog where 1=1
<if test="title != null">
and title = #{title}
</if>
<if test="author != null">
and author = #{author}
</if>
</select>
</mapper>
以上就是if语句,当满足第一个条件title不为空,就会返回select * from blog where 1=1 and title = #{title},当满足第二个条件author不为空,就会返回select * from blog where 1=1 and title = #{title} and author = #{author}。
where
注意到这个and,当我们写sql语句时,有时候会省略或者多加and,为此mybatis通过where可以优化匹配and,即where可以自动处理条件语句中的第一个 AND 或 OR 关键字,从而避免 SQL 语法错误。
xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.huang.dao.BlogMapper">
<select id="getUser" parameterType="map" resultType="blog">
select * from blog where 1=1
<where>
<if test="title != null">
<!--这里我们可以直接忽略第一个and-->
title = #{title}
</if>
<if test="author != null">
and author = #{author}
</if>
</where>
</select>
</mapper>
choose和when
choose语句是用来从以下sql语句中选取一项,**只要有一项成立,马上执行,并结束语句。**类似于java中的switch-case语句。
xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.huang.dao.BlogMapper">
<select id="getUser2" parameterType="map" resultType="blog">
select * from blog
<where>
<choose>
<when test="title != null">
title = #{title}
</when>
<when test="author != null">
and author = #{author}
</when>
<otherwise>
and views = #{views}
</otherwise>
</choose>
</where>
</select>
</mapper>
set
set语句主要用于update更新语句
xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.huang.dao.BlogMapper">
<update id="updateUser" parameterType="map">
update blog
<set>
<if test="title != null">
title = #{title}
</if>
<if test="author != null">
author = #{author}
</if>
</set>
where id = #{id}
</update>
</mapper>
sql片段
sql片段可以实现一部分语句复用,只需要使用include refid="sql片段"实现引用即可
xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.huang.dao.BlogMapper">
<sql id="aaa">
<if test="title != null">
title = #{title}
</if>
<if test="author != null">
and author = #{author}
</if>
</sql>
<!--引用sql片段-->
<select id="getUser3" parameterType="map" resultType="blog">
select * from blog
<where>
<include refid="aaa"></include>
</where>
</select>
</mapper>
foreach增强循环动态
为了实现select * from blog where ... and (id=1 or id=2 ...)的sql语句,可以先将其设置为list,再传输到map中,其中id=1为第一个元素,以此类推
xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.huang.dao.BlogMapper">
<select id="getUser4" parameterType="map" resultType="blog">
select * from blog
<where>
<foreach collection="ids" item="id" open="and (" close=")" separator="or">
id = #{id}
</where>
</select>
</mapper>
java
//测试
public class test {
@Test
public void test1() {
SqlSession sqlSession = MybatisUtils.getSqlSession();
BlogMapper blogMapper = sqlSession.getMapper(BlogMapper.class);
//new一个哈希表,用来存储ids这个需要循环的表
HashMap map = HashMap();
ArrayList<Integer> ids = new ArrayList<>();
//加入需要的id
ids.add(1);
ids.add(2);
//存入map
map.put("ids",ids);
List<Blog> blogList = blogMapper.getUser4(map);
for (Blog blog : blogList) {
System.out.prinln(blogList);
}
sqlSession.close();
}
}
缓存
将一次查询的结果,暂存到一个可以直接取到的地方,这个就是缓存
- 缓存是为了分担读写的复杂度,当与内存交互时,我们可以通过缓存进行读取,这样内存只需要进行写的部分即可。
- 缓存可以分为一级缓存和二级缓存
- 缓存的作用在于减少与数据库的交互次数,以此减少开销
一级缓存
- 一级缓存的作用域是sqlSession,即在一次会话中生效,当以下情况出现时,会失效
- 所在insert、update、delete,都会刷新缓存
- 查询不同的东西时,即会更改session
- 查询不同的Mapper.xml
- 手动清理缓存 sqlSession.clearCache();
二级缓存
二级缓存需要手动开启
- 开启方式1
xml
<cache/>
- 开启方式2
xml
<cache
eviction="FIFO"
flushInterval="60000"
size="512"
readOnly=true"">
其中,flushInterval是指刷新次数,size是最大容积, eviction是指缓存策略
重点:
- 二级缓存是基于namespace级别,即是命名空间级别,会在整个Mapper.xml文件中生效
- 机制:
- 一次session,查询一次数据,会被放入一级缓存
- 当session生效时,一级缓存就失效了,这是如果开启了二级缓存,这些数据会自动放入二级缓存中
- 缓存顺序
- 先看二级缓存中有没有
- 再看一级缓存中
- 最后看数据库,如果这是查到数据,会自动进入一级缓存
mybatis-spring
mybatis和spring的集合运用的本质是让Spring的IOC容器接管MyBatis的核心组件生命周期,通过标准化配置实现"数据访问层和业务层解耦、开发效率和可维护性双重提升"
其中,mybatis的核心步骤:
- 读取配置文件mybatis-config.xml构建的SqlSessionFactory
- 通过SqlSessionFactory拿到sqlSession(通过工具类MybatisUtils中的getsession方法)
- 通过sqlSession拿到Mapper接口的动态代理对象(getMapper)
- 通过Mapper接口的动态代理对象执行sql语句(具体方法)
- 关闭sqlSession
而spring集成mybatis,则是通过IOC容器接管了Datasource、SqlSessionFactroy、Mapper代理对象,自动创建管理对象、依赖注入与销毁。
其中,集成的核心步骤:
- 通过SqlSessionFactoryBean来代替mybatis的SqlSessionFactoryBilder,由springIOC容器来初始化创建SqlSessionFactory
- 在工具类MainConfiguration中设置SqlSessionTemplate实现类,通过SqlSessionFactory回去sqlsession,整合事务逻辑管理
- 通过MapperScannerConfiguar自动扫描接口,生成动态代理对象,并注入IOC容器
- 不需要关闭sqlSession,因为sqlsessionTemplate会自动关闭
具体案例
- 接口类TestMapper
java
public interface TestMapper {
//这里通过注解来写sql语句,以省略Mapper.xml
@Select("select * from student where id = #{id}")
Student getStudent(int id);
}
- 实体类Student
java
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Student{
private int id;
private String name;
}
- 配置类MainConfiguration,代替了之前的MybatisUtils工具类,主要用来创建SqlSessionFactory,连接外部配置文件mybatis-config.xml
java
//扫描Mapper接口
@MapperScan("com.huang.dao")
//标记配置类
@configuration
//声明式事务
@EnableTransactionManagement
public class MainConfiguration {
@Bean
public SqlSessionTemplate sqlSessionTemplate() throws Exception {
//创建sqlSessionFactory,连接数据库
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(Resource.getResourceAsStream("mybatis-config.xml"));
//创建sqlSession包装器,代替直接使用sqlsession
return new SqlSessionTemplate(factory);
}
}
- 外部配置文件mybatis-config.xml
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>
<properties resource="db.properties"/>
<!-- 加入日志-->
<settings>
<setting name="logImpl" value="LOG4J"/>
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
<!---->
<!-- 别名优化-->
<!-- <typeAliases>-->
<!-- <typeAlias type="com.huang.pojo.Blog" alias="Blog"/>-->
<!-- </typeAliases>-->
<!--环境配置,可以设定多个环境,但sqlsessionfactory只能选择一种-->
<environments default="d2">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=false&useUnicode=true&characterEncoding=UTF-8"/>
<property name="username" value="root"/>
<property name="password" value="213214"/>
</dataSource>
</environment>
<environment id="d2">
<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>
<!-- 每一个Mapper.xml都需要在Mybatis核心配置文件中注册-->
<mappers>
<mapper class="com.huang.dao.TestMapper"/>
</mappers>
</configuration>
- 测试test(常规业务使用)
java
public class test {
@Test
public void getStudent() {
//获取容器
ApplicationContext context = new AnnotationConfigApplicationContext(MainConfiguration.class);
//由于在配置类中已经扫描了mapper接口,自动将mapper接口注册为Bean,可以直接获取
TestMapper testMapper2 = context.getBean(TestMapper.class);
Student student = testMapper2.getStudent(1);
System.out.println(student);
}
}
- 测试方法2
java
//如果没用在配置类扫描mapper接口
public class test {
@Test
public void getStudent() {
//获取容器
ApplicationContext context = new AnnotationConfigApplicationContext(MainConfiguration.class);
//此时bean中没有mapper接口,通过sqlsessionTemplate获取Mapper
SqlSessionTemplate sqlSessionTemplate = context.getBean(SqlSessionTemplate.class);
TestMapper testMapper = sqlSessionTemplate.getMapper(TestMapper.class);
}
Student student = testMapper.getStudent(2);
System.out.println(student);
}
- 测试方法3,通过自动注入,代替手动创建容器(因为手动创建每次测试都创建新容器),而注入是可重复使用的。
java
//启用spring测试,并创建spring容器
@ExtendWith(SpringExtension.class)
//指定配置类
@ContextConfiguration(classes = MainConfiguration.class)
public class test{
@Autowired
private TestMapper testMapper;
@Test
public void test() {
Student student = testMapper.getStudent(3);
System.out.println(student);
}
}
同时,对于目前的面向注解编程开发,我们也可以在不使用外部配置文件的情况下,实现集成开发。具体步骤就是在配置类MainConfiguration中将数据库连接注册为bean。
在配置类MainConfiguration.java中将数据库连接注册为bean
java
@MapperScan("com.huang.dao")
@Configuration
public class MainConfiguration {
//通过Bean代替xml配置文件
@Bean
public DataSource dataSource() {
return new PooledDataSource("com.mysql.jdbc.Driver",
"jdbc:mysql://localhost:3306/mybatis",
"root","213214");
}
//这里可以使用鬼子的Hikari连接池配置,效率很高,要导入maven依赖,二选一
@Bean
public DataSource dataSource2() {
HikariDataSource dataSource = new HikariDataSource();
dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/mybatis");
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
dataSource.setUsername("root");
dataSource.setPassword("213214");
return dataSource;
}
@Bean
public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
// 创建 SqlSessionFactoryBean 实例
// 作用:创建工厂 Bean 对象,用于构建 SqlSessionFactory
SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
// 设置数据源
// 作用:将 Spring 管理的 DataSource 注入到 MyBatis 中
// 关键:这个 dataSource 参数由 Spring 自动注入(来自 @Bean dataSource() 方法)
bean.setDataSource(dataSource);
return bean.getObject();
}
}