Mybatis

项目中常用

对于日期

java中日期属性使用String类型也可以插入Msyql (Mybatis做了转换)

 

插入和更新操作的返回值

大概吧,忘记了

insert:   插入n条记录,返回影响行数n。(要么成功,要么失败抛出异常)

update:更新n条记录,返回影响行数n。(n>=0)

delete: 删除n条记录,返回影响行数n。(n>=0)

 

打印sql语句插件

mybatis log free

需开启设置

 

 

 

 

 

 

插入

单条插入

    <insert id="createEmployee">
        insert into emploee
        values (
            null,
            #{employee.name},
            #{employee.age},
            #{employee.inductionDate},
            #{employee.quitDate},
            #{employee.identityDesc},
            #{employee.statusFlag})
    </insert>

批量插入

方式一:原生sql方式

 xml使用原生sql结合mybatis动态sql

    int batchCreateEmployee(@Param("empList") List<Employee> employees);

 

    <!--    批量插入-->
    <insert id="batchCreateEmployee">
        insert into emploee
        values
        <foreach collection="empList" item="emp" separator=",">
            (null,
             #{emp.name},
             #{emp.age},
             #{emp.inductionDate},
             #{emp.quitDate},
             #{emp.identityDesc},
             #{emp.statusFlag})
        </foreach>

sql语句过长会报错(项目中一般不超过100条),这个由数据库决定,过长时可以对list集合进行拆分,再循环调用批量插入

 

 

方式二:使用JDBC批处理

参考:https://cloud.tencent.com/developer/article/1825993

   @Autowired
    private SqlSessionFactory sqlSessionFactory;
    //批处理
    @Transactional
    public void add(List<Item> itemList) {
        SqlSession session = sqlSessionFactory.openSession(ExecutorType.BATCH,false);
        ItemMapper mapper = session.getMapper(ItemMapper.class);
        for (int i = ; i < itemList.size(); i++) {
            mapper.insertSelective(itemList.get(i));
            if(i%1000==0){//每1000条提交一次防止内存溢出
                session.commit();
                session.clearCache();
            }
        }
        session.commit();
        session.clearCache();
    }

 

插入操作设置返回主键

useGeneratedKeys:
对于支持自动生成记录主键的数据库,如:MySQL,SQL Server,此时设置useGeneratedKeys参数值为true,在执行添加记录之后可以获取到数据库自动生成的主键ID。
实际上,在settings元素中设置useGeneratedKeys是一个全局参数,但是只会对接口映射器产生影响,对xml映射器不起效。
 
keyProperty指向POJO属性
 
执行一条语句,自动返回id到POJO属性上
<!--<insert id="addTeacher" useGeneratedKeys="true" keyProperty="teacherId" parameterType="Teacher">
    INSERT INTO teacher(teacherId, teacherName) VALUES(teacherId,#{teacherName})
</insert>-->
或者 (只要能自动生成就能获取)
INSERT INTO teacher(teacherName) VALUES(#{teacherName})
或者指明 keyColumn ——对应表id字段
<insert id="addTeacher" useGeneratedKeys="true" keyProperty="teacherId" parameterType="Teacher" keyColumn="teacherId">
    INSERT INTO teacher(teacherName) VALUES(#{teacherName})
</insert>

测试

@Test
public void addTeacher() {
    SqlSession sqlSession=null;
    try {
        sqlSession= MybatisUtils.getSqlSession();
        TeacherMapper teacherMapper=sqlSession.getMapper(TeacherMapper.class);
       Teacher teacher=new Teacher().builder()
               .teacherName("隔壁老王")
               .build();
        System.out.println("之前:"+teacher);
       teacherMapper.addTeacher(teacher);
        System.out.println("之后:"+teacher);
    }catch(Exception e){
        e.printStackTrace();
    }finally{
        MybatisUtils.closeSqlSession();
    }
}

结果

 

更新

批量更新

注意

  1.列名不加单引号,若要区分敏感字符可以加反撇号,不然会导致语法错误

  2.数据库连接要增加参数设置allowMultiQueries=true

 

 

 

jdbc:mysql://localhost/java_base?serverTimezone=GMT%2B8&characterEncoding=utf8&useUnicode=true&allowMultiQueries=true

 

 

 

    int batchUpdateEmployee(@Param("empList") List<Employee> employees);

 

    <!--    批量更新-->
    <update id="batchUpdateEmployee">
        <foreach collection="empList" item="emp" separator=";">
            update emploee
            set induction_date = #{emp.inductionDate}
            where name = #{emp.name}
        </foreach>
    </update>

 

 Mybatis参数传递的几种方式

匿名参数 顺序传递

xml中按照参数传递顺序,使用#{arg0}  , #{arg1} 或者#{param1},#{param2}来获取传递参数

List result= employeeMapper.selectByGenderAndAge(gender,age)

 

xml中

<select id="selectByGenderAndAge" resultMap="BaseResultMap" >
    select *  from employee where gender = #{param1} and age = #{param2}
  </select>

 

@param注解 (一般用这种)

一般该方式结合javaBean和List集合

UserInfo selectByUserIdAndStatus(@Param("userId") String userId,  @Param("status") Integer status);

 

xml中

<select id="selectByUserIdAndStatus" resultType="cn.cb.demo.domain.UserInfo">
     select * from user_info where user_id=#{userId} and status=#{status}
</select>

 

一般用对象封装参数也是结合@param注解

 

 

Map对象封装

List result= employeeMapper.selectByMapParams(params);

按照key名称来取

<select id="selectByMapParams" resultMap="BaseResultMap" parameterType="map">
  select * from employee where gender = #{gender} and age = #{age}
</select>

 

JSON对象封装

@ApiOperation(value = "多个参数查询_通过JSON传递多个参数")
@PostMapping("findByJSONObject")
public ResultMsg findByJSONObject(@RequestBody JSONObject params)
{
    List result= employeeMapper.findByJSONObject(params);
    return ResultMsg.getMsg(result);
}
parameterType为JSONObject
按键名来取
<select id="findByJSONObject" resultMap="BaseResultMap" parameterType="com.alibaba.fastjson.JSONObject">
  select
  *
  from employee where gender = #{gender} and age = #{age}
</select>

 

SQL可以放在哪?

  • 常见的xml里放sql
  • Mapper接口上使用注解方式放sql

引用:https://blog.csdn.net/weixin_44411569/article/details/93468943

 

 

Mybatis缓存机制

用来减少对数据库查询操作的次数。性能会有提升
MyBatis缓存

  1. 一级缓存(默认)
  2. 二级缓存

一级缓存

一级缓存的概念

  • 一级缓存基于SqlSession级别 (存储的数据只在sqlsession内有效)
  • 只有SqlSession关闭、提交、或flush,一级缓存消失( 否则重复同一条sql只会输出一次sql) (commit方法带有刷新)
  • 干预一级缓存(强制将对象从一级缓存中移除)(sqlsession.clearCache清空缓存后再查会再次输出sql)
  • 只要执行了增删改操作,一级缓存消失 (缓存替代 查-改-查会输出两次select的sql)
  • 内容相同,但是执行sql的id不同,不算同一条语句 会输出两次sql (缓存的Map和namespace、id值(方法名)有关)

 

一级缓存内存结构

 

 

 

从上面的内存图得知:一级缓存实际缓存的是HashMap。这个Map中的Key主要的组成部分是映射文件中namepace属性值<select>标签中的id值和具体sql语句拼接而成,valuse则是一个Arraylist,存放查询出来的对象

 

 

二级缓存

二级缓存是mapper映射级别的缓存,多个SqlSession去操作同一个Mapper映射的sql语句,多个SqlSession可以共享二级缓存,二级缓存是跨SqlSession的

 

 

映射语句文件中的所有 select 语句的结果将会被缓存。
映射语句文件中的所有 insert、update 和 delete 语句会刷新缓存。
缓存会使用最近最少使用算法(LRU, Least Recently Used)算法来清除不需要的缓存。
缓存不会定时进行刷新(也就是说,没有刷新间隔)。
缓存会保存列表或对象(无论查询方法返回哪种)的 1024 个引用。
缓存会被视为读/写缓存,这意味着获取到的对象并不是共享的,可以安全地被调用者修改,而不干扰其他调用者或线程所做的潜在修改。

作者:AC编程
链接:https://www.jianshu.com/p/003a449cd8e2
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

 

先配置文件中开启总开关

<setting name="cacheEnabled" value="true"/>

 

再Mapper中或者Mapper接口上添加也行

<mapper namespace="com.bjlemon.mybatis.mapper.EmployeeMapper">
   <!--开启二级缓存的支持-->
   <cache type=""/>
</mapper>

 

在接口上添加

@CacheNamespace

后面可以带参数指定加载自定义的缓存实现类

@CacheNamespace(implementation = RedisCache.class)//开启二级缓存

 

使用

useCache控制二级缓存
<select id="findAll" resultMap="EmployeeResultMap" useCache="true">
  select
  *
  from
  mybatis_employee
</select>

 

自定义缓存

1.与Ecache整合

映射语句文件中的所有 select 语句的结果将会被缓存。
使用Ehcache
两个依赖
   org.ehcache
   ehcache
   3.8.1

   org.mybatis
   mybatis-ehcache
   1.0.0

将ehcache核心库中一个ehcache-xxx.xml文件拷贝一份到类路径下,一般修改为ehcache.xml

  • 修改配置(一般使用默认)
  • 开启二级缓存

 

1.config.xml里面开启二级缓存

<setting name="cacheEnabled" value="true"/>

2.mapper.xml里面开启Ehcache

<cache type="org.mybatis.caches.ehcache.EhcacheCache"/>


3.mapper.xml方法上开启

<select id="selectByID" resultType="user" useCache="true">

 

复制文件报错解决办法

 分页

1.直接sql中limit分页  物理分页

2.RowBounds 一次性加载所有服务查询条件的目标数据 根据分页参数的值再去内存中实现分页 滚动加载数据 不适合数据量较大场景 频繁访问数据库  内存级分页

3.拦截器实现 拦截需要分页的select语句 然后动态拼接分页关键字  PageHelper  Mybatis-Plus 已经实现

 

 $与#占位符传参的区别

在预编译之前#{}解析为JDBC的预编译语句(preperd statement)的参数标记符 "?"

${} 在sql解析阶段会进行变量替换,不能实现预编译,${xxx} 这样格式的参数会直接参与sql编译导致可能出现sql注入 

-》使用${} 不会进行预编译,传参会参与编译过程,可能导致sql注入

使用#{}会进行预编译,而且预编译能够防止sql注入

什么是预编译

数据库接收到sql之后需要进行词法和语义解析、优化sql、指定执行计划等会花费时间,但是很多时候一条sql语句可能会反复执行,或者只是个别参数不同,为了减少编译,就有了预编译。

预编译就是使用占位符替代将sql语句模板化,一次编译多次运行,省去了解析优化等过程

缓存预编译

预编译语句被DB的编译器编译后的执行代码被缓存起来,name下次调用时只要是相同的预编译语句就不需要编译,只要将参数直接传入编译过的语句的执行代码中,相当于函数,就会执行

预编译实现

基于preperdStatement和占位符来实现

 

 

 

 

 

 

 

 

 

 

 

posted on 2023-02-26 22:19  or追梦者  阅读(46)  评论(0编辑  收藏  举报