Mybatis 笔记

1 依赖

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.6.3</version>
    <!-- 默认为 ../pom.xml 中去找该坐标, 避免这样 -->
    <relativePath/> <!-- lookup parent from repository -->
</parent>

...
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>
        <!-- Mybatis核心 -->
        <dependency>
          <groupId>org.mybatis</groupId>
          <artifactId>mybatis</artifactId>
          <version>3.5.7</version>
        </dependency>

	    <!-- 可选插件 -->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.5.1</version>
        </dependency>

2 (参考)MyBatis的核心配置文件

建议命名为mybatis-config.xml。整合Spring之后,这个配置文件可以省略,实际很少使用,了解原理即可。

核心配置文件主要用于配置连接数据库的环境以及MyBatis的全局配置信息

位于src/main/resources目录下

核心配置文件中的标签必须按照固定的顺序(可以不写,但顺序不能乱):
properties、settings、typeAliases、typeHandlers、objectFactory、objectWrapperFactory、reflectorFactory、plugins、environments、databaseIdProvider、mappers

<?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文件,此时就可以${属性名}的方式访问属性值-->
    <properties resource="jdbc.properties"></properties>
    <settings>
        <!--将表中字段的下划线自动转换为驼峰-->
        <setting name="mapUnderscoreToCamelCase" value="true"/>
        <!--开启延迟加载-->
        <setting name="lazyLoadingEnabled" value="true"/>
    </settings>
    <typeAliases>
        <!--
        typeAlias:设置某个具体的类型的别名
        属性:
        type:需要设置别名的类型的全类名
        alias:设置此类型的别名,且别名不区分大小写。若不设置此属性,该类型拥有默认的别名,即类名
        -->
        <!--<typeAlias type="com.bibi.bean.User"></typeAlias>-->
        <!--<typeAlias type="com.bibi.bean.User" alias="user">
        </typeAlias>-->
        <!--以包为单位,设置改包下所有的类型都拥有默认的别名,即类名且不区分大小写-->
        <package name="com.bibi.bean"/>
    </typeAliases>
    <!--
    environments:设置多个连接数据库的环境
    属性:
	    default:设置默认使用的环境的id
    -->
    <environments default="mysql_test">
        <!--
        environment:设置具体的连接数据库的环境信息
        属性:
	        id:设置环境的唯一标识,可通过environments标签中的default设置某一个环境的id,表示默认使用的环境
        -->
        <environment id="mysql_test">
            <!--
            transactionManager:设置事务管理方式
            属性:
	            type:设置事务管理方式,type="JDBC|MANAGED"
	            type="JDBC":设置当前环境的事务管理都必须手动处理
	            type="MANAGED":设置事务被管理,例如spring中的AOP
            -->
            <transactionManager type="JDBC"/>
            <!--
            dataSource:设置数据源
            属性:
	            type:设置数据源的类型,type="POOLED|UNPOOLED|JNDI"
	            type="POOLED":使用数据库连接池,即会将创建的连接进行缓存,下次使用可以从缓存中直接获取,不需要重新创建
	            type="UNPOOLED":不使用数据库连接池,即每次使用连接都需要重新创建
	            type="JNDI":调用上下文中的数据源
            -->
            <dataSource type="POOLED">
                <!--设置驱动类的全类名-->
                <property name="driver" value="${jdbc.driver}"/>
                <!--设置连接数据库的连接地址-->
                <property name="url" value="${jdbc.url}"/>
                <!--设置连接数据库的用户名-->
                <property name="username" value="${jdbc.username}"/>
                <!--设置连接数据库的密码-->
                <property name="password" value="${jdbc.password}"/>
            </dataSource>
        </environment>
    </environments>
    <!--引入映射文件-->
    <mappers>
        <!-- <mapper resource="UserMapper.xml"/> -->
        <!--
        以包为单位,将包下所有的映射文件引入核心配置文件
        注意:
			1. 此方式必须保证mapper接口和mapper映射文件必须在相同的包下
			2. mapper接口要和mapper映射文件的名字一致
        -->
        <package name="com.bibi.mapper"/>
    </mappers>
</configuration>

3 配置

  1. 连接池(数据源 + driverClassName)
  2. 配置 SqlSessionFactory(数据源 + 实体类别名路径 + xml路径
  3. 配置 MapperScannerConfigure(绑定 SqlSessionFactory 和 mapper 接口路径)这一步也就是 @MapperScan 的作用
spring:
  datasource:
    type: com.zaxxer.hikari.HikariDataSource
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://127.0.0.1:3306/db_name?useUnicode=true&characterEncoding=utf-8&useSSL=false&nullCatalogMeansCurrent=true&serverTimezone=Asia/Shanghai
    username: root
    password: qweryt

mybatis-plus:
  #  mapper-locations: classpath*:/mapper/**/*.xml 已经默认
  type-aliases-package: com.bibi.domain
  type-enums-package: com.bibi.domain
  global-config:
    db-config:
      table-prefix: ops_
      id-type: assign_uuid
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
    auto-mapping-behavior: full
    map-underscore-to-camel-case: true
#    敏感的懒加载 默认boolean未赋值,为false(启用时只要加载对象,就会加载该对象的所有属性;关闭该属性则会按需加载)
    aggressive-lazy-loading: false
#    懒加载开启 默认为false
#    lazy-loading-enabled: true

#    二级缓存(默认开启)
    cache-enabled: true

#    一级缓存(默认开启):session;
#	    关闭(选择 statement 每次查询结束都会清空缓存)
#    local-cache-scope: statement

4 测试

package com.bibi;

import com.bibi.domain.account.BaseOrg;
import com.bibi.domain.device.Fence;
import com.bibi.mapper.BaseOrgMapper;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest
public class MybatisTest {

  @Test
  // @Transactional
  public void testMybatis() {
    BaseOrg baseOrg = new BaseOrg();
    baseOrg.setOrgId("100003644");
    BaseOrg baseOrgs = baseOrg.selectOne(Wrappers.lambdaQuery(baseOrg));

    BaseOrg baseOrgs2 =
        baseOrg.selectOne(Wrappers.lambdaQuery(baseOrg)); // 用同一个session,使用缓存 WHERE org_id = ?
    // BaseOrg baseOrgs1 = baseOrg.selectOne(new
    //     LambdaQueryWrapper<BaseOrg>().eq(BaseOrg::getOrgId,"100003644")); // sql 与上面不一样 WHERE
    // (org_id = ?)
    System.out.println("baseOrg1 = " + baseOrgs);
    System.out.println("baseOrg1 = " + baseOrgs2);
    // System.out.println("baseOrg1 = " + baseOrgs1);

    // List<String> entityIds = SimpleQuery.list(Wrappers.lambdaQuery(), BaseOrg::getOrgId);
    // System.out.println("entityIds = " + entityIds);
    // List<String> names =
    // SimpleQuery.list(Wrappers.lambdaQuery(BaseOrg.class).like(BaseOrg::getOrgName,"总行"),
    // BaseOrg::getOrgName, e ->
    // Optional.ofNullable(e.getOrgName()).map(String::toUpperCase).ifPresent(e::setOrgName));
    // System.out.println("names = " + names);
  }
  public void update() {
    // 在测试类中写增删改会被自动撤销
    Fence fence = new Fence();
    fence.setId("1");
    fence.setCreator("2");
    boolean b = fence.updateById();
    System.out.println("b = " + b);
  }

  @Autowired private BaseOrgMapper baseOrgMapper;

  @Test
  void testCache() {
    // 不在Mapper.xml里面的无法进行二级缓存
    System.out.println(baseOrgMapper.selectSimple());
    System.out.println(baseOrgMapper.selectSimple());
  }

  @Test
  void testCacheII() {
    LambdaQueryWrapper<BaseOrg> eq =
        new LambdaQueryWrapper<BaseOrg>().eq(BaseOrg::getOrgId, "100003644");
    BaseOrg baseOrg = baseOrgMapper.selectOne(eq);
    BaseOrg baseOrg2 = baseOrgMapper.selectOne(eq);
    System.out.println("baseOrg = " + baseOrg);
    System.out.println("baseOrg2 = " + baseOrg2);
  }

}

  • 特殊
// 返回[{password=123456, sex=男, id=1, age=23, username=admin},{password=123456, sex=男, id=2, age=23, username=张三}]
List<Map<String, Object>> getAllUserToMap();

// {1={password=123456, sex=男, id=1, age=23, username=admin},2={password=123456, sex=男, id=2, age=23, username=张三}}
@MapKey("id")
Map<String, Object> getAllUserToMap();

5 缓存

dataSource:设置数据源
属性:
type:设置数据源的类型,type=“POOLED|UNPOOLED|JNDI”
type=“POOLED”:使用数据库连接池,即会将创建的连接进行缓存,下次使用可以从缓存中直接获取,不需要重新创建
type=“UNPOOLED”:不使用数据库连接池,即每次使用连接都需要重新创建
type=“JNDI”:调用上下文中的数据源

  • 懒加载(需要手动开启,一步):
    aggressive-lazy-loading: false # 已经默认为false了
    lazy-loading-enabled: true
    property select column fetchType(p:对应填充的属性名 s:对方的方法限定名 c:当前类传过去的条件列 f:抓取类型 懒加载否)
    fetchType:当开启了全局的延迟加载之后,可以通过该属性手动控制延迟加载的效果,fetchType=“lazy(延迟加载)|eager(立即加载)”
<resultMap id="empAndDeptByStepResultMap" type="Emp">
	<id property="eid" column="eid"></id>
	<result property="empName" column="emp_name"></result>
	<result property="age" column="age"></result>
	<result property="sex" column="sex"></result>
	<result property="email" column="email"></result>
	<association property="dept"
				 select="com.bibi.mapper.DeptMapper.getEmpAndDeptByStepTwo"
				 column="did"
				 fetchType="lazy"></association>
</resultMap>
  • 自定义映射 resultMap

    • association:多对一(对象组合) (property 当前类填充属性,javaType)
    <resultMap id="empAndDeptResultMapTwo" type="Emp">
    	<id property="eid" column="eid"></id>
    	<result property="empName" column="emp_name"></result>
    	<result property="age" column="age"></result>
    	<association property="dept" javaType="Dept">
    		<id property="did" column="did"></id>
    		<result property="deptName" column="dept_name"></result>
    	</association>
    </resultMap>
    <!--Emp getEmpAndDept(@Param("eid")Integer eid);-->
    <select id="getEmpAndDept" resultMap="empAndDeptResultMapTwo">
    	select * from t_emp left join t_dept on t_emp.eid = t_dept.did where t_emp.eid = #{eid}
    </select>
    
    <!--此处的resultMap仅是处理字段和属性的映射关系-->
    <resultMap id="EmpAndDeptByStepTwoResultMap" type="Dept">
    	<id property="did" column="did"></id>
    	<result property="deptName" column="dept_name"></result>
    </resultMap>
    <!--Dept getEmpAndDeptByStepTwo(@Param("did") Integer did);-->
    <select id="getEmpAndDeptByStepTwo" resultMap="EmpAndDeptByStepTwoResultMap">
    	select * from t_dept where did = #{did}
    </select>
    
    • collection:一对多 (list/set 组合) (property ofType)

      <resultMap id="DeptAndEmpResultMap" type="Dept">
      	<id property="did" column="did"></id>
      	<result property="deptName" column="dept_name"></result>
      	<collection property="emps" ofType="Emp">
      		<id property="eid" column="eid"></id>
      		<result property="empName" column="emp_name"></result>
      		<result property="age" column="age"></result>
      		<result property="sex" column="sex"></result>
      		<result property="email" column="email"></result>
      	</collection>
      </resultMap>
      <!--Dept getDeptAndEmp(@Param("did") Integer did);-->
      <select id="getDeptAndEmp" resultMap="DeptAndEmpResultMap">
      	select * from t_dept left join t_emp on t_dept.did = t_emp.did where t_dept.did = #{did}
      </select>
      
    <association property="dept"
                 select="com.bibi.mapper.DeptMapper.getEmpAndDeptByStepTwo" column="did" fetchType="lazy"></association>
	</resultMap>
  <select id="getEmpAndDeptByStepOne" resultMap="empAndDeptByStepResultMap">
    select * from t_emp where eid = #{eid}
  </select>
        <collection property="emps"
        select="com.bibi.mapper.EmpMapper.getDeptAndEmpByStepTwo"
        column="did"
        fetchType="lazy"></collection>

@Param 源码

  • 一级缓存:sqlSession级别
    默认开启,在同一个sqlSession里面会使用缓存

    ​ Creating a new SqlSession Closing non transactional SqlSession(was not registered for synchronization because synchronization is not active)

    ​ mybatis的sqlSession和数据库连接池中维护的数据库连接Collection不是同一个概念,SqlSession是mybatis框架中的概念,是mybatis持久层框架的顶层API。

    ​ 在sqlSession中操作数据库的时候会去获取collection,collection的获取是去连接池中取的!所以Creating a new SqlSession并不是每次都去创建了数据库新连接,底层使用的collection还是连接池提供的。至于每次事务执行sql,mybatis都Creating a new SqlSession而不是共享SqlSession,是为了保证sql会话独立避免发生脏数据,从而保证会话线程安全。

  • 二级缓存:sqlSessionFactory级别

    1. 在核心配置文件中,设置全局配置属性cacheEnabled="true",默认为true,不需要设置
    2. 在映射文件中设置标签`<cache />`
    3. 二级缓存必须在SqlSession关闭或提交之后有效
    4. 查询的数据所转换的实体类类型必须实现序列化的接口
    

    两次查询之间执行了任意的增删改,会使一级和二级缓存同时失效
    刷新默认为 增删改语句挂钩,除非设置 flushInterval 属性(刷新间隔,单位毫秒)
    size 设置缓存数目(正整数)
    readOnly:缓存取出来是拷贝一份给他(false:默认,安全)还是直接把缓存实例给他(true:性能快)

  • 查询顺序:先查询二级,再查询一级
    sqlSession关闭之后,一级缓存中的数据会写入二级缓存

  • 二级缓存相关配置

    cache标签可以设置一些属性
    
    - eviction属性:缓存回收策略
    	- LRU(Least Recently Used) – 最近最少使用的:移除最长时间不被使用的对象。  
      - FIFO(First in First out) – 先进先出:按对象进入缓存的顺序来移除它们。  
      - SOFT – 软引用:移除基于垃圾回收器状态和软引用规则的对象。  
      - WEAK – 弱引用:更积极地移除基于垃圾收集器状态和弱引用规则的对象。
      - 默认的是 LRU
    - flushInterval属性:刷新间隔,单位毫秒。默认情况是不设置,也就是没有刷新间隔,缓存仅仅调用语句(增删改)时刷新
    - size属性:引用数目,正整数。代表缓存最多可以存储多少个对象,太大容易导致内存溢出
    - readOnly属性:只读,true/false
    	- true:只读缓存;会给所有调用者返回缓存对象的相同实例。因此这些对象不能被修改。这提供了很重要的性能优势。  
    	- false:读写缓存;会返回缓存对象的拷贝(通过序列化)。这会慢一些,但是安全,因此默认是false
    - type:设置二级缓存类型,如<cache type="org.mybatis.caches.ehcache.EhcacheCache"/>
    

SQL 片段

  • 声明 sql 片段:<sql>
<sql id="empColumns">eid,emp_name,age,sex,email</sql>
  • 引用 sql 片段:<include refid="sql_id">
<!--List<Emp> getEmpByCondition(Emp emp);-->
<select id="getEmpByCondition" resultType="Emp">
	select <include refid="empColumns"></include> from t_emp
</select>
  • 使用 org.apache.ibatis.jdbc.SQL 构建 sql (用于 SelectProvider)
 			SQL sql = new SQL() {{
				UPDATE(SystemOptionItemConstant.TABLENAME);
				SET("ITEMVALUE=#{value}");
				WHERE("ITEMKEY=#{key}");
			}};
			return sql.toString();

			// def selectMaxOrdinalSQL():
			SQL sql = new SQL(){{
				// SELECT("*");
				SELECT("MAX(ORDINAL)");
				FROM(SystemConstant.TABLENAME);
			}};
			return sql.toString();
@SelectProvider(type = SystemDaoProvider.class,method = "selectMaxOrdinalSQL")

6 事务 不需要手动启用

不需要手动启用事务 @EnableTransactionManagement,因为Spring自动配置已经自动开启了:org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration

7 mybatis Interceptor 插件机制

https://mybatis.org/mybatis-3/configuration.html#plugins

MyBatis有四大核心对象:
(1)ParameterHandler:处理 SQL 的 参数对象
(2)ResultSetHandler:处理 SQL 返回的 结果集
(3)StatementHandler:数据库的 处理对象 ,用于执行SQL语句构建(可以理解为包含sql语句)
(4)Executor:拦截 MyBatis 的执行器,用于执行 crud 操作

Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)
ParameterHandler (getParameterObject, setParameters)
ResultSetHandler (handleResultSets, handleOutputParameters)
StatementHandler (prepare, parameterize, batch, update, query)

在这里插入图片描述

注意,jdbc有提供一种预编译对象 PreparedStatement

  • @Intercepts:标识该类是一个拦截器
    • @Signature:指明自定义拦截器需要拦截哪一个类型,哪一个方法
      • type:上述四种类型中的一种
      • method:对应接口中的哪类方法(因为可能存在重载方法)
      • args:对应哪一个方法的入参
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Plugin;
import org.apache.ibatis.plugin.Signature;
import org.springframework.stereotype.Component;

import java.sql.Connection;
import java.util.Properties;


  /*
    MyBatis允许使用插件来拦截的方法调用包括:
      Executor (update, query, flushStatements, commit, rollback,getTransaction, close, isClosed) 执行器
      ParameterHandler (getParameterObject, setParameters) 参数
      ResultSetHandler (handleResultSets, handleOutputParameters) 结果集
      StatementHandler (prepare, parameterize, batch, update, query) Sql 构建
    */

@Intercepts(
    value = {
      @Signature(
          type = StatementHandler.class, // 确定要拦截的对象
          method = "prepare", // 确定要拦截的方法
          args = {Connection.class, Integer.class} // 拦截方法的参数
          )
    })
@Component
public class MyInterceptor implements Interceptor {

  @SuppressWarnings("unused")
  private Properties properties;

  @Override
  public Object intercept(Invocation invocation) throws Throwable { // interceptor 能够拦截的四种类型对象,此处入参 invocation 便是指拦截到的对象
  
    StatementHandler statementHandler = (StatementHandler) invocation.getTarget();
    BoundSql boundSql = statementHandler.getBoundSql();
    Object obj = boundSql.getParameterObject();
    String sql = boundSql.getSql();
    if (sql.trim().toUpperCase().startsWith("INSERT")) {
      ReflectUtil.setFieldValue(obj, "rev", 0);
      ReflectUtil.setFieldValue(obj, "createTime", new Date());
      ReflectUtil.setFieldValue(obj, "operateTime", new Date());
      ReflectUtil.setFieldValue(boundSql,"parameterObject", obj);
    } else if (sql.trim().toUpperCase().startsWith("UPDATE")) {
      sql = sql.replaceAll(" set ", " SET ")
              .replaceAll(" Set ", " SET ")
              .replaceAll(" SET ", " SET rev = rev+1, operate_time = NOW(), ");
      ReflectUtil.setFieldValue(boundSql,"sql", sql);
    }
    return invocation.proceed();
  }

  @Override
  public Object plugin(Object target) { // 每经过一个拦截器对象都会调用 plugin 方法, 该方法会调用 4 次
  
    // 判断是否拦截这个类型对象(根据@Intercepts注解决定),然后决定是返回一个代理对象还是返回原对象
    // 故我们在实现plugin方法时,要判断一下目标类型,如果是插件要拦截的对象时才执行Plugin.wrap方法,否则的话,直接返回目标本身
    System.out.println("生成代理对象...." + target);
    if (target instanceof StatementHandler) {
      return Plugin.wrap(target, this);
    }
    return target;
    // or
    return Plugin.wrap(target, this);
  }

  @Override
  public void setProperties(Properties properties) {
  
    System.out.println("拦截器需要一些变量对象,而且这个对象是支持可配置的 who: " + properties.get("who"));
    this.properties = properties;
  }
}

自定义插件实现步骤

  1. 实现接口org.apache.ibatis.plugin.Interceptor
  2. @Intercepts 注解修饰插件类,@Signature定义切点
  3. 插件注册 (三选一)
    1. @Component 加入 Spring Bean 容器
    2. SqlSessionFactory 设置插件
    3. myabtis.xml 文件配置

8 Mybatis实现动态建表

    /**
     * 动态建表
     *
     * @param tableName
     * @return int
     */
    int createNewTableIfNotExists(@Param("tableName") String tableName);

#{name} is for parameters in PreparedStatement (see String Substitution in Parameters).
一定要使用 $ 符号, # 只能用于参数; 另外注意控制字符串长度

<mapper namespace="com.mappers.TableCreatorMapper">
    <cache />
    <update id="createNewTableIfNotExists" parameterType="String" > 
        CREATE TABLE IF NOT EXISTS ${tableName} 
        (
        `ID` varchar(20) NOT NULL,
        PRIMARY KEY (`ID`)
        ) 
        ENGINE=InnoDB
    </update>
</mapper>

9 Other

tk.mybatis.mapper

see: https://github.com/abel533/Mapper/wiki/2.2-mapping

8 大注解 Id KeySql(tk) NameStyle(tk) Table Column ColumnType(tk) Transient Version(tk)

1 @Id (jpa)

上面几个注解都涉及到映射。 @Id 注解和映射无关,它是一个特殊的标记,用于标识数据库中的主键字段

如果实体类中没有一个标记 @Id 的字段,当你使用带有 ByPrimaryKey 的方法时,所有的字段会作为联合主键来使用

2 @KeySql (tk)

替换 @GeneratedValue 注解(JPA) , 可配合 interface GenId<T> 使用;

使用 genId 方式时,字段的值可回写, 因为使用 genId 时,在和数据库交互前,ID 值就已经生成了,由于这个 ID 和数据库的机制无关,因此一个实体中可以出现任意个 使用 genId 方式的 @KeySql 注解,这些注解可以指定不同的实现方法

public class UUIdGenId implements GenId<String> {
    @Override
    public String genId(String table, String column) {
        return UUID.randomUUID().toString();
    }
}

public class User {
    @Id
    @KeySql(genId = UUIdGenId.class)
    private String id;
}

新增加的 tk.mybatis.mapper.additional.insert.InsertListMapper 接口是一个和数据库无关的方法,他不支持任何的主键策略; 但是有了 genId 方式后,这个接口增加了对 @KeySql 注解 genId 方法的支持, 可以批量插入,并且可以回写 ID;

3 @NameStyle (tk)

默认情况下将实体类字段按照 驼峰转下划线 形式的 表名列名 进行转换

normal,                     //原值
camelhump,                  //驼峰转下划线
uppercase,                  //转换为大写
lowercase,                  //转换为小写
camelhumpAndUppercase,      //驼峰转下划线大写形式
camelhumpAndLowercase,      //驼峰转下划线小写形式

@NameStyle(Style.camelhumpAndUppercase)
public class Country

4 @Table (jpa)

支持 name, catalogschema 三个属性

直接使用提供的表名,不再根据实体类名进行转换。
其他两个属性中,同时配置时,catalog 优先级高于 schema,也就是只有 catalog 会生效

5 @Column (jpa)

支持 name, insertableupdateable 三个属性;
insertable 对提供的 insert 方法有效,如果设置 false 就不会出现在插入 SQL 中
updateable 对提供的 update 方法有效,设置为 false 后不会出现在更新 SQL 中

@Column(name = "`order`") // 支持自动转换关键字
private String order;

支持自动转换关键字, 也可单独使用 wrapKeyword 配置

wrapKeyword 配置后会自动处理关键字,可以配的值和数据库有关

例如 sqlserver 可以配置为 [{0}],使用 {0} 替代原来的列名

MySql 对应的配置如下:

wrapKeyword=`{0}`

使用该配置后,类似 private String order 就不需要通过 @Column 来指定别名

6 @ColumnType (tk)

column, jdbcType, typeHandler

column@Columnname 作用相同, 但 @Column 优先级更高

jdbcType 用于设置特殊 数据库类型 时指定数据库中的 jdbcType

typeHandler 用于设置 特殊类型 处理器 (常见为枚举)

@ColumnType(column = "countryname", jdbcType = JdbcType.VARCHAR,
        typeHandler = StringTypeHandler.class)
private String  countryname;

7 @Transient (jpa)

默认情况下,只有简单类型会被自动认为是表中的字段 (可通过配置中的 useSimpleType 控制, 不包含 Java 中的8种基本类型)

对于类中的复杂对象,以及 Map, List 等属性不需要配置这个注解

对于枚举类型作为数据库字段的情况,需要看配置中的 enumAsSimpleType 参数

8 @Version (tk)

注解有一个 nextVersion 属性,默认值为默认的实现,默认实现 DefaultNextVersion , 支持 Integer, Longjava.sql.Timestamp

支持的方法有 6 个

delete
deleteByPrimaryKey
updateByPrimaryKey
updateByPrimaryKeySelective
updateByExample
updateByExampleSelective

由于 tkMapper 是内置的实现,不是通过 拦截器 方式实现的,因此当执行上面支持的方法时,如果版本不一致,那么执行结果影响的行数可能就是 0. 这种情况下也不会报错, 需要在调用方法后自行判断是否执行成功

tk.Example

// 通用
Example example = new Example(Country.class);
example.setForUpdate(true);
example.setDistinct(true);
example.createCriteria().andGreaterThan("id", 100).andLessThan("id",151);
example.or().andLessThan("id", 41);
List<Country> countries = mapper.selectByExample(example);

Example cond = new Example(Template.class);
// define
cond.and().andEqualTo("define", dto.getDefine());
cond.and().andIn("uit", uit);
cond.orderBy("id").desc().orderBy("countryname").orderBy("countrycode").asc(); // default is asc
List<Template> templates = mapper.selectByExample(cond);

// select less
Example example = new Example(Country.class);
example.selectProperties("id", "countryname");
Example.Criteria criteria = example.createCriteria();
if(query.getCountryname() != null){
    criteria.andLike("countryname", query.getCountryname() + "%");
}
if(query.getId() != null){
    criteria.andGreaterThan("id", query.getId());
}
List<Country> countries = mapper.selectByExample(example);
// builder
Example example = Example.builder(Country.class)
        .select("countryname")
        .where(Sqls.custom().andGreaterThan("id", 100))
        .orderByAsc("countrycode")
        .forUpdate()
        .build();
List<Country> countries = mapper.selectByExample(example);
// jdk8
List<Country> selectByWeekendSql = mapper.selectByExample(new Example.Builder(Country.class)
        .where(WeekendSqls.<Country>custom().andLike(Country::getCountryname, "%a%")
                .andGreaterThan(Country::getCountrycode, "123"))
        .build());

(END)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值