MyBatis入门到"入坟"
侧边栏壁纸
  • 累计撰写 61 篇文章
  • 累计收到 18 条评论

MyBatis入门到"入坟"

龙流
2022-06-01 / 0 评论 / 68 阅读 / 正在检测是否收录...
温馨提示:
本文最后更新于2022年08月31日,已超过603天没有更新,若内容或图片失效,请留言反馈。
mybatis它内部封装了jdbc,简化了原始持久层操作每次都要去处理加载驱动、创建连接、创建statement等繁杂的过程;
上面这些操作,JDBCTemplate工具类也实现了,为什么JDBCTemplate不叫框架,而是工具类呢?原因是工具类只是对操作的一些封装,对于更多的细节它并没有去处理和提供解决方案。
定制化SQL、存储过程、高级映射
相较jdbc,SQL代码夹杂在Java代码中耦合度高不易维护
相较Hibernate和JPA
自动生成SQL,不容易做特殊优化
反射太多,导致数据库性能下降

mybatis的简单搭建

  1. 打开idea

142-01.png

  1. 创建一个空的maven项目,导入mybatis和数据库MySQL的maven依赖

142-02.png

  1. 配置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">
<!-- mybatis的主配置文件 -->
<configuration>
    <!-- 配置环境 -->
    <environments default="mysql">
        <!-- 配置mysql的环境-->
        <environment id="mysql">
            <!-- 配置事务的类型-->
            <transactionManager type="JDBC"></transactionManager>
            <!-- 配置数据源(连接池) -->
            <dataSource type="POOLED">
                <!-- 配置连接数据库的4个基本信息 -->
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/eesy_mybatis"/>
                <property name="username" value="root"/>
                <property name="password" value="1234"/>
            </dataSource>
        </environment>
    </environments>

    <!-- 指定映射配置文件的位置,映射配置文件指的是每个dao独立的配置文件 -->
    <mappers>
        <mapper resource="com/lwlong/dao/IUserDao.xml"/>
    </mappers>
</configuration>

顺便配置一下日志

# Set root category priority to INFO and its only appender to CONSOLE.
#log4j.rootCategory=INFO, CONSOLE            debug   info   warn error fatal
log4j.rootCategory=debug, CONSOLE, LOGFILE

# Set the enterprise logger category to FATAL and its only appender to CONSOLE.
log4j.logger.org.apache.axis.enterprise=FATAL, CONSOLE

# CONSOLE is set to be a ConsoleAppender using a PatternLayout.
log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
log4j.appender.CONSOLE.layout.ConversionPattern=%d{ISO8601} %-6r [%15.15t] %-5p %30.30c %x - %m\n

# LOGFILE is set to be a File appender using a PatternLayout.
log4j.appender.LOGFILE=org.apache.log4j.FileAppender
log4j.appender.LOGFILE.File=d:\axis.log
log4j.appender.LOGFILE.Append=true
log4j.appender.LOGFILE.layout=org.apache.log4j.PatternLayout
log4j.appender.LOGFILE.layout.ConversionPattern=%d{ISO8601} %-6r [%15.15t] %-5p %30.30c %x - %m\n
  1. 创建映射文件mapper
<?xml version="1.0" encodeing="UTF-8"?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.lwlong.dao.IUserDao">

  <!--查询所有用户-->
  <select id="findAll" resultType="com.lwlong.entity.User">
    select * from user
  </select>

</mapper>
  1. MyBatis的使用案例
public static void main(String[] args)throws Exception {

  //1.读取配置文件
  InputStream is = Resource.getResourceAsStream("SqlMapConfig.xml");
  //2.创建SqlSessionFactory工厂
  SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
  SqlSessionFactory factory = builder.build(is);
  //3.使用工厂生产SqlSession对象
  SqlSession sqlSession = factory.openSession();
  //4.使用SqlSession创建Dao(Mapper)接口的代理对象
  IUserDao userDao = sqlSession.getMapper(IUserDao.class);
  //5.使用代理对象执行方法
  List<User> users = userDao.findAll();
  for(User user : users) {
    System.out.println(user);
  }
  //6.释放资源
  sqlSession.close();
  is.close();
}

MyBatis细节处理解决方案

MyBatis获取参数的几种方式

1、通过#{}方式,原理是占位符的形式,可以防止SQL注入,替换的参数默认使用''包围
2、通过${}方式,原理字符串拼接形式,存在SQL注入风险,传入的参数是什么就直接替换到SQL语句中
3、Mapper的代理实现类,mapper接口方法的参数,底层对传入的参数是以Map的形式封装,默认有键为parma1,parma2 ...;
在mapper.xml文件中sql的#{}、${}通过属性名获取,①使用java对象传递,对象的属性;②使用多个参数,mapper接口中参数设置@Param注解value值;③通过位置形式获取;④通过map

通过${}方式,动态替换表名
标签中设置useGeneratedKeys="true"开启获取自增的主键,seyProperty="对象属性名"设置回写到对象属性中

全局配置处理字段名和属性名不一致的情况

在mybatis-config.xml配置文件中添加如下配置

<settings>
  <!--将下划线映射为驼峰->
  <setting name="mapUnderscoreTocamelcase" value="true"/>
</settings>

使用resultMap处理属性名与字段名不一致的情况

在Mapper.xml映射SQL字段,使用标签,设置

<resultMap id="empResultMap" type="com.xxx.Emp"><!--如果配置了mybatis全局别名配置,可以直接写默认简称别名-->
  <result property="empId" column="emp_id" jdbcType="int"/>
  <result property="empName" column="emp_name" jdbcType="String"/>
  <result property="age" column="age" jdbcType="int"/>
</resultMap>

也用来处理封装级联对象属性:关联查询中如员工信息关联部门信息,分别封装在对应的实体类中
使用association来处理封装级联对象属性

<association property="dept" javaType="Dept">
  <id column="dept_id" property="deptId"></id>
  <result column="dept_name" property="deptName"></result>
</ association>
> MyBatis处理多对一的映射关系查询,使用分步的查询方式;缺点:写的查询语句多;优点:可以延迟加载
> 必须在全局配置文件中设置全局配置信息
> lazyLoadingEnabled:延迟加载的全局开关。当开启时,所有关联对象都会延迟加载
> aggressiveLazyLoading:当开启时,任何方法的调用都会加载该对象的所有属性。否则,每个属性会按需加载
> 此时就可以实现按需加载,获取的数据是什么,就只会执行相应的sql。此时可通过association和collection中的fetchType属性设置当前的分步查询是否使用延迟加载, fetchType="lazy(延迟加载)|eager(立即加载)"

mybatis动态SQL

Mybatis框架的动态SQL技术是一种根据特定条件动态拼装SQL语句的功能,它存在的意义是为了解决拼接sQL语句字符串时的痛点问题。I

标签的使用:

if

if标签可通过test属性的表达式进行判断,若表达式的结果为true,则标签中的内容会执行;反之标签中的内容不会执行
<!--List<Emp> getEmpListByCondition(Emp emp);-->
<select id="getEmpListByMoreT" resultType="Emp">
  select * from t_emp where 1=1
  <if test="ename != '' and ename != nu11">
    and ename = #{ename}
  </if>
  <if test="age != '' and age != nu11">
    and age = #{age}
  </if>
  <if test="sex != '' and sex != nu11">
    and sex = #{sex}
  </if>
</select>

where

where标签,可以自动生成where关键字,可以自动去除条件字段前的and关键字,不能去除条件字段后的and的关键字;

trim

trim标签属性
prefix、suffix:在标签中内容前面或后面添加指定内容
prefixOverrides、suffixOverrides:在标签中内容前面或后面去掉指定内容
<select id="getEmpListByMoreT" resultType="Emp">
  select * from t_emp
  <trim prefix="where" suffixoverrides="and">
  <if test="ename != '' and ename != nu11">
    ename = #{ename} and
  </if>
  <if test="age != '' and age != nu11">
    age = #{age} and
  </if>
  <if test="sex != '' and sex != nu11">
    sex = #{sex} and
  </if>
  </trim>
</select>

choose、when、otherwise

多条件判断组合标签,相当于java中的if...else if...else

forEach

试用在批量添加、批量删除,非常常用的标签
<!--void insertMoreEmp(@param ( " emps" ) List<Emp> emps);-->
<insert id="insertMoreEmp">
  insert into t_emp values
  <foreach collection="emps" item="emp" separator=",">
    (null,#{emp. empName} ,#{emp.age} ,#{emp.gender},null)
  </foreach>
</insert>

<!--void deleteMoreEmp(@param ( "empIds" ) Integer[] empIds);-->
<delete id="deleteMoreEmp">
  delete from t_emp where 
  <foreach collection="empIds" item="empId" separator="or">
    emp_id = #{empId}
  </foreach>
</delete>

sql

sql标签,定义一个sql片段,例如:在mybatis中建议不要写select * ...因为在执行时还是要去查找一遍具有的字段;也可以定义存储过程;
<sql id="empcolumns">
  emp_id,emp_name,age,gender,dept_id
</sql>
<!--然后在select标签中,写查询语句时,使用<include refid="片段id">标签引入sql片段-->
<select id="getEmpListByMoreT" resultType="Emp">
  select <include refid="empcolumns"></include> from t_emp
</select>

MyBatis的缓存

MyBatis的一级缓存(默认开启的)

一级缓存是SqlSession级别的,通过同一个SqlSession查询的数据会被缓存,下次查询相同的数据,就会从缓存中直接获取,不会从数据库重新访问
使一级缓存失效的四种情况:|
1)不同的SqlSession对应不同的一级缓存
2)同一个SqlSession但是查询条件不同
3)同一个SqlSession两次查询期间执行了任何一次增删改操作
4)同一个SqlSession两次查询期间手动清空了缓存
@Test
public void testGetEmpById(){
  SqlSession sqlSession1 = SqlSessionUtil.getSqlSession();
  CacheMapper mapper1 = sqlSession1.getMapper(CacheMapper.class);
  Emp emp1 = mapper1.getEmpById(1);
  System.out.println(emp1);
  Emp emp2 = mapper1.getEmpById(1);
  System.out.println(emp2);
  SqlSession sqlSession2 = SqlSessionUtil.getSqlSession();
  CacheMapper mapper2 = sqlSession2.getMapper(CacheMapper.class);
  Emp emp3 = mapper2.getEmpById(1);
  System.out.println(emp3);
}

l7hrmpf7.png

MyBatis的二级缓存

二级缓存是SqlSessionFactory级别,通过同一个SqlSessionFactory创建的SqlSession查询的结果会被缓存;此后若再次执行相同的查询语句,结果就会从缓存中获取
二级缓存开启的条件:
a>在核心配置文件中,设置全局配置属性cacheEnabled="true",默认为true,不需要设置
b>在映射文件中设置标签<cache/>
c>二级缓存必须在SqlSession关闭或提交之后有效
d>查询的数据所转换的实体类类型必须实现序列化的接口
使二级缓存失效的情况:
两次查询之间执行了任意的增剧改,会使一级和二级缓存同时失效
0

评论 (0)

取消