mybatis

Mybatis

基础概念

Mybatis是一个针对数据库连接的持久化框架,根据之前学习的JDBC代码,mybatis就是对应的连接Dao层,即持久化层。

具体实现步骤:

  • Dao接口:为了创建具体方法
  • User实现类:实现了是为了写出具体要使用的属性,重写setting等方法
  • 配置类mybatis-config.xml:配置环境(连接那个数据库)、注册mapper(连接到那个映射类)
  • 映射类userMapper.xml:写具体的sql语句,连接到Dao接口
  • 工具类mybatisUtils:获取SqlSessionFactroy仓库,创建getSqlSession方法,可以通过该方法实现sqlSession对象的创建
  • 测试类:使用junit,测试mybatis的具体实现

创建第一个mybatis

  1. 创建工具类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();
    }
}
  1. 创建配置类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&amp;useUnicode=true&amp;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>
  1. User实现类,用来写出属性
java 复制代码
public class User {
     public int id;
    public String name;
    public String pwd;
    //忽略setting、getting、tostring及有参无参构造
}
  1. 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);
}
  1. 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>
  1. 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和实现分页

  1. 映射sql,以上我们是直接通过数据库中的表名进行命名的,但如果数据库中的表名和我们实际中的名字不同,该如何,比如:数据库中为id,实际为userid,使用映射resultMap
  2. 实现分页:之前使用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语句。

  1. User实现类
java 复制代码
//其中,@Data是实现了无参构造、getting、setting、toStrin等的注解
//@AllArgsConstructor则是实现有参构造,同时如果Data同时存在,会自动覆盖无参构造
//@NoArgsConstructor是再次实现无参构造,这时有参和无参才会同时存在
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User{
    public int id;
    public String name;
    public String pwd;
}
  1. 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对其进行查询,就先要明确联表之间的关系。

  • 多对一和一对多关系是基于不同视角进行评判的,比如一个老师教多个学生,在学生视角就是多对一,即多个学生被一个老师教。而在老师视角,就是一个老师教多个学生
  1. 在进行案例说明之前,先在数据库中创建出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,"小黑");
  1. 现在创建出两个表,其中student表中有一个tid外键,用来连接teacher表,即从学生视角出发,多对一。
  2. 创建实体类
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;
}
  1. 创建接口类
java 复制代码
//StudentMapper
public interface StudentMapper {
    //联表查询1:先查询学生信息,再通过映射子查询老师信息
    List<Student> getUser();
    //联表查询2:先查询,后统一映射
    List<Student> getUser();
}
  1. 配置类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&amp;useUnicode=true&amp;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>
  1. 创建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>
  1. 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对象查询老师,进行联表

一对多

从老师视角进行联表查询,即从数据量少的表连接到数据量多的表

  1. teacher实现类
java 复制代码
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Teacher {
    private int id;
    private String name;
    //一个老师对应多个学生,这里要使用列表收集学生
    private List<Student> students;
}
  1. TeacherMapper接口类
java 复制代码
public interface TeacherMapper {
    //根据id获取老师
    Teacher getTeacher(@Param("tid") int id);
    //方法二
    Teacher getTeacher2(@Param("id") int id)
}
  1. 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. 开启方式1
xml 复制代码
<cache/>
  1. 开启方式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会自动关闭

具体案例

  1. 接口类TestMapper
java 复制代码
public interface TestMapper {
    //这里通过注解来写sql语句,以省略Mapper.xml
    @Select("select * from student where id = #{id}")
    Student getStudent(int id);
}
  1. 实体类Student
java 复制代码
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Student{
    private int id;
    private String name;
}
  1. 配置类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);	
    }
}
  1. 外部配置文件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&amp;useUnicode=true&amp;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>
  1. 测试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);
    }
}
  1. 测试方法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);
}
  1. 测试方法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();
    }
}
相关推荐
喵手2 小时前
项目实战案例:从设计到部署!
java·部署·项目实战·设计
源码获取_wx:Fegn08952 小时前
基于springboot + vue小区人脸识别门禁系统
java·开发语言·vue.js·spring boot·后端·spring
youngee112 小时前
hot100-61电话号码的字母组合
java·数据结构·leetcode
AI Echoes2 小时前
自定义 LangChain 文档加载器使用技巧
数据库·人工智能·python·langchain·prompt·agent
寂寞旅行2 小时前
java敏感词过滤(sensitive-word)
java·开发语言·word
90后小陈老师2 小时前
Java项目接入AI大模型的四种方式
java·开发语言·人工智能
hunjinYang2 小时前
使用嵌入式 Tomcat 创建Java Web应用程序
java·tomcat
在风中的意志2 小时前
[数据库SQL] [leetcode] 578. 查询回答率最高的问题
数据库·sql
liuc03172 小时前
AI下调用redis并调用deepseek
数据库·redis·mybatis