1.占位符问题

mybatis3.4.1 之前版本版本,可以使用数字来占位

@Select("select * from t_user where user_name = #{0} and user_age = #{1}")
User getByNameAndAge(String userName,String age);

mybatis3.4.2 版本就换为 #{param1} #{param2} #{paramn..} 来映射 或者 使用参数名称,需要使用 @param 进行映射。使用 @param("name") 才可以使用 #{name} 单个参数

Typehandle getWithId(@Param("id") Integer id);

MyBatis 中使用 # 和 $ 书写占位符有什么区别? # 将传入的数据都当成一个字符串,会对传入的数据自动加上引号;会预编译。sql 安全不会注入。

$ 将传入的数据直接显示生成在 SQL 中。 注意:使用 $ 占位符可能会导致 SQL 注入攻击,能用#的地方就不要使用 $,写 order by 子句的时候应该用​$而不是 #。

2.驼峰问题

springboot 配置可以直接加配置

mybatis.configuration.map-underscore-to-camel-case=true

但是如果有整合 mybtais-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>
    <!-- 配置mybatis的缓存,延迟加载等等一系列属性 -->
    <settings>
        <!--启用log4j日志-->
        <setting name="logImpl" value="LOG4J"/>
        <!-- 全局映射器启用缓存 -->
        <setting name="cacheEnabled" value="true"/>
        <!-- 查询时,关闭关联对象即时加载以提高性能 -->
        <setting name="lazyLoadingEnabled" value="true"/>
        <!-- 设置关联对象加载的形态,此处为按需加载字段(加载字段由SQL指 定),不会加载关联表的所有字段,以提高性能 -->
        <setting name="aggressiveLazyLoading" value="false"/>
        <!-- 对于未知的SQL查询,允许返回不同的结果集以达到通用的效果 -->
        <setting name="multipleResultSetsEnabled" value="true"/>
        <!-- 允许使用列标签代替列名 -->
        <setting name="useColumnLabel" value="true"/>
        <!-- 给予被嵌套的resultMap以字段-属性的映射支持 -->
        <setting name="autoMappingBehavior" value="FULL"/>
        <!-- 对于批量更新操作缓存SQL以提高性能 -->
        <setting name="defaultExecutorType" value="REUSE"/>
        <!-- 数据库超过25000秒仍未响应则超时 -->
        <setting name="defaultStatementTimeout" value="25000"/>
        <!-- 自动转换驼峰命名 -->
        <setting name="mapUnderscoreToCamelCase" value="true"/>
    </settings>
    
     <typeAliases>
        <typeAlias alias="Typehandle" type="com.entity.Typehandle"/>
    </typeAliases>
</configuration>

3.mybatis结果集映射

注意:前者有构造方法的,对应实体类要实现构造方法,并且顺序构造方法参数一致。如果使用lombok要加上 @AllArgsConstructor

PS:同时使用 @Data 和 @AllArgsConstructor 后 ,默认的无参构造函数失效,如果需要它,要重新设置 @NoArgsConstructor

1558598580845.png

4.插入获取主键 ID

无事务控制
  • xml useGeneratedKeys="true" keyProperty="id" keyColumn="id"

    <insert id="save" parameterType="User"
    useGeneratedKeys="true" keyProperty="id" keyColumn="id">
    		insert into t_user(user_name,user_age) values(#{userName},#{age})
    </insert>
    
  • 注解形式

     @Insert("insert into Demo(name) values(#{name})")  
     @Options(keyProperty="id",keyColumn="id",useGeneratedKeys=true)  
     public void save(Demo demo); 
    

获取方式,注解 ID 会注入到入参对象中,可以直接 get 方法获取。

有事务控制

如果有事务控制如 @Transaction 无法通过以上方式获取,要先查询出来下个数据库自增id

select max(id)+1 from table;

5.嵌套 Select 查询

一般我们使用都是单表操作,多表都是手写 sql 进行关联查询,进行映射。这些都是一对一的情景。 一对一中,我们定义实体类型如果是复杂类型。

  • 首先是订单类:

    public class Orders {  
        private Integer id;  
        private Integer userId;  
        private String number;  
        private Date createtime;  
        private String note;  
        private User user;  
        //getter、setter  
    }  
    
  • 用户信息类:

    public class User {  
        private int id;  
        private String username;  
        private int sex;  
        private Date birthday;  
        private String address;  
        //getter、setter  
    }  
    
  • 构建 resultMap 的时候,可以指定关联 association 的配置

    <resultMap type="com.ssm.mybatis.po.Orders" id="selectOrderUserLazyLoading">  
            <!-- 配置映射的订单信息 -->    
            <id column="id" property="id"/>  
            <result column="user_id" property="userId"/>  
            <result column="number" property="number"/>  
            <result column="createtime" property="createtime"/>  
            <result column="note" property="note"/>  
      
            <!-- 配置映射的用户信息 -->  
            <association property="user" javaType="com.ssm.mybatis.po.User" select="com.ssm.mybatis.mapper.UserMapper.findUserById" 		column="user_id" fetchType="lazy">                   
            </association>  
     </resultMap>
    
    <select id="selectOrderUserLazyLoading" resultMap="OrderUserLazyLoadingResultMap" >  
            select * from orders  
    </select>
    

注意:如果两表联查,主表和明细表的主键都是id的话,明细表的多条只能查询出来第一条。

解决方案:级联查询的时候,主表和从表有一样的字段名的时候,在 mysql 上命令查询是没问题的。但在 mybatis 中主从表需要为相同字段名设置别名。设置了别名就 OK 了。

6.嵌套结果映射

使用嵌套的结果映射来处理连接结果的重复子集。一个复杂的数据结构包含集合的情况,恶心点的做法是先查出上层数据,再根据 id 迭代获取子结果注入到结果集中。这里使用高级映射来实现。

  • entity

    public class Department {
        private Integer id;
        private String name;
        private List<Employee> employees;
     }
    
  • Mapper

    public Department getDepartmentByIdPlus(Integer id);
    
  • XML

    <!-- 嵌套结果集的方式-->
            <!--public Department getDepartmentByIdPlus(Integer id);-->
            <resultMap id="myDept" type="com.stayreal.mybatis.Department">
                <id column="did" property="id"/>
                <result column="dept_name" property="name"/>
                <!-- collection定义关联集合类型的属性封装规则
                offType:指定集合中的元素类型
                -->
                <collection property="employees" ofType="com.stayreal.mybatis.Employee">
                    <id column="eid" property="id"/>
                    <result column="last_name" property="lastName"/>
                    <result column="email" property="email"/>
                    <result column="gender" property="gender"/>
                </collection>
            </resultMap>
            <select id="getDepartmentByIdPlus"  resultMap="myDept">
                select d.id did,d.dept_name dept_name,e.id eid,e.last_name last_name,
                e.email email,e.gender gender
                from tbl_dept d
                left JOIN tbl_employee e on d.id = e.d_id
                where d.id = #{id}
            </select>
    

7.记录时间问题

mysql 对应datetime 类型,使用 now() 或者 sysdate() 代码中最好不要直接set时间。不要在代码中去生成时间(代码生成的时间是服务器系统时间,因为微服务会存在于不同服务器上,千万时间不一致)。插入时created_time = sysdate(), 更新时 update_time 不可更改 update_time = sysdate()

8.批量插入数据

  • for 循环

  • batch 插入 批处理需要在 JDBC url 添加参数 rewriteBatchedStatements=true 1558603997194.png

    ExecutorType.SIMPLE: 这个执行器类型不做特殊的事情。它为每个语句的执行创建一个新的预处理语句。 ExecutorType.REUSE: 这个执行器类型会复用预处理语句。 ExecutorType.BATCH:这个执行器会批量执行所有更新语句,如果 SELECT 在它们中间执行还会标定它们是 必须的,来保证一个简单并易于理解的行为。

  • foreach xml 循环

    <!--批量添加-->
    <insert id="insertStandardItemInfo" parameterType="hashmap">
      insert into t_standard_item(standard_id,item_id)
      values
      <foreach collection="itemIdList" index="index" item="item"  separator="," >
        (#{standardId,jdbcType=INTEGER}, #{item,jdbcType=INTEGER})
      </foreach>
    </insert>
    

    ps:mysql 默认接受sql的大小是 1048576(1M),即第三种方式若数据量超过 1M 会报如下异常:(可通过调整MySQL 安装目录下的my.ini文件中[mysqld]段的"max_allowed_packet = 1M")

    批量插入,外面再控制集合大小,分批次批量插入即可。可以使用 guava 现成的拆分 list 工具

    // 按大小 2 拆分
    Lists.partition(list, 2) 
    
上次更新时间: 2024/5/7 05:59:02