注意事项
<if test="assetsName != null and assetsName != '' ">中间and不能用大写字母当你将查询结果的
resultType设置为java.util.Map时,MyBatis 会为查询结果集中的每一行创建一个Map对象。
collection 一对多
在 MyBatis 中,一对多的对应关系通常是通过 SQL 的 JOIN 来实现的,其中使用 ON 关键字来定义关联条件。通过 JOIN,可以将两个相关的表连接在一起,然后将相关数据合并成一个结果集。
在一对多关系中,通常是通过在主表的查询结果中包含子表的多个匹配记录来表示。例如,一个主表记录对应多个子表记录。
在 MyBatis 中,你可以使用 JOIN 和 ON 条件来定义一对多的对应关系,以及如何将子表的多个匹配记录映射到主表的属性中。这可以通过 resultMap 或者 association 和 collection 元素来实现。
以下是一个示例,假设你有两个表 users 和 orders,一个用户可以有多个订单,表 orders 中有一个外键 user_id 关联到表 users 的 id 字段。
- 在
User实体类中定义一个集合属性List<Order> orders,表示一对多关系。
public class User {
private Long id;
private String username;
private List<Order> orders;
// Getters and setters...
}
- 创建
user_mapper.xml文件,并在其中定义一个resultMap来映射查询结果。
<!-- user_mapper.xml -->
<resultMap id="UserResultMap" type="User">
<id property="id" column="id" />
<result property="username" column="username" />
<!-- 其他属性映射... -->
<collection property="orders" ofType="Order">
<id property="id" column="order_id" />
<result property="amount" column="amount" />
<!-- Order 实体类的其他属性映射... -->
</collection>
</resultMap>
<select id="selectUserWithOrders" resultMap="UserResultMap">
SELECT
u.id, u.username, o.id as order_id, o.amount
FROM
users u
LEFT JOIN
orders o ON u.id = o.user_id
WHERE
u.id = #{userId}
</select>
在上面的 resultMap 定义中,使用 <collection> 元素来配置 orders 属性的映射关系。其中,property 属性指定了集合属性名,ofType 属性指定了集合元素的类型(即 Order 类型),然后在 <collection> 元素内部配置了 Order 实体类的属性映射关系。
- 在
UserMapper接口中添加查询方法。
public interface UserMapper {
User selectUserWithOrders(@Param("userId") Long userId);
}
- 调用查询方法并获取结果。
public class YourService {
@Autowired
private UserMapper userMapper;
public User getUserWithOrders(Long userId) {
return userMapper.selectUserWithOrders(userId);
}
}
通过上述配置,MyBatis 将会通过 JOIN 操作获取用户信息以及关联的多个订单信息,并将结果映射到 User 对象的 orders 属性中,实现了一对多的查询。
association 一对一
提问:在你上方的例子中,如果又有一个User对应单个收货地址,怎么增加一个一对一
如果你的 User 表对应着一个单个的收货地址,那么在 MyBatis 中可以通过 association 元素来定义一对一的对应关系。这样,你可以在 resultMap 中同时定义 collection 元素表示一对多关系和 association 元素表示一对一关系。
假设你有两个实体类 User 和 Address,其中 User 包含一个集合属性 List<Order> orders 表示一对多关系,以及一个属性 Address address 表示一对一关系。
- 在
User类中,定义集合属性orders和一对一属性address,并添加对应的 getter 和 setter 方法。
javaCopy codepublic class User {
private Long id;
private String username;
private List<Order> orders;
private Address address;
// Getters and setters...
}
- 创建
user_mapper.xml文件,并在其中定义一个resultMap来映射查询结果。
xmlCopy code<!-- user_mapper.xml -->
<resultMap id="UserResultMap" type="User">
<id property="id" column="id" />
<result property="username" column="username" />
<!-- 其他属性映射... -->
<collection property="orders" ofType="Order">
<id property="id" column="order_id" />
<result property="amount" column="amount" />
<!-- Order 实体类的其他属性映射... -->
</collection>
<association property="address" javaType="Address">
<id property="id" column="address_id" />
<result property="city" column="city" />
<!-- Address 实体类的其他属性映射... -->
</association>
</resultMap>
<select id="selectUserWithOrdersAndAddress" resultMap="UserResultMap">
SELECT
u.id, u.username, o.id as order_id, o.amount, a.id as address_id, a.city
FROM
users u
LEFT JOIN
orders o ON u.id = o.user_id
LEFT JOIN
address a ON u.address_id = a.id
WHERE
u.id = #{userId}
</select>
在上面的 resultMap 定义中,我们通过 <association> 元素来配置 address 属性的映射关系。其中,property 属性指定了一对一属性名,javaType 属性指定了一对一属性的类型(即 Address 类型),然后在 <association> 元素内部配置了 Address 实体类的属性映射关系。
- 在
UserMapper接口中添加查询方法。
javaCopy codepublic interface UserMapper {
User selectUserWithOrdersAndAddress(@Param("userId") Long userId);
}
- 调用查询方法并获取结果。
javaCopy codepublic class YourService {
@Autowired
private UserMapper userMapper;
public User getUserWithOrdersAndAddress(Long userId) {
return userMapper.selectUserWithOrdersAndAddress(userId);
}
}
通过以上配置,MyBatis 将会通过 JOIN 操作获取用户信息以及关联的多个订单信息和单个收货地址信息,并将结果映射到 User 对象的 orders 属性和 address 属性中,实现了一对多和一对一的查询。
使用自定义查询进行一对一
问题:继续你上方的例子,如果我的对应关系是根据一个已经写好的用户-收货地址的一对一查询sql,怎么更改xml文件
如果你已经有一个独立的 SQL 查询语句用于用户和收货地址的一对一查询,那么你可以在 MyBatis 的 resultMap 中使用 <association> 元素来配置一对一的映射关系。这样,你可以在 resultMap 中引用已有的一对一查询结果,而不必重新定义查询逻辑。
以下是在 MyBatis 的 resultMap 中使用 <association> 元素来配置一对一关系的示例:
假设你已经有一个 SQL 查询语句 selectUserWithAddress 用于用户和收货地址的一对一查询,这个查询语句返回了用户信息和对应的收货地址信息。
- 创建
user_mapper.xml文件,并在其中定义一个resultMap来映射查询结果。
<!-- user_mapper.xml -->
<resultMap id="UserResultMap" type="User">
<id property="id" column="id" />
<result property="username" column="username" />
<!-- 其他属性映射... -->
<collection property="orders" ofType="Order">
<id property="id" column="order_id" />
<result property="amount" column="amount" />
<!-- Order 实体类的其他属性映射... -->
</collection>
<association property="address" select="selectUserWithAddress" />
</resultMap>
<select id="selectUserWithOrdersAndAddress" resultMap="UserResultMap">
SELECT
u.id, u.username, o.id as order_id, o.amount
FROM
users u
LEFT JOIN
orders o ON u.id = o.user_id
WHERE
u.id = #{userId}
</select>
<select id="selectUserWithAddress" resultType="Address">
-- 这里写你已经定义的用户-收货地址的一对一查询SQL
-- 例如:SELECT * FROM address WHERE user_id = #{userId}
</select>
在上面的 resultMap 定义中,我们通过 <association> 元素来配置 address 属性的映射关系。其中,property 属性指定了一对一属性名,select 属性指定了引用的已有查询语句 selectUserWithAddress。在这里,我们假设你已经在 XML 文件中定义了名为 selectUserWithAddress 的查询语句,该查询语句返回了用户对应的收货地址信息。
请注意,select 属性中的值是 selectUserWithAddress,这是一个自定义的查询语句的 id,用于引用已有的查询逻辑。
- 在
UserMapper接口中添加查询方法。
public interface UserMapper {
User selectUserWithOrdersAndAddress(@Param("userId") Long userId);
}
- 调用查询方法并获取结果。
public class YourService {
@Autowired
private UserMapper userMapper;
public User getUserWithOrdersAndAddress(Long userId) {
return userMapper.selectUserWithOrdersAndAddress(userId);
}
}
通过以上配置,MyBatis 将会在查询用户信息时,自动调用 selectUserWithAddress 查询语句来获取用户对应的收货地址信息,并将结果映射到 User 对象的 address 属性中,实现了一对多和一对一的查询。
foreach
dept IN
<foreach collection="deptIds" item="id" open="(" close=")" separator=",">
#{id}
</foreach>
SqlSession的四大对象
1、SqlSession下的四大对象介绍
通过前面的分析,我们应该知道在Mybatis中的,首先是通过SqlSessionFactoryBuilder加载全局配置文件(包括SQL映射器),这些配置都会封装在Configuration中,其中每一条SQL语句的信息都会封装在MappedStatement中。然后创建SqlSession,这时还会初始化Executor执行器。最后通过调用sqlSession.getMapper()来动态代理执行Mapper中对应的SQL语句。而当一个动态代理对象进入到了MapperMethod的execute()方法后,它经过简单地判断就进入了SqlSession的delete、update、insert、select等方法,这里是真正执行SQL语句的地方。那么这些方法是如何执行呢?答:实际上SqlSession的执行过程是通过Executor、StatementHandler、ParameterHandler和ResultSetHandler来完成数据库操作和结果返回的,它们简称为四大对象:
- Executor:代表执行器,由它调度StatementHandler、ParameterHandler、ResultSetHandler等来执行对应的SQL,其中StatementHandler是最重要的。
- StatementHandler:作用是使用数据库的Statement(PreparedStatement)执行操作,它是四大对象的核心,起到承上启下的作用,许多重要的插件都是通过拦截它来实现的。
- ParameterHandler:是用来处理SQL参数的。
- ResultSetHandler:是进行数据集(ResultSet)的封装返回处理的,它非常的复杂,好在不常用。
这四个对象都是通过Mybatis的插件来完成的,在实例化Executor、StatementHandler、ParameterHandler、ResultSetHandler四大接口对象的时候都是调用interceptorChain.pluginAll() 方法插入进去的(Mybatis的插件实现原理可以参考:链接1 ,链接2 )。下面依次分析这四大对象的生成和运作原理。
开放的核心接口
MyBatis 是一个持久层框架,提供了许多接口和类用于数据库访问。以下是 MyBatis 中一些常见的接口及其功能的列表:
| 接口 | 功能 | 主要方法/回调 |
|---|---|---|
SqlSessionFactory | MyBatis 的工厂接口,用于创建 SqlSession 实例 | openSession、getConfiguration |
SqlSession | 用于执行 SQL 命令,提供了数据库操作的方法 | select、insert、update、delete 等 |
Mapper | 映射器接口,定义了与数据库交互的方法 | 根据方法名执行 SQL 语句,方法参数与 SQL 参数绑定 |
SqlSessionFactoryBuilder | 用于创建 SqlSessionFactory 实例的构建器 | build |
Configuration | MyBatis 的配置类,包含了 MyBatis 的全部配置信息 | getMapper、addMapper、setXXX 等 |
MapperProxy | 映射器接口的动态代理实现 | invoke |
Executor | 执行器接口,定义了 SQL 语句的执行方式 | query、update 等 |
Transaction | 事务接口,定义了事务的开始、提交、回滚等方法 | begin、commit、rollback 等 |
| 语句处理器接口,用于处理预处理 SQL 语句、Statement 等,许多重要的插件都是通过拦截它来实现的。 | prepare、parameterize、batch 等 | |
| 参数处理器接口,用于处理 SQL 语句的参数 | setParameters | |
| 结果集处理器接口,用于处理从数据库返回的结果集 | handleResultSets、handleOutputParameters 等 | |
ResultHandler | 结果处理器接口,用于自定义处理查询结果 | handleResult |
ResultSetType | 结果集类型枚举,用于指定 Statement 的结果集类型 | FORWARD_ONLY、SCROLL_INSENSITIVE 等 |
ResultSetConcurrency | 结果集并发性枚举,用于指定 Statement 的结果集并发性 | CONCUR_READ_ONLY、CONCUR_UPDATABLE 等 |
| 类型处理器接口,用于处理 Java 类型与 JDBC 类型之间的转换 | setParameter、getResult 等 | |
TypeAliasRegistry | 类型别名注册表,用于管理 MyBatis 中的类型别名 | registerAlias、resolveAlias 等 |
MappedStatement | 映射语句对象,表示一个映射配置文件中的 SQL 语句配置 | getId、getSqlCommandType、getBoundSql 等 |
DataSource | 数据源接口,定义了获取数据库连接的方法 | getConnection |
SqlNode | SQL 节点接口,表示 XML 映射文件中的一个 SQL 节点 | apply |
SqlSource | SQL 源接口,表示 XML 映射文件中的一个 SQL 配置项的来源 | getBoundSql |
Cache | 缓存接口,定义了缓存的基本操作 | putObject、getObject、clear 等 |
LanguageDriver | 语言驱动接口,定义了动态 SQL 语句的生成方式 | createSqlSource、parse |
| 拦截器接口,用于拦截 MyBatis 的执行过程,如给所有语句加上status != 0 | intercepr |
这些接口是 MyBatis 框架中的核心组件,通过实现这些接口,可以在不同的阶段对数据库操作进行自定义处理。在实际使用中,开发者通常会使用 MyBatis 的注解或 XML 配置来定义 SQL 映射关系,而这些接口则是 MyBatis 在底层实现中使用的关键组件。
<foreach>
遍历
Map<String, Object>(动态拼接sql),index不是索引!是Map中的key再次骂一次傻逼GPT,item是Map中的value。insert
<insert id="insertBatchByMap" parameterType="java.util.Map"> insert into t_student (class_id, // 这里模拟有业务需要插入前端没有传入,但后端需要记录的一个值,也可以在走的Mapper之前手动put一个,这里只是展示这种写法 <foreach collection="studentMap.keySet()" item="columnItem" separator=","> ${columnItem} </foreach>) values ('101', <foreach collection="studentMap.values()" item="valueItem" separator=","> #{valueItem} </foreach>) </insert>update
<update id="updateByMap" parameterType="java.util.Map"> UPDATE t_student set <foreach collection="studentMap.entrySet()" index="columnName" item="columnValue" separator=","> ${columnName} = #{columnValue} </foreach> WHERE stu_id = '105' </update>
需求
我们可以通过结合Spring AOP和MyBatis的拦截器来实现:访问不同Controller来拼接不同sql的处理
首先,我们创建一个拦截器 SqlInterceptor 用于动态修改SQL语句的后缀:
import org.apache.ibatis.executor.parameter.ParameterHandler;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.plugin.*;
import java.sql.Connection;
import java.util.Properties;
@Intercepts({
@Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class})
})
public class SqlInterceptor implements Interceptor {
private static final ThreadLocal<String> dynamicSqlSuffix = new ThreadLocal<>();
public static String getDynamicSqlSuffix() {
return dynamicSqlSuffix.get();
}
public static void setDynamicSqlSuffix(String suffix) {
dynamicSqlSuffix.set(suffix);
}
public static void clear() {
dynamicSqlSuffix.remove();
}
@Override
public Object intercept(Invocation invocation) throws Throwable {
StatementHandler statementHandler = (StatementHandler) invocation.getTarget();
ParameterHandler parameterHandler = statementHandler.getParameterHandler();
// 获取原始SQL语句
String originalSql = statementHandler.getBoundSql().getSql();
// 获取动态SQL后缀
String dynamicSuffix = getDynamicSqlSuffix();
if (dynamicSuffix != null && !dynamicSuffix.isEmpty()) {
// 在原始SQL语句后面添加动态后缀
// 加上判断是否包含WHERE的逻辑,以免直接AND报错
originalSql += dynamicSuffix;
}
// 将修改后的SQL语句设置回去
statementHandler.getBoundSql().setSql(originalSql);
// 继续执行原来的操作
return invocation.proceed();
}
@Override
public Object plugin(Object target) {
return Plugin.wrap(target, this);
}
@Override
public void setProperties(Properties properties) {
// 可以在这里配置一些属性
}
}
然后,我们创建一个切面类 SqlAspect 来根据Controller的访问路径设置动态SQL后缀:
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class SqlAspect {
@Before("execution(* com.example.controllers.AController.*(..))")
public void beforeAController(JoinPoint joinPoint) {
// 设置动态SQL后缀为 " AND status != 1"
SqlInterceptor.setDynamicSqlSuffix(" AND status != 1");
}
@Before("execution(* com.example.controllers.BController.*(..))")
public void beforeBController(JoinPoint joinPoint) {
// 设置动态SQL后缀为 " AND status = 0"
SqlInterceptor.setDynamicSqlSuffix(" AND status = 0");
}
// 加上对应@After清空动态sql:SqlInterceptor.clear()
}
在上述代码中,SqlAspect 类使用 @Before 注解,分别匹配了 AController 和 BController 的执行前通知,根据访问的Controller设置了相应的动态SQL后缀。
最后,在你的MyBatis配置文件中注册拦截器:
<!-- mybatis-config.xml -->
<configuration>
<!-- 其他配置 -->
<plugins>
<plugin interceptor="com.example.SqlInterceptor"/>
</plugins>
</configuration>
并确保你的Spring AOP配置生效,可以通过 @ComponentScan 或者其他方式确保 SqlAspect 被扫描到。
这样,当访问 /a 的Controller时,所有的SQL语句都会被添加上 AND status != 1 的后缀;而访问 /b 的Controller时,所有的SQL语句都会被添加上 AND status = 0 的后缀。
——————————
Mybatis-Plus
如果需要,参考
<dependency> <groupId>com.github.yulichang</groupId> <artifactId>mybatis-plus-join</artifactId> <version>1.4.6</version> <!-- 请使用最新版本 --> </dependency>
分页插件
⚠️ 提示
于
v3.5.9起,PaginationInnerInterceptor已分离出来。如需使用,则需单独引入mybatis-plus-jsqlparser依赖 , 具体请查看 安装 一章。<dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-spring-boot3-starter</artifactId> <version>3.5.9</version> </dependency> <!-- 分页插件 --> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-jsqlparser</artifactId> <version>3.5.9</version> <!-- 确保版本和 MyBatis Plus 主包一致 --> </dependency>
使用
public interface SysUserMapper extends BaseMapper<SysUser> {}
public interface ISysUserService extends IService<SysUser> {}
public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> implements ISysUserService {}
Page<CustomerStoreType> page = new Page<>(current, size);
LambdaQueryWrapper<实体类> lqw = new LambdaQueryWrapper<>();
lqw
.like(StrUtil.isNotBlank(param.getTypeCode()),CustomerStoreType::getTypeCode, param.getTypeCode())
.orderByAsc(CustomerStoreType::getSortOrder);
Page<CustomerStoreType> result = customerStoreTypeServiceImpl.page(page, lqw);
return Result.success(result);
如果 Mapper 文件在 SpringBoot 启动类所在包及其子包下,则不需要给启动类加
@MapperScan注解;如果 Mapper 文件加了
@Mapper注解,则不需要@MapperScan也可以扫描到这个 Mapper;
mybatis-plus.mapper-locations配置(如classpath*:/mapper/*.xml,classpath*:com/enlin/mapper/*.xml,classpath*:com/enlin/mapper/**/*.xml)仅用于定位 XML 映射文件,与 Mapper 接口的注册无关。
实体类自动插入创建人、时间等等
UserSession.java
package com.enlin.session;
import com.enlin.commons.cache.RedisCacheUtil;
import com.enlin.entity.SysUser;
import jakarta.servlet.http.HttpServletRequest;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import java.util.concurrent.TimeUnit;
/**
* @author
* @projectName SpringBoot_MongoDB
* @description 用户缓存
* @date 2023/11/3 11:53
*/
@Slf4j
@Service
@RequiredArgsConstructor
public class UserSession {
private static final String BEARER_PREFIX = "Bearer ";
private static final int BEARER_PREFIX_LENGTH = 7;
@Autowired
protected HttpServletRequest request;
private SysUser user;
private final RedisCacheUtil redisCacheUtil;
public SysUser getUser() {
// 从request中获取token
String authorization = request.getHeader("Authorization");
// 判断token是否为空
if (authorization == null) {
return null;
}
String token = authorization.replace(BEARER_PREFIX, "");
if (user==null){
user = getUser(token);
}
return user;
}
/**
* 根据token获取用户信息
* @param token 用户token
* @return 用户信息,如果未找到则返回null
*/
public SysUser getUser(String token) {
if (!StringUtils.hasText(token)) {
return null;
}
return redisCacheUtil.get(token, SysUser.class);
}
/**
* 设置用户会话信息
* @param token 用户token
* @param user 用户信息
* @return 是否设置成功
*/
public boolean setUser(String token, SysUser user) {
if (!StringUtils.hasText(token) || user == null) {
log.warn("设置用户会话失败:token或用户信息为空");
return false;
}
boolean success = redisCacheUtil.set(token, user, BEARER_PREFIX_LENGTH, TimeUnit.DAYS);
if (success) {
log.debug("用户会话设置成功,token: {}, userId: {}", token, user.getId());
}
return success;
}
/**
* 根据token移除用户会话
* @param token 用户token
* @return 是否移除成功
*/
public boolean removeUser(String token) {
if (!StringUtils.hasText(token)) {
return false;
}
return redisCacheUtil.delete(token);
}
/**
* 检查用户会话是否存在
* @param token 用户token
* @return 是否存在
*/
public boolean exists(String token) {
if (!StringUtils.hasText(token)) {
return false;
}
return redisCacheUtil.exists(token);
}
/**
* 刷新用户会话过期时间
* @param token 用户token
* @return 是否刷新成功
*/
public boolean refresh(String token) {
if (!StringUtils.hasText(token)) {
return false;
}
return redisCacheUtil.expire(token, BEARER_PREFIX_LENGTH, TimeUnit.DAYS);
}
/**
* 获取用户会话过期时间
* @param token 用户token
* @return 过期时间,单位:毫秒
*/
public long getExpire(String token) {
if (!StringUtils.hasText(token)) {
return -1;
}
return redisCacheUtil.getExpire(token);
}
}
MyMetaObjectHandler.java
package com.enlin.handler;
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import com.enlin.entity.SysUser;
import com.enlin.session.UserSession;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.reflection.MetaObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
/**
* 自定义元数据对象处理器
*/
@Slf4j
@Component
public class MyMetaObjectHandler implements MetaObjectHandler {
@Autowired
private UserSession userSession;
/**
* 插入操作,自动填充
* @param metaObject
*/
@Override
public void insertFill(MetaObject metaObject) {
SysUser user;
String userId = "1";
try {
user = userSession.getUser();
if (user != null) {
userId = user.getId();
}
} catch (Exception e) {
log.warn("获取当前用户信息失败,使用默认用户ID: {}", userId, e);
}
metaObject.setValue("createTime", System.currentTimeMillis());
metaObject.setValue("updateTime", System.currentTimeMillis());
metaObject.setValue("createBy",userId);
metaObject.setValue("updateBy",userId);
metaObject.setValue("deleted", false);
}
/**
* 更新操作,自动填充
* @param metaObject
*/
@Override
public void updateFill(MetaObject metaObject) {
SysUser user;
String userId = "1";
try {
user = userSession.getUser();
if (user != null) {
userId = user.getId();
}
} catch (Exception e) {
log.warn("获取当前用户信息失败,使用默认用户ID: {}", userId, e);
}
metaObject.setValue("updateTime", System.currentTimeMillis());
metaObject.setValue("updateBy", userId);
}
}
实体类
package com.enlin.entity;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import java.io.Serializable;
/**
* <p>
* 登录账号表
* </p>
*
* @author Jay.Bao
* @since 2025-08-13
*/
@Getter
@Setter
@ToString
@TableName("t_sys_user")
public class SysUser extends BaseEntity implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 账号编码(唯一)
*/
@TableField("c_code")
private String code;
}
BaseEntity.java
package com.enlin.entity;
import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import lombok.Data;
@Data
public class BaseEntity {
/**
* 雪花算法生成的唯一ID
*/
@TableId(value = "c_id", type = IdType.ASSIGN_ID)
private String id;
@TableField(value = "c_create_by", fill = FieldFill.INSERT)
private String createBy;
@TableField(value = "c_create_time", fill = FieldFill.INSERT)
private Long createTime;
@TableField(value = "c_update_by", fill = FieldFill.INSERT_UPDATE)
private String updateBy;
@TableField(value = "c_update_time", fill = FieldFill.INSERT_UPDATE)
private Long updateTime;
/**
* 是否逻辑删除
*/
@TableField(value = "c_deleted", fill = FieldFill.INSERT)
private Boolean deleted;
}
