How to Perform CRUD Operations with MyBatis-Flex BaseMapper
I needed to implement basic database operations for a new project, but I dreaded writing the same repetitive SQL statements for every table. I wanted something that would handle CRUD operations automatically without sacrificing flexibility.
That’s when I discovered MyBatis-Flex’s BaseMapper interface.
The Problem
Traditional MyBatis requires writing XML mapper files or annotations for even the simplest operations. Need to insert a record? Write SQL. Want to select by ID? Write SQL. Update a field? You guessed it—more SQL.
This leads to bloated mapper files filled with boilerplate code that’s essentially the same across all entities.
BaseMapper to the Rescue
MyBatis-Flex solves this by providing a BaseMapper interface with built-in CRUD methods. You just extend it, and you get insert, select, update, and delete operations without writing any SQL.
Let me show you how each operation works.
INSERT: Creating New Records
The insert() method persists a new entity and automatically populates the generated ID back into your object.
@SpringBootTestclass AccountMapperTest {
@Autowired private AccountMapper accountMapper;
@Test @Transactional void testInsert() { Account account = new Account(); account.setUserName("john_doe"); account.setAge(28); account.setStatus(1);
int rows = accountMapper.insert(account);
assertThat(rows).isEqualTo(1); assertThat(account.getId()).isNotNull(); // ID populated automatically }}The entity uses standard JPA annotations:
@Table("tb_account")public class Account {
@Id(keyType = KeyType.Auto) private Long id;
private String userName; private Integer age; private Integer status;
// getters and setters}Your mapper interface simply extends BaseMapper:
public interface AccountMapper extends BaseMapper<Account> { // No SQL needed - all CRUD methods inherited}READ: Retrieving Records
The selectOneById() method fetches a single record by its primary key. It returns null when no record exists instead of throwing an exception.
@Testvoid testSelectOneById() { Account account = accountMapper.selectOneById(1L);
assertThat(account).isNotNull(); assertThat(account.getUserName()).isEqualTo("john_doe"); assertThat(account.getAge()).isEqualTo(28);}
@Testvoid testSelectOneByIdNotFound() { Account account = accountMapper.selectOneById(999L);
assertThat(account).isNull(); // Returns null, not an exception}For more complex queries, you can use the QueryWrapper:
@Testvoid testSelectByCondition() { List<Account> accounts = accountMapper.selectListByQuery( QueryWrapper.create() .where(Account::getAge).ge(25) .and(Account::getStatus).eq(1) );
assertThat(accounts).isNotEmpty();}UPDATE: Modifying Existing Records
The update() method modifies an existing entity. You typically load the entity first, modify it, then save.
@Test@Transactionalvoid testUpdate() { // Load existing record Account account = accountMapper.selectOneById(1L); assertThat(account).isNotNull();
// Modify fields account.setStatus(0);
// Persist changes int rows = accountMapper.update(account);
assertThat(rows).isEqualTo(1);
// Verify the change Account updated = accountMapper.selectOneById(1L); assertThat(updated.getStatus()).isEqualTo(0);}You can also update specific columns without loading the entire entity:
@Test@Transactionalvoid testUpdateSpecificColumns() { Account account = new Account(); account.setId(1L); account.setStatus(0);
int rows = accountMapper.update(account);
assertThat(rows).isEqualTo(1);}DELETE: Removing Records
The deleteById() method removes a record by its primary key.
@Test@Transactionalvoid testDelete() { // First, verify record exists Account before = accountMapper.selectOneById(2L); assertThat(before).isNotNull();
// Delete the record int rows = accountMapper.deleteById(2L); assertThat(rows).isEqualTo(1);
// Verify deletion Account after = accountMapper.selectOneById(2L); assertThat(after).isNull();}Batch deletion is equally straightforward:
@Test@Transactionalvoid testBatchDelete() { int rows = accountMapper.deleteByIds(Arrays.asList(1L, 2L, 3L));
assertThat(rows).isEqualTo(3);}Why This Matters
With traditional MyBatis, implementing CRUD for a single entity might require:
- 4+ XML mapper methods
- Corresponding SQL statements
- Parameter type definitions
- Result mapping configuration
With MyBatis-Flex BaseMapper, you get all of this by simply extending an interface:
public interface AccountMapper extends BaseMapper<Account> { // That's it - you're done}The @Transactional annotation in the tests ensures each test runs in isolation, rolling back changes after completion. This keeps your test database clean.
Summary
In this post, I showed you how to perform CRUD operations using MyBatis-Flex BaseMapper. The insert() method creates records and populates generated IDs, selectOneById() retrieves records by primary key, update() modifies existing entities, and deleteById() removes records. All without writing a single SQL statement.
If you’re starting a new project with MyBatis-Flex, BaseMapper should be your first choice for standard CRUD operations. Write custom SQL only when you need something beyond basic persistence patterns.
Final Words + More Resources
My intention with this article was to help others share my knowledge and experience. If you want to contact me, you can contact by email: Email me
Here are also the most important links from this article along with some further resources that will help you in this scope:
Oh, and if you found these resources useful, don’t forget to support me by starring the repo on GitHub!
Comments