MybatisPlus可以节省大量时间,所有的CRUD代码都可以自动化完成。MyBatis-Plus是一个MyBatis的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。
特性:
注:本文实验环境为Spring Boot + MySQL。
<parent>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-parentartifactId>
<version>3.1.6version>
<relativePath/>
parent>
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starterartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-testartifactId>
<scope>testscope>
dependency>
<dependency>
<groupId>com.mysqlgroupId>
<artifactId>mysql-connector-jartifactId>
dependency>
<dependency>
<groupId>com.baomidougroupId>
<artifactId>mybatis-plus-boot-starterartifactId>
<version>3.5.3.1version>
dependency>
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
<version>1.18.4version>
dependency>
<dependency>
<groupId>junitgroupId>
<artifactId>junitartifactId>
<version>4.12version>
dependency>
<dependency>
<groupId>org.slf4jgroupId>
<artifactId>slf4j-log4j12artifactId>
<version>1.6.4version>
dependency>
<dependency>
<groupId>org.testnggroupId>
<artifactId>testngartifactId>
<version>RELEASEversion>
<scope>testscope>
dependency>
dependencies>
DROP TABLE IF EXISTS user;
CREATE TABLE user(
id BIGINT(20) NOT NULL COMMENT '主键ID',
name VARCHAR(30) NULL DEFAULT NULL COMMENT '姓名',
age INT(11) NULL DEFAULT NULL COMMENT '年龄',
email VARCHAR(50) NULL DEFAULT NULL COMMENT '邮箱',
PRIMARY KEY (id)
);
DELETE FROM user;
INSERT INTO user (id, name, age, email) VALUES
(1, 'Jone', 18, '[email protected]'),
(2, 'Jack', 20, '[email protected]'),
(3, 'Tom', 28, '[email protected]'),
(4, 'Sandy', 21, '[email protected]'),
(5, 'Billie', 24, '[email protected]');
spring:
datasource:
username: root
password: 123456
url: jdbc:mysql://localhost:3306/mybatis_plus?userUnicode=true&characterEncoding=utf-8
driver-class-name: com.mysql.cj.jdbc.Driver
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
log4j.rootLogger=DEBUG,A1
log4j.appender.A1=org.apache.log4j.ConsoleAppender
log4j.appender.A1.layout=org.apache.log4j.PatternLayout
log4j.appender.A1.layout.ConversionPattern=[%t] [%c]-[%p] %m%n
@Data
@NoArgsConstructor
@AllArgsConstructor
@TableName("user")
public class User {
private Long id;
private String name;
private Integer age;
private String email;
}
@Mapper
public interface UserMapper extends BaseMapper<User> {
//所有的CRUD都已经完成,不需要像以前一样配置一大堆文件:pojo->dao(连接mybatis,配置mapper.xml文件)->service->controller
}
@SpringBootApplication
@MapperScan("com.shiftycat.mybatis_plus.mapper")
public class MybatisPlusApplication {
public static void main(String[] args) {
SpringApplication.run(MybatisPlusApplication.class, args);
}
}
@RunWith(SpringRunner.class)
@SpringBootTest
public class UserMapperTest {
@Autowired
private UserMapper userMapper;
@Test
public void testSelect() {
List<User> userList = userMapper.selectList(null);
for (User user : userList) {
System.out.println(user);
}
}
}
// int insert(T entity);
@Test
public void testInsert() {
User user = new User();
user.setAge(18);
user.setEmail("[email protected]");
user.setName("张三");
//返回的result是受影响的⾏数,并不是⾃增后的id
int result = userMapper.insert(user);
System.out.println(result);
System.out.println(user.getId());
}
通过数据库可以看到,数据已经写入到了数据库,但是,id的值不正确,我们期望的是数据库自增长,实际是MP生成了id的值写入到了数据库。
解决方案:自定义ID生成器
在复杂分布式系统中,往往需要大量的数据和消息进行唯一标识。比如支付宝每一个账号在数据库分表后都需要有一个唯一ID做标识。此时一个能够生成全局唯一ID的系统是非常必要的。所以,生成的ID需要具备一下特点:
@Data
@NoArgsConstructor
@AllArgsConstructor
@TableName("user")
public class User {
@TableId(type = IdType.AUTO) //指定id类型为自增长
private Long id;
private String name;
private Integer age;
private String email;
}
通过@TableId(type = IdType.AUTO)
来设置主键ID自增长,ctrl+鼠标左键可查看剩下的type枚举类型。主要主键生成方式有:
数据库ID自增(AUTO)
雪花算法(ASSIGN_ID)
这种方案是一种以划分命名空间来生成ID的一种算法,这种方案把64-bit分别划分成多段,分开来标示机器、时间等。使用41bit作为毫秒数,10bit作为机器的ID(5个bit是数据中心,5个bit的机器ID),12bit作为毫秒内的流水号(意味着每个节点在每毫秒可以产生4096个ID),最后还有一个符号位,永远是0。
优点:
缺点:
UUID(ASSIGN_UUID)
UUID(Universally Unique Identifier)的标准型式包含32个16进制数字,以连字号分为五段,形式为8-4-4-4-12的36个字符。
优点:
缺点:
注:自 3.3.0 开始,默认使用雪花算法+UUID(不含中划线)。
public enum IdType {
// 数据库ID⾃增
AUTO(0),
// 该类型为未设置主键类型
NONE(1),
// ⽤户输⼊ID,该类型可以通过⾃⼰注册⾃动填充插件进⾏填充
INPUT(2),
// 分配ID (主键类型为number或string)(雪花算法)
ASSIGN_ID(3),
// 分配UUID (主键类型为 string)
ASSIGN_UUID(4);
private final int key;
private IdType(int key) {
this.key = key;
}
public int getKey() {
return this.key;
}
}
@TableField注解可以指定字段的⼀些属性:
几个常见的应用场景:
映射数据库表字段名与实体类属性名不一致
通过设置value属性,可以灵活地指定属性对应的数据库表字段名,避免在查询和更新操作时发生字段名不匹配的错误。
设置插入和更新时需要忽略的字段
通过设置insert和update属性,可以灵活地控制是否插入或者更新某个属性,避免不必要的数据库操作。
根据条件查询指定的字段
通过设置condition属性,可以指定查询时的条件,只有满足条件的数据才会查询到该字段,提高查询性能。
@Data
@NoArgsConstructor
@AllArgsConstructor
@TableName("user")
public class User {
@TableId(type = IdType.AUTO) //指定id类型为自增长
private Long id;
private String name;
@TableField(select = false)
private Integer age;
@TableField(value = "email")
private String mail;
@TableField(exist = false)
private String address;
}
@TableName用来设置实体类对应的表明。如果我们不设置这个注解,我们操作的数据库的表就由BaseMapper
泛型决定(User)
@TableName(value="user")
public class User {
......
}
另外的一种方法是在ymal文件中设置实体类所对应的表的统一前缀。
mybatis-plus:
global-config:
db-config:
table-prefix: db_
// 据 ID 修改
// int updateById(@Param("et") T entity);
@Test
public void testUpdateById() {
User user = new User();
user.setId(5L); //主键
user.setAge(21); //更新的字段
//根据id更新,更新不为null的字段
this.userMapper.updateById(user);
}
/**
* 根据 whereEntity 条件,更新记录
* 正常的更新sql语句为:update 表名 set 字段 = 值 where 条件语句;
* @param entity 实体对象 (set 条件值,可以为 null)
* @param updateWrapper 实体对象封装操作类(可以为 null,⾥⾯的 entity ⽤于⽣成 where 语句)
*/
// int update(@Param("et") T entity, @Param("ew") Wrapper updateWrapper);
@Test
public void testUpdate1() {
/*
根据条件进行更新:将姓名为“张三”的年龄修改为30岁。
*/
User user = new User();
//更新的字段
user.setAge(30);
//更新的条件
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.eq("name", "张三");
//执⾏更新操作
int result = this.userMapper.update(user, wrapper);
System.out.println("result = " + result);
}
@Test
public void testUpdate2() {
// 方法2:通过UpdateWrapper进⾏更新
//更新的条件以及字段
UpdateWrapper<User> wrapper=new UpdateWrapper<>();
wrapper.eq("name","张三").set("age",28);
//执⾏更新操作
int result=this.userMapper.update(null,wrapper);
System.out.println("result = "+result);
}
// int deleteById(T entity);
@Test
public void testDeleteById() {
//执⾏删除操作
int result = this.userMapper.deleteById(5L);
System.out.println("result = " + result);
}
// int deleteByMap(@Param("cm") Map columnMap);
@Test
public void testDeleteByMap1() {
Map<String, Object> columnMap = new HashMap<>();
columnMap.put("age", 28);
columnMap.put("name", "张三");
//将columnMap中的元素设置为删除的条件,多个之间为and关系
int result = this.userMapper.deleteByMap(columnMap);
System.out.println("result = " + result);
}
// int delete(@Param("ew") Wrapper queryWrapper);
@Test
public void testDeleteByMap2() {
User user = new User();
user.setAge(18);
user.setName("张三");
//将实体对象进⾏包装,包装为操作条件
QueryWrapper<User> wrapper = new QueryWrapper<>(user);
int result = this.userMapper.delete(wrapper);
System.out.println("result = " + result);
}
// int deleteBatchIds(@Param("coll") Collection> idList);
@Test
public void testDeleteByMap() {
//根据id集合批量删除
int result = this.userMapper.deleteBatchIds(Arrays.asList(2L, 3L));
System.out.println("result = " + result);
}
// T selectById(Serializable id);
@Test
public void testSelectById() {
//根据id查询数据
User user = this.userMapper.selectById(1L);
System.out.println("result = " + user);
}
// List selectBatchIds(@Param("coll") Collection extends Serializable> idList);
@Test
public void testSelectBatchIds() {
//根据id集合批量查询
List<User> users = this.userMapper.selectBatchIds(Arrays.asList(1L, 4L));
for (User user : users) {
System.out.println(user);
}
}
根据 entity
条件,查询⼀条记录,如果查询结果超过一条会报错。
default T selectOne(@Param("ew") Wrapper<T> queryWrapper) {
List<T> list = this.selectList(queryWrapper);
if (list.size() == 1) {
return list.get(0);
} else if (list.size() > 1) {
throw new TooManyResultsException("Expected one result (or null) to be returned by selectOne(), but found: " + list.size());
} else {
return null;
}
}
@Test
public void testSelectOne() {
QueryWrapper<User> wrapper = new QueryWrapper<User>();
wrapper.eq("name", "Jone");
//根据条件查询⼀条数据,如果结果超过⼀条会报错
User user = this.userMapper.selectOne(wrapper);
System.out.println(user);
}
// Long selectCount(@Param("ew") Wrapper queryWrapper);
@Test
public void testSelectCount() {
QueryWrapper<User> wrapper = new QueryWrapper<User>();
wrapper.gt("age", 20); //年龄⼤于20岁
//根据条件查询数据条数
Long count = this.userMapper.selectCount(wrapper);
System.out.println("count = " + count);
}
// List selectList(@Param("ew") Wrapper queryWrapper);
@Test
public void testSelectList1() {
QueryWrapper<User> wrapper = new QueryWrapper<User>();
wrapper.gt("age", 23); //年龄⼤于23岁
//根据条件查询数据
List<User> users = this.userMapper.selectList(wrapper);
for (User user : users) {
System.out.println("user = " + user);
}
}
// List
// 方法返回List
@Test
public void testSelectList2() {
QueryWrapper<User> wrapper = new QueryWrapper<User>();
wrapper.gt("age", 23); //年龄⼤于23岁
//根据条件查询数据
List<Map<String, Object>> users = this.userMapper.selectList(wrapper);
users.forEach(System.out::println);
}
// List
// 只返回第一个字段的值
@Test
public void testSelectList3() {
QueryWrapper<User> wrapper = new QueryWrapper<User>();
wrapper.gt("age", 23); //年龄⼤于23岁
//根据条件查询数据
List<Object> users = this.userMapper.selectList(wrapper);
users.forEach(System.out::println);
}
配置分页插件:
@Configuration
@MapperScan("com.shiftycat.mybatis_plus.mapper") //设置mapper接⼝的扫描包
public class MybatisPlusConfig {
/**
* 新的分页插件,一缓和二缓遵循mybatis的规则,需要设置 MybatisConfiguration#useDeprecatedExecutor = false 避免缓存出现问题(该属性会在旧插件移除后一同移除)
*/
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
return interceptor;
}
}
// > P selectPage(P page, @Param("ew") Wrapper queryWrapper);
@Test
public void testSelectPage() {
QueryWrapper<User> wrapper = new QueryWrapper<User>();
wrapper.gt("age", 10); //年龄⼤于10岁
//第一个参数:当前页 第二个参数:每页的条数
Page<User> page = new Page<>(2, 1);
//根据条件查询数据
IPage<User> iPage = this.userMapper.selectPage(page, wrapper);
System.out.println("数据总条数:" + iPage.getTotal());
System.out.println("总⻚数:" + iPage.getPages());
System.out.println("分页数据:" + iPage.getRecords());
}
在 Mybatis Plus 中,ISqlInjector
接口负责 SQL 的注入工作,AbstractSqlInjector
是它的实现类。
首先,AbstractSqlInjector
抽象类执行 inspectInject
方法。在该方法中,使用this.getMethodList()
获取到实现类的列表,并且对于列表中的每一个元素运行inject()方法。
public abstract class AbstractSqlInjector implements ISqlInjector {
protected final Log logger = LogFactory.getLog(this.getClass());
public AbstractSqlInjector() {
}
// inspectInject()方法
public void inspectInject(MapperBuilderAssistant builderAssistant, Class<?> mapperClass) {
Class<?> modelClass = ReflectionKit.getSuperClassGenericType(mapperClass, Mapper.class, 0);
if (modelClass != null) {
String className = mapperClass.toString();
Set<String> mapperRegistryCache = GlobalConfigUtils.getMapperRegistryCache(builderAssistant.getConfiguration());
if (!mapperRegistryCache.contains(className)) {
TableInfo tableInfo = TableInfoHelper.initTableInfo(builderAssistant, modelClass);
// 获取 CRUD 实现类列表
List<AbstractMethod> methodList = this.getMethodList(mapperClass, tableInfo);
if (CollectionUtils.isNotEmpty(methodList)) {
methodList.forEach((m) -> {
// inject()方法在这边
m.inject()(builderAssistant, mapperClass, modelClass, tableInfo);
});
} else {
this.logger.debug(mapperClass.toString() + ", No effective injection method was found.");
}
mapperRegistryCache.add(className);
}
}
}
public abstract List<AbstractMethod> getMethodList(Class<?> mapperClass, TableInfo tableInfo);
}
其次,进入 inject()
方法。
public void inject(MapperBuilderAssistant builderAssistant, Class<?> mapperClass, Class<?> modelClass, TableInfo tableInfo) {
this.configuration = builderAssistant.getConfiguration();
this.builderAssistant = builderAssistant;
this.languageDriver = this.configuration.getDefaultScriptingLanguageInstance();
// injectMappedStatement()方法在这边
this.injectMappedStatement(mapperClass, modelClass, tableInfo);
}
再进入 injectMappedStatement()
方法,ctrl+Alt
选择你执行的 CRUD 操作。以 SelectById
为例,生成了 SqlSource
对象,再将 SQL 通过 addSelectMappedStatementForTable()
方法添加到 meppedStatements
中。
public class SelectById extends AbstractMethod {
public SelectById() {
this(SqlMethod.SELECT_BY_ID.getMethod());
}
public SelectById(String name) {
super(name);
}
public MappedStatement injectMappedStatement(Class<?> mapperClass, Class<?> modelClass, TableInfo tableInfo) {
SqlMethod sqlMethod = SqlMethod.SELECT_BY_ID;
SqlSource sqlSource = new RawSqlSource(this.configuration, String.format(sqlMethod.getSql(), this.sqlSelectColumns(tableInfo, false), tableInfo.getTableName(), tableInfo.getKeyColumn(), tableInfo.getKeyProperty(), tableInfo.getLogicDeleteSql(true, true)), Object.class);
// addSelectMappedStatementForTable()方法在这边
return this.addSelectMappedStatementForTable(mapperClass, this.methodName, sqlSource, tableInfo);
}
}
实现类是如何获取到 meppedStatements
返回值:在 AbstractSqlInjector
抽象类 inspectInject
方法从 this.getMethodList()
方法获取。
public abstract class AbstractSqlInjector implements ISqlInjector {
protected final Log logger = LogFactory.getLog(this.getClass());
public AbstractSqlInjector() {
}
public void inspectInject(MapperBuilderAssistant builderAssistant, Class<?> mapperClass) {
......
// 获取 CRUD 实现类列表
List<AbstractMethod> methodList = this.getMethodList(mapperClass, tableInfo);
......
}
// getMethodList()方法
public abstract List<AbstractMethod> getMethodList(Class<?> mapperClass, TableInfo tableInfo);
}
DefaultSqlInjector
中的 getMethodList()
方法获取CRUD实现类列表。
public class DefaultSqlInjector extends AbstractSqlInjector {
public DefaultSqlInjector() {
}
public List<AbstractMethod> getMethodList(Class<?> mapperClass, TableInfo tableInfo) {
Stream.Builder<AbstractMethod> builder = Stream.builder()
.add(new Insert())
.add(new Delete())
.add(new DeleteByMap())
.add(new Update())
.add(new SelectByMap())
.add(new SelectCount())
.add(new SelectMaps())
.add(new SelectMapsPage())
.add(new SelectObjs())
.add(new SelectList())
.add(new SelectPage());
if (tableInfo.havePK()) {
builder
.add(new DeleteById())
.add(new DeleteBatchByIds())
.add(new UpdateById())
.add(new SelectById())
.add(new SelectBatchByIds());
} else {
this.logger.warn(String.format("%s ,Not found @TableId annotation, Cannot use Mybatis-Plus 'xxById' Method.", tableInfo.getEntityType()));
}
return (List)builder.build().collect(Collectors.toList());
}
}
总结:项目启动时,首先由默认注入器DefaultSqlInjector生成基础 CRUD 实现类对象,其次遍历实现类列表,依次注入各自的模板 SQL,最后将其添加至 mappedstatement
。
SqlSource
通过解析SQL 模板、以及传入的表信息和主键信息构建出了 SQL 语句。
在 injectMappedStatement()
方法中,首先获取在mapper.xml method id
public MappedStatement injectMappedStatement(Class<?> mapperClass, Class<?> modelClass, TableInfo tableInfo) {
// 定义 mybatis xml method id, 对应
SqlMethod sqlMethod = SqlMethod.SELECT_BY_ID;
// 构造 id 对应的具体 xml 片段
SqlSource sqlSource = new RawSqlSource(this.configuration, String.format(sqlMethod.getSql(), this.sqlSelectColumns(tableInfo, false), tableInfo.getTableName(), tableInfo.getKeyColumn(), tableInfo.getKeyProperty(), tableInfo.getLogicDeleteSql(true, true)), Object.class);
// 将 xml method 方法添加到 mybatis 的 MappedStatement 中
return this.addSelectMappedStatementForTable(mapperClass, this.methodName, sqlSource, tableInfo);
}
根据 AbstractSqlInjector
抽象类的 inspectInject
方法中的 initTableInfo
方法获取数据库表信息。
public static synchronized TableInfo initTableInfo(MapperBuilderAssistant builderAssistant, Class<?> clazz) {
// 获取数据库表缓存信息
TableInfo targetTableInfo = (TableInfo)TABLE_INFO_CACHE.get(clazz);
Configuration configuration = builderAssistant.getConfiguration();
// 获取到缓存信息
if (targetTableInfo != null) {
Configuration oldConfiguration = targetTableInfo.getConfiguration();
// 配置信息变化,则初始化
if (!oldConfiguration.equals(configuration)) {
targetTableInfo = initTableInfo(configuration, builderAssistant.getCurrentNamespace(), clazz);
}
return targetTableInfo;
} else {
// 没有获取到缓存信息,则初始化
return initTableInfo(configuration, builderAssistant.getCurrentNamespace(), clazz);
}
}
private static synchronized TableInfo initTableInfo(Configuration configuration, String currentNamespace, Class<?> clazz) {
TableInfo tableInfo = new TableInfo(configuration, clazz);
tableInfo.setCurrentNamespace(currentNamespace);
GlobalConfig globalConfig = GlobalConfigUtils.getGlobalConfig(configuration);
// 初始化表名相关
String[] excludeProperty = initTableName(clazz, globalConfig, tableInfo);
List<String> excludePropertyList = excludeProperty != null && excludeProperty.length > 0 ? Arrays.asList(excludeProperty) : Collections.emptyList();
// 初始化字段相关
initTableFields(configuration, clazz, globalConfig, tableInfo, excludePropertyList);
// 自动构建 resultMap
tableInfo.initResultMapIfNeed();
globalConfig.getPostInitTableInfoHandler().postTableInfo(tableInfo, configuration);
// 放入缓存
TABLE_INFO_CACHE.put(clazz, tableInfo);
TABLE_NAME_INFO_CACHE.put(tableInfo.getTableName(), tableInfo);
// 缓存 lambda
LambdaUtils.installCache(tableInfo);
return tableInfo;
}
initTableName()
方法,获取表名信息源码中传入了实体类信息 class,其实就是通过实体上的@TableName 注解拿到了表名。
private static String[] initTableName(Class<?> clazz, GlobalConfig globalConfig, TableInfo tableInfo) {
GlobalConfig.DbConfig dbConfig = globalConfig.getDbConfig();
// 通过实体上的@TableName 注解拿到了表名
TableName table = (TableName)clazz.getAnnotation(TableName.class);
String tableName = clazz.getSimpleName();
String tablePrefix = dbConfig.getTablePrefix();
String schema = dbConfig.getSchema();
boolean tablePrefixEffect = true;
String[] excludeProperty = null;
if (table != null) {
if (StringUtils.isNotBlank(table.value())) {
tableName = table.value();
if (StringUtils.isNotBlank(tablePrefix) && !table.keepGlobalPrefix()) {
tablePrefixEffect = false;
}
} else {
tableName = initTableNameWithDbConfig(tableName, dbConfig);
}
if (StringUtils.isNotBlank(table.schema())) {
schema = table.schema();
}
if (StringUtils.isNotBlank(table.resultMap())) {
tableInfo.setResultMap(table.resultMap());
}
tableInfo.setAutoInitResultMap(table.autoResultMap());
excludeProperty = table.excludeProperty();
} else {
tableName = initTableNameWithDbConfig(tableName, dbConfig);
}
String targetTableName = tableName;
if (StringUtils.isNotBlank(tablePrefix) && tablePrefixEffect) {
targetTableName = tablePrefix + tableName;
}
if (StringUtils.isNotBlank(schema)) {
targetTableName = schema + "." + targetTableName;
}
tableInfo.setTableName(targetTableName);
if (CollectionUtils.isNotEmpty(dbConfig.getKeyGenerators())) {
tableInfo.setKeySequence((KeySequence)clazz.getAnnotation(KeySequence.class));
}
return excludeProperty;
}
根据 AbstractSqlInjector
抽象类的 inspectInject
方法中的 initTableFields
方法获取主键及其他字段信息。
private static void initTableFields(Configuration configuration, Class<?> clazz, GlobalConfig globalConfig, TableInfo tableInfo, List<String> excludeProperty) {
// 数据库全局配置
GlobalConfig.DbConfig dbConfig = globalConfig.getDbConfig();
PostInitTableInfoHandler postInitTableInfoHandler = globalConfig.getPostInitTableInfoHandler();
Reflector reflector = tableInfo.getReflector();
List<Field> list = getAllFields(clazz);
// 标记是否读取到主键
boolean isReadPK = false;
// 是否存在 @TableId 注解
boolean existTableId = isExistTableId(list);
boolean existTableLogic = isExistTableLogic(list);
List<TableFieldInfo> fieldList = new ArrayList(list.size());
Iterator var13 = list.iterator();
while(var13.hasNext()) {
Field field = (Field)var13.next();
if (!excludeProperty.contains(field.getName())) {
boolean isPK = false;
boolean isOrderBy = field.getAnnotation(OrderBy.class) != null;
if (existTableId) {
// 主键ID初始化
TableId tableId = (TableId)field.getAnnotation(TableId.class);
if (tableId != null) {
if (isReadPK) {
throw ExceptionUtils.mpe("@TableId can't more than one in Class: \"%s\".", new Object[]{clazz.getName()});
}
initTableIdWithAnnotation(dbConfig, tableInfo, field, tableId);
isReadPK = true;
isPK = true;
}
} else if (!isReadPK) {
isPK = isReadPK = initTableIdWithoutAnnotation(dbConfig, tableInfo, field);
}
if (isPK) {
if (isOrderBy) {
tableInfo.getOrderByFields().add(new TableFieldInfo(dbConfig, tableInfo, field, reflector, existTableLogic, true));
}
} else {
TableField tableField = (TableField)field.getAnnotation(TableField.class);
TableFieldInfo tableFieldInfo;
if (tableField != null) {
tableFieldInfo = new TableFieldInfo(dbConfig, tableInfo, field, tableField, reflector, existTableLogic, isOrderBy);
fieldList.add(tableFieldInfo);
postInitTableInfoHandler.postFieldInfo(tableFieldInfo, configuration);
} else {
tableFieldInfo = new TableFieldInfo(dbConfig, tableInfo, field, reflector, existTableLogic, isOrderBy);
fieldList.add(tableFieldInfo);
postInitTableInfoHandler.postFieldInfo(tableFieldInfo, configuration);
}
}
}
}
tableInfo.setFieldList(fieldList);
// 未发现主键注解,提示警告信息
if (!isReadPK) {
logger.warn(String.format("Can not find table primary key in Class: \"%s\".", clazz.getName()));
}
}