MyBatis-简化JDBC-优秀的持久层框架

MyBatis

111

中文官方文档
Github项目地址
Maven下载地址
学习视频

简介

  • MyBatis 是一款优秀的持久层框架,SQL于代码分离
  • 它支持自定义 SQL、存储过程以及高级映射。
  • 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。
  • 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。
  • MyBatis 本是apache的一个开源项目iBatis, 2010年这个项目由apache software foundation 迁移到了google code,并且改名为MyBatis 。2013年11月迁移到Github。

持久层

  • 持久化:数据持久化
    • 持久化就是将程序的数据在持久状态和瞬时状态转化的过程
    • 内存是断电即失的
    • 数据库(jdbc)、io使文件持久化
  • 持久层:完成持久化工作的代码块
    • Dao层、Service层、Controller层…

MyBatis环境搭建

  1. 创建一个数据库(数据库环境搭建
  2. 新建项目:新建一个普通的maven项目作为父工程
  3. 导入maven依赖:mysql + MyBatis +junit:
        <!--mysql连接数据库-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.49</version>
        </dependency>

        <!--mybatis -->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.5.5</version>
        </dependency>

        <!--单元测试-->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.13</version>
            <scope>test</scope>
        </dependency>
  1. 新建一个子项目
  2. 编写MyBatis核心配置文件resources下新建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核心配置文件-->
<configuration>
    <!--环境:可以配置多套环境-->
    <environments default="development">
        <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/school?useSSL=true&amp;useUnicode=true&amp;characterEncoding=UTF-8}"/>
                <property name="username" value="root"/>
                <property name="password" value="123456"/>
            </dataSource>
        </environment>
    </environments>
<!--每一个Mapper.xml都要注册-->
    <mappers>
        <mapper resource="com/huang/Dao/ClassMapper.xml"/>
    </mappers>
</configuration>
  1. 编写myBatis工具类
//工具类
public class MyBatisUtils {
    private static SqlSessionFactory sqlSessionFactory=null;
    static {
        try {
            //1. 获取SqlSessionFactory对象
            String resource = "mybatis-config.xml";
            InputStream inputStream = Resources.getResourceAsStream(resource);
           sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    //2. 获得sqlsession的实例
    public static SqlSession getSqlSession(){
        return sqlSessionFactory.openSession();
    }
}
  1. 编写代码:
  • 编写实体类
  • 编写Dao接口
public interface ClassDao {
    public List<test> getClassObject();
}
  • 实现接口:实现类由原来的实现类转换为一个mapper配置文件
<?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">
<!--namespace=命名空间-->
<mapper namespace="com.huang.Dao.ClassDao">
<!--select查询语句-->
    <select id="getClassObject" resultType="com.huang.pojo.test">
        select * from school.test2
    </select>
</mapper>
  1. 测试
  • 核心配置文件中注册mappers
  • 可能遇到的问题:
    • 配置文件没有注册
    • 绑定接口错误
    • 方法名不对
    • 返回类型不对
    • Maven导出资源问题
public class DaoTest {
    @Test
    public void test(){
        //获取sqlSession对象
        SqlSession sqlSession = MyBatisUtils.getSqlSession();
        //方法一:getMapper()
        ClassDao mapper = sqlSession.getMapper(ClassDao.class);
        List<test> classObject = mapper.getClassObject();
        for (test test : classObject) {
            System.out.println(test.toString());
        }

		//方法二:
        List<test> classObject2 = sqlSession.selectList("com.huang.Dao.ClassDao.getClassObject");
        for (test test : classObject2) {
            System.out.println(test.toString());
        }
        //关闭sqlSession
        sqlSession.close();
    }
}

CRUD(增删改查)

  • namespace中的包名要和mapper的包名一致
  • 增删改必须要提交事务

Select

  • 选择查询语句
  • id:就是对应的namespace中的方法名
  • resultType:sql语句执行的返回值
  • parameterType:参数类型
  1. 增加接口
    //根据id查询用户
    test getClassById(int id);
  1. 写sql配置文件
<!--根据参数查询-->
    <select id="getClassById" resultType="com.huang.pojo.test" parameterType="int">
        select * from school.test2 where id=#{id}
    </select>
  1. 测试
@Test
    public void testgetClassById() {
        //获取sqlSession对象
        SqlSession sqlSession = null;
        try {
            sqlSession = MyBatisUtils.getSqlSession();
            ClassMapper mapper = sqlSession.getMapper(ClassMapper.class);
            test classObject = mapper.getClassById(1);
            System.out.println(classObject.toString());

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            //关闭sqlSession
            sqlSession.close();
        }
    }

insert

  • 插入数据
  • 插入数据没有返回值
  1. 增加接口
    //插入数据
    int addClass(test t);
  1. 编写sql配置
<!--插入数据-->
    <insert id="addClass" parameterType="com.huang.pojo.test">
        insert into school.test2(class_name) values (#{class_name})
    </insert>
  1. 测试
   @Test
    public void testInsert(){
        //获取sqlSession对象
        SqlSession sqlSession = null;
        try {
            sqlSession = MyBatisUtils.getSqlSession();
            ClassMapper mapper = sqlSession.getMapper(ClassMapper.class);
            mapper.addClass(new test(4,"大一"));

            //提交事务
            sqlSession.commit();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            //关闭sqlSession
            sqlSession.close();
        }
    }

Update

  • 修改数据
  1. 增加接口
//修改数据
    int updateClass(test t);
  1. 编写sql配置文件
<!--修改数据-->
    <update id="updateClass" parameterType="com.huang.pojo.test">
        update school.test2 set class_name=#{class_name} where id=#{id};
    </update>
  1. 测试数据
    @Test
    public void testUpdate(){
        //获取sqlSession对象
        SqlSession sqlSession = null;
        try {
            sqlSession = MyBatisUtils.getSqlSession();
            ClassMapper mapper = sqlSession.getMapper(ClassMapper.class);
            mapper.updateClass(new test(1,"大二"));

            //提交事务
            sqlSession.commit();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            //关闭sqlSession
            sqlSession.close();
        }
    }

delete

  • 删除数据
  1. 编写接口
    //删除数据
    int deleteClass(int id);
  1. 编写sql配置文件
<!--删除数据-->
    <delete id="deleteClass" parameterType="int">
        delete from school.test2 where id=#{id}
    </delete>
  1. 测试
    @Test
    public void testDelete(){
        //获取sqlSession对象
        SqlSession sqlSession = null;
        try {
            sqlSession = MyBatisUtils.getSqlSession();
            ClassMapper mapper = sqlSession.getMapper(ClassMapper.class);
            mapper.deleteClass(4);
            //提交事务
            sqlSession.commit();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            //关闭sqlSession
            sqlSession.close();
        }
    }

Map替代实体类

  • 我们的实体类,或者数据库中的表,字段或参数过多,我们应该考虑用Map

  • map传参数,直接在sql中取出来即可

  • 只有一个基本类型参数的情况下,可以直接在sql中取到

  • 多个参数用Map,或注解

  • 传递的参数靠Map来代替

    //插入数据Map
    int addClass2(Map map);
  • sql注册修改为map,value名称可随意写可以随便写
<!--插入数据-->
    <insert id="addClass2" parameterType="Map">
        insert into school.test2(class_name) values (#{map_class_name})
    </insert>
  • 测试数据
			Map<String, Object> map = new HashMap<String, Object>();
            map.put("map_class_name","哈哈哈哈");
            mapper.addClass2(map);

模糊查询

  • 在Java代码层,传递通配符%
  • 在sql拼接中使用通配符
select * from school.test2 where name like "%"#{id}"%"

配置解析

核心配置文件(mybatis-config.xml)

  • MyBatis的配置文件包含了会影响MyBatis行为的设置和属性信息

configuration(配置)

  • properties(属性)
  • settings(设置)
  • typeAliases(类型别名)
  • typeHandlers(类型处理器)
  • objectFactory(对象工厂)
  • plugins(插件)
  • environments(环境配置)
    • environment(环境变量)
      • transactionManager(事务管理器)
      • dataSource(数据源)
  • databaseIdProvider(数据库厂商标识)
  • mappers(映射器)
环境配置(environments)
  • MyBatis 可以配置成适应多种环境,这种机制有助于将 SQL 映射应用于多种数据库之中
  • 不过要记住:尽管可以配置多个环境,但每个 SqlSessionFactory 实例只能选择一种环境
  • Mybatis默认的事务管理器就是JDBC,连接池:POOLED
属性(properties)
  • 可以通过properties在外部进行配置,并可以进行动态替换
  1. 编写一个配置文件:db.properties
  2. 在核心配置文件中引入
<!--db.properties配置文件引入-->
    <properties resource="db.properties">
<!--可以在这增加属性-->
        <property name="username" value="root"/>
    </properties>
  • 可以直接引入外部配置文件
  • 可以在其中增加一些属性
  • 如果两个都有同一个字段,优先使用外部配置文件
设置(settings)
  • 这是 MyBatis 中极为重要的调整设置,它们会改变 MyBatis 的运行时行为。
  • 查文档:设置
类型别名(typeAliases)
  1. 自定义别名
  • 为 Java 类型设置一个缩写名字。 它仅用于 XML 配置,
  • 意在降低冗余的全限定类名书写。
<!--给类型起别名-->
    <typeAliases>
        <typeAlias type="com.huang.pojo.test" alias="Test"></typeAlias>
    </typeAliases>
  1. 指定包
  • 也可以指定一个包名,MyBatis 会在包名下面搜索需要的 Java Bean
  • 扫描实体类的包,它的默认别名是这个类的类名,首字母小写
<!--给类型起别名-->
    <typeAliases>
        <typeAlias type="com.huang.pojo.test" alias="Test"></typeAlias>
        <package name="com.huang.pojo"/>
    </typeAliases>
  • 实体类比较少的时候,使用第一种方式
  • 实体类十分多,建议使用第二种方式
  • 第一种可以自定义别名,第二种要通过在实体类上加注解注解
@Alias("test")
public class test {}
映射器(mappers)
  • 注册绑定我们的Mapper文件
  1. 方法一:(resource)使用相对于类路径的资源引用
<!--每一个Mapper.xml都要注册-->
    <mappers>
        <mapper resource="com/huang/Dao/ClassMapper.xml"/>
    </mappers>
  1. 方法二:使用映射器接口实现类的完全限定类名
  • 接口和Mapper配置文件必须同名
  • 接口和配置文件必须在同一个包下
    <mappers>
        <mapper class="com.huang.pojo.test"/>
    </mappers>
  1. 方法三:通过package扫描包进行注入绑定
  • 接口和Mapper配置文件必须同名
  • 接口和配置文件必须在同一个包下
    <mappers>
        <package name="com.huang.Dao"/>
    </mappers>

生命周期和作用域

11111

== 理解不同作用域和生命周期类别是至关重要的,因为错误的使用会导致非常严重的并发问题==

  • SqlSessionFactoryBuilder:
    • 一旦创建了 SqlSessionFactory,就不再需要它了
    • 局部变量
  • SqlSessionFactory:
    • 一旦被创建就应该在应用的运行期间一直存在,没有任何理由丢弃它或重新创建另一个实例
    • 可以想象为数据库数据库连接池
    • SqlSessionFactory 的最佳作用域是应用作用域
    • 最简单的就是使用单例模式或者静态单例模式
  • SqlSession:
    • 可以理解为连接到连接池的请求
    • SqlSession 的实例不是线程安全的,因此是不能被共享的,所以它的最佳的作用域是请求或方法作用域。
    • 用完之后赶紧关闭,否则资源会浪费
      123

ResultMap的使用

  • 解决属性名和字段名不一致的问题
  • 数据库中的字段和实体类中的字段命名不一致

解决方法:

  1. 起别名:
        select id class_name as name from school.test2 where id=#{id}

  1. resultMap:结果集映射
    <!--//结果映射-->
    <resultMap id="MyMap" type="Test">
        <!--column数据库中的字段,property实体类中的属性-->
        <result column="id" property="id"/>
        <result column="class_name" property="name"/>
    </resultMap>
    <select id="getClassObject" resultMap="MyMap">
        select * from school.test2
    </select>
  • ResultMap 的设计思想是,对简单的语句做到零配置,对于复杂一点的语句,只需要描述语句之间的关系就行了。
  • ResultMap 的优秀之处——你完全可以不用显式地配置它们:字段名和实体类相同的命名可以不写,只写不同的

日志

日志工厂

  • 如果一个数据库操作出现了异常,需要排错,日志就是最好的助手

  • myBatis提供的日志
    1233333

  • SLF4J

  • LOG4J

  • LOG4J2

  • JDK_LOGGING

  • COMMONS_LOGGING

  • STDOUT_LOGGING :标准日志工厂

  • NO_LOGGING

STDOUT_LOGGING(标准日志工厂)
  • 在myBatis中具体使用哪一个日志实现,在设置中实现
  • 在myBatis核心配置文件中,配置我们的日志
<!--setting设置-->
    <settings>
        <setting name="logImpl" value="STDOUT_LOGGING"/>
    </settings>
LOG4J

Log4j是可以控制日志信息输送的目的地是控制台、文件、GUI组件,甚至是套接口服务器、NT的事件记录器、UNIX Syslog守护进程等;
我们也可以控制每一条日志的输出格式;
通过定义每一条日志信息的级别,我们能够更加细致地控制日志的生成过程。
这些可以通过一个配置文件来灵活地进行配置,而不需要修改应用的代码。

  1. 导入LOG4J的包:在maven的pom文件中添加
<!-- https://mvnrepository.com/artifact/log4j/log4j -->
<dependency>
    <groupId>log4j</groupId>
    <artifactId>log4j</artifactId>
    <version>1.2.17</version>
</dependency>

  1. 在resources目录下,新建log4j.properties配置文件
#将等级为DEBUG的日志信息输出到console和file这两个目的地,console和file的定义在下面的代码
log4j.rootLogger=DEBUG,console,file

#控制台输出的相关设置
log4j.appender.console = org.apache.log4j.ConsoleAppender
log4j.appender.console.Target = System.out
log4j.appender.console.Threshold=DEBUG
log4j.appender.console.layout = org.apache.log4j.PatternLayout
log4j.appender.console.layout.ConversionPattern=【%c】-%m%n

#文件输出的相关设置
log4j.appender.file = org.apache.log4j.RollingFileAppender
log4j.appender.file.File=./log/huang.log
log4j.appender.file.MaxFileSize=10mb
log4j.appender.file.Threshold=DEBUG
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=[%p][%d{yy-MM-dd}][%c]%m%n

#日志输出级别
log4j.logger.org.mybatis=DEBUG
log4j.logger.java.sql=DEBUG
log4j.logger.java.sql.Statement=DEBUG
log4j.logger.java.sql.ResultSet=DEBUG
log4j.logger.java.sql.PreparedStatement=DEBUG
  1. 配置log4j的实现
    <settings>
        <setting name="logImpl" value="LOG4J"/>
    </settings>
  1. log4j的使用
  • 在使用的时候导入包:import org.apache.log4j.Logger
static Logger logger = Logger.getLogger(DaoTest.class);
    @Test
    public void testLog4j(){
        logger.info("info:进入了Log4j的方法");
        logger.debug("debug:进入了debug格式");
        logger.error("error:进入了error方法");
    }

分页

使用Limit分页

select * from 表名 limit 个数;[0,个数]

使用myBatis实现分页(sql层面)

  1. 编写接口
    //分页查询
    List<test> getClassByLimit(Map<String,String> map);
  1. mapper.xml
<!--分页查询getClassByLimit-->
    <select id="getClassByLimit" parameterType="map" resultType="Test" resultMap="MyMap">
        select * from school.test2 limit #{startIndex},#{pageSize}
    </select>
  1. 测试
@Test
    public void testLimit(){
        //获取sqlSession对象
        SqlSession sqlSession = null;
        try {
            sqlSession = MyBatisUtils.getSqlSession();
            ClassMapper mapper = sqlSession.getMapper(ClassMapper.class);

            Map<String, Integer> map = new HashMap<String, Integer>();
            map.put("startIndex",0);
            map.put("pageSize",2);
            List<test> list = mapper.getClassByLimit(map);
            for (test test : list) {
                System.out.println(test.toString());
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            //关闭sqlSession
            sqlSession.close();
        }
    }

RowBounds类实现分页(Java代码层实现)

  • 接口
    List<test> getClassByRowBounds();
  • mapper.xml
    <select id="getClassByRowBounds" resultMap="MyMap">
        select * from school.test2
    </select>
  • 测试
    @Test
    public void testRowBounds(){
        //获取sqlSession对象
        SqlSession sqlSession = null;
        try {
            sqlSession = MyBatisUtils.getSqlSession();
            //方法二:通过java代码层面实现分页
            RowBounds rowBounds = new RowBounds(0, 2);
            List<test> classObject =sqlSession.selectList("com.huang.Dao.ClassMapper.getClassObject",null,rowBounds);

            for (test test : classObject) {
                System.out.println(test.toString());
            }
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            //关闭sqlSession
            sqlSession.close();
        }
    }

插件实现分页(pagehelper)

使用注解开发

  • 像 BlogMapper 这样的映射器类来说,还有另一种方法来完成语句映射。 它们映射的语句可以不用 XML 来配置,而可以使用 Java 注解来配置。
  • 注解开发的本质是利用反射机制实现
  • 底层是动态代理
  1. 在接口上添加注解
    @Select("select * from test2 where id=#{id}")
    test getClassById(int id);
  1. 在核心配置文件上绑定接口
    <mappers>
        <mapper class="com.huang.Dao.ClassMapper"/>
    </mappers>
  1. 测试使用

工具类创建时实现自动事务提交

  • openSession里添加一个true
    public static SqlSession getSqlSession(){
        return sqlSessionFactory.openSession(true);
    }

注解CRUD

  • 有多个参数传递的时候加上@Param

    • 基本类型的参数或者String类型,需要加上
    • 引用类型不需要加
    • 如果只有一个基本类型可以忽略
    • sql中引用的就是我们在这里@Param(“id”)中设置的属性名
  • 关于#{}和${}

    • #{}表示使用预编译sql,可以防止大部分SQL注入
    • ${}使用的拼接字符串的形式生产sql语句,不能防止SQL注入
    • 尽量使用#{}
    @Select("select * from test2 where id=#{id}")
    test getClassById(@Param("id") int id,@Param("name") String name);

myBatis详细实现过程

234

Lombok插件

  • 简化实体类的操作
  1. 在IDEA插件市场中安装Lombok插件
  2. Maven中导入Lombok包
        <!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.12</version>
            <scope>provided</scope>
        </dependency>
  1. 在实体类上加注解
@Getter and @Setter
@FieldNameConstants
@ToString
@EqualsAndHashCode
@AllArgsConstructor,(有参构造)@RequiredArgsConstructor and @NoArgsConstructor(无参构造)
@Log, @Log4j, @Log4j2, @Slf4j, @XSlf4j, @CommonsLog, @JBossLog, @Flogger, @CustomLog
@Data	:无参构造,get,set,toString,hashCode,equals
@Builder
@SuperBuilder
@Singular
@Delegate
@Value
@Accessors
@Wither
@With
@SneakyThrows
@val
@var
experimental @var
@UtilityClass

复杂查询

多对一查询:association

  • 多个学生对应一个老师 :查询学生信息包含老师信息
  • 学生实体类中有一个老师对象
  • 当实体类中不只是有基本类型的数据
  • 复杂属性我们需要单独处理,对象:association,集合:collection
按照查询嵌套处理(子查询)
<--
	思路:
		1. 查询所有的学生信息
		2. 根据学生信息的tid查询老师信息------子查询
-->
<-- 查询学生信息 -->
<selsct id="getStudent" resultMap="myStudent">
	select * from student
</select>

<-- 根据id查询老师信息 -->
<selsct id="getTeacher" resultType="Teacher">
	select * from teacher where id=#{id}
</select>

<-- 用resultMap把学生和老师信息做结果集映射 -->
<resultMap id="myStudent" type="student">
	<-- 相同字段可以不写 -->
	<resule property="name" column="name"/>
	<-- 复杂属性我们需要单独处理,对象:association,集合:collection -->
	<association property="teacher" column="id" javaType="Teacher" select="getTeacher"/>
</resultMap>
按照结果嵌套处理(联表查询)
<--
	思路:
		1. 查询所有的学生信息和老师的信息
-->
<-- 查出所有信息 -->
<selsct id="getStudent" resultMap="myStudent">
	select s.id sid,s.name sname,t.name tname
	from student s,teacher t
	where s.tid = t.id
</select>

<-- 结果集映射 -->
<resultMap id="myStudent" type="Student">
	<result property="id" column="sid">
	<result property="name" column="sname">
	<assoction property="teacher" javaType="Teacher">
		<result property="name" column="tname">
	</assoction>
</resultMap>

一对多查询:collection

  • 一个老师对应多个学习:查询老师信息,对应多个学生
  • 老师实体类中有一个学生对象数组
  • 老师实体类包含属性:id(int),name(String),students(List)
联表查询
  • 接口:
Teacher getTeacher(@Param("id") int id);
  • 配置文件
<-- 查询老师信息 -->
<selsct id="getTeacher" resultMap="myTeacher">
	select s.id sid, s.name sname,t.name tname t.id tid
	from student s,teacher t
	where s.id = t.id and t.id=#{id}
</select>

<-- 结果集映射 -->
<resultMap id="myTeacher" 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">
		<result property="tid" column="tid">
	</collection>
</resultMap>
子查询
  • 接口
Teacher getTeacher(@Param("id") int id);
  • 配置文件
<-- 根据老师信息 -->
<selsct id="getTeacher" resultMap="myTeacher">
	select * from teacher where id=#{id}
</select>

<-- 查询学生信息 -->
<selsct id="getStudent" resultType="Student">
	select * from student where tid=#{id}
</select>

<-- 用resultMap把学生和老师信息做结果集映射 -->
<resultMap id="myTeacher" type="student">
	<-- 复杂属性我们需要单独处理,对象:association,集合:collection -->
	<collection property="students" javaType="ArrayList" ofType="Student" select="getStudent" column="id"/>
</resultMap>

javaType和ofType

  1. javaType用来指定实体类中属性的类型
  2. ofType用来指定映射到List或者集合中的实体类类型,泛型中的约束类型

动态SQL

根据不同条件生产不同SQL语句

if

  • 在后面拼接 AND title like #{title}
  • 如果title不为空就会拼接后面的语句
<select id="findActiveBlogWithTitleLike"
     resultType="Blog">
  SELECT * FROM BLOG
  WHERE state = ‘ACTIVE’
  <if test="title != null">
    AND title like #{title}
  </if>
</select>

choose (when, otherwise)

  • 不想使用所有的条件,而只是想从多个条件中选择一个使用
  • 类似于 Java 中的 switch 语句
<select id="findActiveBlogLike"
     resultType="Blog">
  SELECT * FROM BLOG WHERE state = ‘ACTIVE’
  <choose>
    <when test="title != null">
      AND title like #{title}
    </when>
    <when test="author != null and author.name != null">
      AND author_name like #{author.name}
    </when>
    <otherwise>
      AND featured = 1
    </otherwise>
  </choose>
</select>

trim (where, set)

where

  • where 元素只会在子元素返回任何内容的情况下才插入 “WHERE” 子句
  • 若子句的开头为 “AND” 或 “OR”,where 元素也会将它们去除。
    set
  • set 元素会动态地在行首插入 SET 关键字
  • 并会删掉额外的逗号(这些逗号是在使用条件语句给列赋值时引入的)。

trim

  • 自定义标签:prefix(前缀),prefixOverrides
  • Where标签的trim写法
<trim prefix="WHERE" prefixOverrides="AND |OR ">
</trim>
  • Set标签的trim写法
<trim prefix="SET" suffixOverrides=",">
</trim>

动态SQL本质还是SQL语句,只是可以在SQL层面 ,去执行一个逻辑代码

SQL片段(sql,include)

  • 抽取公共标签,复用SQL语句
<-- 抽取的sql语句 -->
<sql id="my-test-id">
	<if test="title!=null"> and title=#{title} </if>
</sql>

<-- 引用抽取的片段 -->
<include refid="my-test-id"></include>
  • 注意:
    • 最好基于单表来定义SQL片段
    • 不要存在Where标签

foreach

  • 对集合进行遍历(尤其是在构建 IN 条件语句的时候)
  • 元素:
    • item:每一项的名字,例如id
    • index:键
    • collection:集合名
    • open:以什么开始
    • separator:分隔符
    • close:以什么结尾
<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>

可以将任何可迭代对象(如 List、Set 等)、Map 对象或者数组对象作为集合参数传递给 foreach。当使用可迭代对象或者数组时,index 是当前迭代的序号,item 的值是本次迭代获取到的元素。当使用 Map 对象(或者 Map.Entry 对象的集合)时,index 是键,item 是值。

例子:
select * from test where 1=1 and (id =1 or id=2 or id=3)

  • sql语句
<select id="" parameterType="map" resultType="blog">
	select * from test
		<where>
			<foreach collection="ids" item="id" open="and (" close=")" separator="or">
				id=#{id}
			</foreach>
		</where>
</select>
  • 测试查询语句
Map map=new HashMap();
List<Integer> ids =new ArrayList<Integer>();
ids.add(1);
ids.add(2);
ids.add(3);

map.put("ids",ids);
List<blog> blogs =mapper.testForeach(map);

动态SQL就是在拼接SQL语句,我们只要保证SQL的正确性,按照SQL的格式,去排列组合

缓存

  • 存在内存中的临时数据
  • 将用户经常查询的数据放到缓存(内存)中,查询数据就不用从磁盘上(关系型数据库数据文件)查询,从缓存中查询,从而提高查询效率,解决了高并发系统的性能问题
  • 使用缓存可以减少和数据库的交互次数,减少系统开销,提高系统效率
  • 缓存中应该存经常查询并且不经常改变的数据

MyBatis缓存

  • MyBatis包含一个非常强大的查询缓存特性,它可以非常方便的定制和配置缓存,可以极大的提升查询效率
  • myBatis中默认定义两级缓存:一级缓存,二级缓存
    • 默认情况下,只有一级缓存开启(sqlSession级别的缓存,本地缓存)
    • 二级缓存需要手动开启和配置吗,是基于namespace级别的缓存
    • 为了提高扩展性,myBatis定义了缓存接口Cache,我们可以通过实现Cache接口来自定义二级缓存
一级缓存
  • 不用手动配置,默认开启,只在一次session中有效,也就是拿到连接到关闭这个区间段
  • 缓存失效情况:
    • 查询不同的东西
    • 增删改操作,可能会改变原来的数据,所有要刷新缓存
    • 查询不同的Mapper.xml
    • 手动清理缓存:调用 sqlSession.clearCache();方法
二级缓存
  • 二级缓存也叫全局缓存
  • 基于namespace级别的缓存,一个命名空间对应一个二级缓存
  • 工作机制:
    • 一个会话查询一条数据,这个会话就会被防在当前会话的一级缓存中
    • 如果会话关闭,这个会话对应的一级缓存就没了,但是我们想要的是会话关闭了,一级缓存中的数据被保存到二级缓存中
    • 新的会话查询信息,就可以从二级缓存中获取内容
    • 不同的mapper查出的数据会放在对应的缓存(map)中
  • 小结:
    • 只要开启了二级缓存,在同一个Mapper下就有效
    • 所有的数据都会先放在一级缓存中
    • 只有当前会话提交,或者关闭的时候,才会提交到二级缓存

使用步骤

  1. 开启全局缓存(二级缓存),在setting在显示开启
    <settings>
        <setting name="logImpl" value="STDOUT_LOGGING"/>
        <!--显示开启缓存-->
        <setting name="cacheEnabled" value="true"/>
    </settings>
  1. 在要使用二级缓存的mapper.xml中添加cache标签,也可以自定义标签
	<cache/>
	<!--或者-->
    <cache
            eviction="FIFO"
            flushInterval="60000"
            size="512"
            readOnly="true"/>
  1. 要将实体类序列化:基础 implements Serializable 接口
  • 否则会报错:java.io.NotSerializableExcrption

缓存原理

1234

自定义缓存(Ehcache)

  • 现在主要用Redis做缓存

  • 一种广泛使用的开源Java分布式缓存。主要面向通用缓存

  1. 导入jar包:
<!-- https://mvnrepository.com/artifact/org.mybatis.caches/mybatis-ehcache -->
<dependency>
    <groupId>org.mybatis.caches</groupId>
    <artifactId>mybatis-ehcache</artifactId>
    <version>1.2.1</version>
</dependency>
  1. 设置mapper.xml中的type:
<cache type="org.mybatis.caches.ehcache.EhcacheCache"/>
  1. 添加配置文件:ehcache.xml
<?xml version="1.0" encoding="UTF-8"?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd"
         updateCheck="false">

    <diskStore path="./tmpdir/Tmp_EhCache"/>

    <defaultCache
            eternal="false"
            maxElementsInMemory="10000"
            overflowToDisk="false"
            diskPersistent="false"
            timeToIdleSeconds="1800"
            timeToLiveSeconds="259200"
            memoryStoreEvictionPolicy="LRU"/>

    <cache
            name="cloud_user"
            eternal="false"
            maxElementsInMemory="5000"
            overflowToDisk="false"
            diskPersistent="false"
            timeToIdleSeconds="1800"
            timeToLiveSeconds="1800"
            memoryStoreEvictionPolicy="LRU"/>
</ehcache>
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值