feature
- 1. Very lightweight, the entire framework only relies on Mybatis and no other third-party dependencies
- 2. Only enhanced, supports Entity addition, deletion, modification and query, and paging query, but does not lose the original functions of Mybatis
- 3. The built-in Db + Row tool can add, delete, modify and check the database without entity classes
- 4. Support multiple database types, and can also continue to expand through dialects
- 5. Support multiple (joint) primary keys and different primary key content generation strategies
- 6. Support tombstone settings, default value configuration for updates or inserts, and settings for large fields
- 7. Support optimistic lock field configuration, automatically perform optimistic lock detection when data is updated
- 8. Extremely friendly SQL linkage query, IDE automatically prompts no more worries about mistakes
- 9. More small surprises
QQ group
Group number: 532992631
start
hello world (native)
Step 1: Write the Entity entity class
@Table("tb_account")
public class Account {
@Id(keyType = KeyType.Auto)
private Long id;
private String userName;
private Date birthday;
private int sex;
//getter setter
}
Step 2: Start querying data
Example 1: Query 1 piece of data
class HelloWorld {
public static void main(String... args) {
HikariDataSource dataSource = new HikariDataSource();
dataSource.setJdbcUrl("jdbc:mysql://127.0.0.1:3306/mybatis-flex");
dataSource.setUsername("username");
dataSource.setPassword("password");
MybatisFlexBootstrap.getInstance()
.setDatasource(dataSource)
.addMapper(AccountMapper.class)
.start();
//示例1:查询 id=100 条数据
Account account = MybatisFlexBootstrap.getInstance()
.execute(AccountMapper.class, mapper ->
mapper.selectOneById(100)
);
}
}
Above
AccountMapper.class
It is automatically generated by APT for Mybatis-Flex without manual coding. You can also turn off the automatic generation function and write AccountMapper manually. See the APT documentation for more information.
Example 2: Query List
//示例2:通过 QueryWrapper 构建条件查询数据列表
QueryWrapper query=QueryWrapper.create()
.select()
.from(ACCOUNT)
.where(ACCOUNT.ID.ge(100))
.and(ACCOUNT.USER_NAME.like("张").or(ACCOUNT.USER_NAME.like("李")));
// 执行 SQL:
// ELECT * FROM tb_account
// WHERE tb_account.id >= 100
// AND (tb_account.user_name LIKE '%张%' OR tb_account.user_name LIKE '%李%' )
List<Account> accounts = MybatisFlexBootstrap.getInstance()
.execute(AccountMapper.class,mapper->
mapper.selectListByQuery(query)
);
Example 3: Paging query
// 示例3:分页查询
// 查询第 5 页,每页 10 条数据,通过 QueryWrapper 构建条件查询
QueryWrapper query=QueryWrapper.create()
.select()
.from(ACCOUNT)
.where(ACCOUNT.ID.ge(100))
.and(ACCOUNT.USER_NAME.like("张").or(ACCOUNT.USER_NAME.like("李")))
.orderBy(ACCOUNT.ID.desc());
// 执行 SQL:
// ELECT * FROM tb_account
// WHERE id >= 100
// AND (user_name LIKE '%张%' OR user_name LIKE '%李%' )
// ORDER BY `id` DESC
// LIMIT 40,10
Page<Account> accounts = MybatisFlexBootstrap.getInstance()
.execute(AccountMapper.class,mapper->
mapper.paginate(5,10,query)
);
QueryWrapper example
select*
QueryWrapper query=new QueryWrapper();
query.select().from(ACCOUNT)
// SQL:
// SELECT * FROM tb_account
select columns
Simple example:
QueryWrapper query=new QueryWrapper();
query.select(ACCOUNT.ID,ACCOUNT.USER_NAME)
.from(ACCOUNT)
// SQL:
// SELECT id, user_name
// FROM tb_account
Multi-table query (while showing powerful as
ability):
QueryWrapper query = new QueryWrapper()
.select(ACCOUNT.ID
, ACCOUNT.USER_NAME
, ARTICLE.ID.as("articleId")
, ARTICLE.TITLE)
.from(ACCOUNT.as("a"), ARTICLE.as("b"))
.where(ACCOUNT.ID.eq(ARTICLE.ACCOUNT_ID));
// SQL:
// SELECT a.id, a.user_name, b.id AS articleId, b.title
// FROM tb_account AS a, tb_article AS b
// WHERE a.id = b.account_id
select functions
QueryWrapper query=new QueryWrapper()
.select(
ACCOUNT.ID,
ACCOUNT.USER_NAME,
max(ACCOUNT.BIRTHDAY),
avg(ACCOUNT.SEX).as("sex_avg")
).from(ACCOUNT);
// SQL:
// SELECT id, user_name,
// MAX(birthday),
// AVG(sex) AS sex_avg
// FROM tb_account
where
QueryWrapper queryWrapper=QueryWrapper.create()
.select()
.from(ACCOUNT)
.where(ACCOUNT.ID.ge(100))
.and(ACCOUNT.USER_NAME.like("michael"));
// SQL:
// SELECT * FROM tb_account
// WHERE id >= ?
// AND user_name LIKE ?
where dynamic condition 1
boolean flag = false;
QueryWrapper queryWrapper = QueryWrapper.create()
.select().from(ACCOUNT)
.where(flag ? ACCOUNT.ID.ge(100) : noCondition())
.and(ACCOUNT.USER_NAME.like("michael"));
// SQL:
// SELECT * FROM tb_account
// WHERE user_name LIKE ?
where dynamic condition 2
boolean flag = false;
QueryWrapper queryWrapper = QueryWrapper.create()
.select().from(ACCOUNT)
.where(ACCOUNT.ID.ge(100).when(flag))
.and(ACCOUNT.USER_NAME.like("michael"));
// SQL:
// SELECT * FROM tb_account
// WHERE user_name LIKE ?
where select
QueryWrapper queryWrapper = QueryWrapper.create()
.select()
.from(ACCOUNT)
.where(ACCOUNT.ID.ge(
select(ARTICLE.ACCOUNT_ID).from(ARTICLE).where(ARTICLE.ID.ge(100))
));
// SQL:
// SELECT * FROM tb_account
// WHERE id >=
// (SELECT account_id FROM tb_article WHERE id >= ? )
exists, not exists
QueryWrapper queryWrapper=QueryWrapper.create()
.select()
.from(ACCOUNT)
.where(ACCOUNT.ID.ge(100))
.and(
exist( // or notExist(...)
selectOne().from(ARTICLE).where(ARTICLE.ID.ge(100))
)
);
// SQL:
// SELECT * FROM tb_account
// WHERE id >= ?
// AND EXIST (
// SELECT 1 FROM tb_article WHERE id >= ?
// )
and (…) or (…)
QueryWrapper queryWrapper=QueryWrapper.create()
.select()
.from(ACCOUNT)
.where(ACCOUNT.ID.ge(100))
.and(ACCOUNT.SEX.eq(1).or(ACCOUNT.SEX.eq(2)))
.or(ACCOUNT.AGE.in(18,19,20).and(ACCOUNT.USER_NAME.like("michael")));
// SQL:
// SELECT * FROM tb_account
// WHERE id >= ?
// AND (sex = ? OR sex = ? )
// OR (age IN (?,?,?) AND user_name LIKE ? )
group by
QueryWrapper queryWrapper=QueryWrapper.create()
.select()
.from(ACCOUNT)
.groupBy(ACCOUNT.USER_NAME);
// SQL:
// SELECT * FROM tb_account
// GROUP BY user_name
having
QueryWrapper queryWrapper=QueryWrapper.create()
.select()
.from(ACCOUNT)
.groupBy(ACCOUNT.USER_NAME)
.having(ACCOUNT.AGE.between(18,25));
// SQL:
// SELECT * FROM tb_account
// GROUP BY user_name
// HAVING age BETWEEN ? AND ?
orderBy
QueryWrapper queryWrapper=QueryWrapper.create()
.select()
.from(ACCOUNT)
.orderBy(ACCOUNT.AGE.asc()
, ACCOUNT.USER_NAME.desc().nullsLast());
// SQL:
// SELECT * FROM tb_account
// ORDER BY age ASC, user_name DESC NULLS LAST
join
QueryWrapper queryWrapper=QueryWrapper.create()
.select()
.from(ACCOUNT)
.leftJoin(ARTICLE).on(ACCOUNT.ID.eq(ARTICLE.ACCOUNT_ID))
.innerJoin(ARTICLE).on(ACCOUNT.ID.eq(ARTICLE.ACCOUNT_ID))
.where(ACCOUNT.AGE.ge(10));
// SQL:
// SELECT * FROM tb_account
// LEFT JOIN tb_article ON tb_account.id = tb_article.account_id
// INNER JOIN tb_article ON tb_account.id = tb_article.account_id
// WHERE tb_account.age >= ?
limit…offset
QueryWrapper queryWrapper = QueryWrapper.create()
.select()
.from(ACCOUNT)
.orderBy(ACCOUNT.ID.desc())
.limit(10)
.offset(20);
// MySql:
// SELECT * FROM `tb_account` ORDER BY `id` DESC LIMIT 20, 10
// PostgreSQL:
// SELECT * FROM "tb_account" ORDER BY "id" DESC LIMIT 20 OFFSET 10
// Informix:
// SELECT SKIP 20 FIRST 10 * FROM "tb_account" ORDER BY "id" DESC
// Oracle:
// SELECT * FROM (SELECT TEMP_DATAS.*,
// ROWNUM RN FROM (
// SELECT * FROM "tb_account" ORDER BY "id" DESC)
// TEMP_DATAS WHERE ROWNUM <=30)
// WHERE RN >20
// Db2:
// SELECT * FROM "tb_account" ORDER BY "id" DESC
// OFFSET 20 ROWS FETCH NEXT 10 ROWS ONLY
// Sybase:
// SELECT TOP 10 START AT 21 * FROM "tb_account" ORDER BY "id" DESC
// Firebird:
// SELECT * FROM "tb_account" ORDER BY "id" DESC ROWS 20 TO 30
In the above “limit…offset” example, Mybatis-Flex can automatically identify the current database and generate different SQL. Users can also easily pass
DialectFactory
Register (add or override) your own implementation dialect.
Have questions?
Question 1: Can QueryWrapper be transmitted via RPC in a distributed project?
Answer: Yes.
Question 2: How to generate the “ACCOUNT” class required by QueryWrapper through the entity class Account.java?
Answer: Mybatis-Flex uses APT (Annotation Processing Tool) technology. When the project is compiled, it will automatically generate the “ACCOUNT” class and the Mapper class corresponding to the Entity class according to the fields defined by the Entity class. Build the project through the development tool (as follows Figure), or execute the maven compilation command: mvn clean package
can be automatically generated. This principle is consistent with lombok.
For more information about Mybatis-Flex APT configuration, please click here.
Db + Row tool class
The Db + Row tool class provides database operation capabilities beyond the Entity entity class. When using Db + Row, there is no need to map the database table. Row is a subclass of HashMap, which is equivalent to a general Entity. Here are some examples of Db + Row:
//使用原生 SQL 插入数据
String sql="insert into tb_account(id,name) value (?, ?)";
Db.insertBySql(sql,1,"michael");
//使用 Row 插入数据
Row account=new Row();
account.set("id",100);
account.set("name","Michael");
Db.insertRow("tb_account",account);
//根据主键查询数据
Row row=Db.selectOneById("tb_account","id",1);
//Row 可以直接转换为 Entity 实体类,且性能极高
Account account=row.toEntity(Account.class);
//查询所有大于 18 岁的用户
String listsql="select * from tb_account where age > ?"
List<Row> rows=Db.selectListBySql(sql,18);
//分页查询:每页 10 条数据,查询第 3 页的年龄大于 18 的用户
QueryWrapper query=QueryWrapper.create()
.where(ACCOUNT.AGE.ge(18));
Page<Row> rowPage=Db.paginate("tb_account",3,10,query);
The Db tool class also provides more methods for adding, deleting, modifying, checking, and paging queries.
Specific reference: Db.java.
More about Row Insertionprimary key generation mechanismand Db’saffairs managementetc., please click here.
Entity partial field update
Compared with other frameworks on the market, this part of the function should also be regarded as one of the highlights of MyBatis-Flex. In BaseMapper, Mybatis-Flex provides the following methods:
update(T entity,boolean ignoreNulls)
- The first parameter is an object of entity.
- The second parameter is whether to ignore null values.
In some scenarios, we may want to update only a few fields, and some of them need to be updated to null.Need to use at this time UpdateEntity
Tool class, the following is the sample code:
Account account=UpdateEntity.of(Account.class);
account.setId(100);
account.setUserName(null);
account.setSex(1);
accountMapper.update(account,false);
In the above example, the user_name field in the data whose id is 100 will be updated to null, the sex field will be updated to 1, and other fields will not be updated.That is, through UpdateEntity
The created object will only update the field whose setter method is called. If the setter method is not called, no matter what the value of the attribute in this object is, it will not be updated to the database.
The generated sql content is as follows:
update tb_account
set user_name = ?, sex = ? where id = ?
#params: null,1,100
Custom TypeHandler
Use the @column annotation:
@Table("tb_account")
public class Account {
@Id(keyType = KeyType.Auto)
private Long id;
private String userName;
@Column(typeHandler = Fastjson2TypeHandler.class)
private Map<String, Object> options;
//getter setter
public void addOption(String key, Object value) {
if (options == null) {
options = new HashMap<>();
}
options.put(key, value);
}
}
Insert data:
Account account = new Account();
account.setUserName("test");
account.addOption("c1", 11);
account.addOption("c2", "zhang");
account.addOption("c3", new Date());
mybatis log:
==> Preparing: INSERT INTO tb_account (user_name, options) VALUES (?, ?)
==> Parameters: test(String), {"c3":"2023-03-17 09:10:16.546","c1":11,"c2":"zhang"}(String)
multiple primary keys
Mybatis-Flex multi-primary key means that there are multiple @Id
Annotation identification only, for example:
@Table("tb_account")
public class Account {
@Id(keyType = KeyType.Auto)
private Long id;
@Id(keyType = KeyType.Generator, value = "uuid")
private String otherId;
//getter setter
}
When we save the data, the id primary key of Account is self-incrementing, while the otherId primary key is generated by uuid.
custom primary key generator
Step 1: Write a class that implements IKeyGenerator
interface, for example:
public class UUIDKeyGenerator implements IKeyGenerator {
@Override
public Object generate(Object entity, String keyColumn) {
return UUID.randomUUID().toString().replace("-", "");
}
}
Step 2: Register UUIDKeyGenerator
KeyGeneratorFactory.register("myUUID",new UUIDKeyGenerator());
Step 3: Use the “myUUID” generator in Entity:
@Table("tb_account")
public class Account {
@Id(keyType = KeyType.Generator, value = "myUUID")
private String otherId;
//getter setter
}
Generated using database Sequence
@Table("tb_account")
public class Account {
@Id(keyType = KeyType.Sequence, value = "select SEQ_USER_ID.nextval as id from dual")
private Long id;
}
For more information about primary key configuration, please click here
more documents
Still have questions?
Join the QQ exchange group: 532992631
Another open source project from the team
React-Better-Admin is a background UI system developed based on React 18, TypeScript, Ant Design v5.x, and Redux. It is permanently free and commercially available based on the MIT open source protocol. Click here to learn more.
#MybatisFlex #MybatisFlex #elegant #Mybatis #enhancement #framework