MyBatis-Plus is an enhanced toolkit for MyBatis providing CRUD operations, query wrappers, pagination, code generation, and Spring Boot integration.
—
MyBatis-Plus provides a high-level service layer abstraction with the IService interface and ServiceImpl base class, offering business-logic-focused methods, batch operations, and convenient utilities built on top of BaseMapper functionality.
High-level service interface providing comprehensive business operations with batch processing and convenience methods.
/**
* Service interface providing business-logic-focused operations
* @param <T> Entity type
*/
public interface IService<T> {
// ==================== Save Operations ====================
/**
* Save a single entity
* @param entity Entity to save
* @return true if successful
*/
boolean save(T entity);
/**
* Save multiple entities in batch
* @param entityList List of entities to save
* @return true if all successful
*/
boolean saveBatch(Collection<T> entityList);
/**
* Save multiple entities in batch with batch size control
* @param entityList List of entities to save
* @param batchSize Batch size for processing
* @return true if all successful
*/
boolean saveBatch(Collection<T> entityList, int batchSize);
/**
* Save or update entity (insert if not exists, update if exists)
* @param entity Entity to save or update
* @return true if successful
*/
boolean saveOrUpdate(T entity);
/**
* Save or update multiple entities in batch
* @param entityList List of entities to save or update
* @return true if all successful
*/
boolean saveOrUpdateBatch(Collection<T> entityList);
/**
* Save or update multiple entities in batch with batch size control
* @param entityList List of entities to save or update
* @param batchSize Batch size for processing
* @return true if all successful
*/
boolean saveOrUpdateBatch(Collection<T> entityList, int batchSize);
// ==================== Remove Operations ====================
/**
* Remove entity by primary key
* @param id Primary key value
* @return true if successful
*/
boolean removeById(Serializable id);
/**
* Remove entities by column map conditions
* @param columnMap Column conditions map
* @return true if successful
*/
boolean removeByMap(Map<String, Object> columnMap);
/**
* Remove entities by query wrapper conditions
* @param queryWrapper Query conditions wrapper
* @return true if successful
*/
boolean remove(Wrapper<T> queryWrapper);
/**
* Remove multiple entities by primary keys
* @param idList Collection of primary key values
* @return true if successful
*/
boolean removeByIds(Collection<? extends Serializable> idList);
// ==================== Update Operations ====================
/**
* Update entity by primary key (non-null fields only)
* @param entity Entity with primary key and new values
* @return true if successful
*/
boolean updateById(T entity);
/**
* Update entities by conditions
* @param updateWrapper Update conditions wrapper
* @return true if successful
*/
boolean update(Wrapper<T> updateWrapper);
/**
* Update entities with new values by conditions
* @param entity Entity with new values
* @param updateWrapper Update conditions wrapper
* @return true if successful
*/
boolean update(T entity, Wrapper<T> updateWrapper);
/**
* Update multiple entities by primary keys in batch
* @param entityList List of entities with primary keys and new values
* @return true if all successful
*/
boolean updateBatchById(Collection<T> entityList);
/**
* Update multiple entities by primary keys in batch with batch size control
* @param entityList List of entities with primary keys and new values
* @param batchSize Batch size for processing
* @return true if all successful
*/
boolean updateBatchById(Collection<T> entityList, int batchSize);
// ==================== Get Operations ====================
/**
* Get entity by primary key
* @param id Primary key value
* @return Entity or null if not found
*/
T getById(Serializable id);
/**
* Get entities by primary keys
* @param idList Collection of primary key values
* @return List of entities
*/
List<T> listByIds(Collection<? extends Serializable> idList);
/**
* Get entities by column map conditions
* @param columnMap Column conditions map
* @return List of entities
*/
List<T> listByMap(Map<String, Object> columnMap);
/**
* Get single entity by query wrapper conditions
* @param queryWrapper Query conditions wrapper
* @return Single entity or null
* @throws TooManyResultsException if more than one result found
*/
T getOne(Wrapper<T> queryWrapper);
/**
* Get single entity by query wrapper conditions with strict mode control
* @param queryWrapper Query conditions wrapper
* @param throwEx Whether to throw exception if more than one result
* @return Single entity or null
*/
T getOne(Wrapper<T> queryWrapper, boolean throwEx);
/**
* Get single map by query wrapper conditions
* @param queryWrapper Query conditions wrapper
* @return Single map or null
*/
Map<String, Object> getMap(Wrapper<T> queryWrapper);
/**
* Get single object by query wrapper conditions
* @param queryWrapper Query conditions wrapper
* @param mapper Function to transform the result
* @return Transformed object or null
*/
<V> V getObj(Wrapper<T> queryWrapper, Function<? super Object, V> mapper);
// ==================== Count Operations ====================
/**
* Count all entities
* @return Total count
*/
long count();
/**
* Count entities by query wrapper conditions
* @param queryWrapper Query conditions wrapper
* @return Count matching conditions
*/
long count(Wrapper<T> queryWrapper);
// ==================== List Operations ====================
/**
* List all entities
* @return List of all entities
*/
List<T> list();
/**
* List entities by query wrapper conditions
* @param queryWrapper Query conditions wrapper
* @return List of matching entities
*/
List<T> list(Wrapper<T> queryWrapper);
/**
* Get paginated entities
* @param page Pagination parameters
* @return Paginated results
*/
<E extends IPage<T>> E page(E page);
/**
* Get paginated entities by query wrapper conditions
* @param page Pagination parameters
* @param queryWrapper Query conditions wrapper
* @return Paginated results
*/
<E extends IPage<T>> E page(E page, Wrapper<T> queryWrapper);
/**
* List all entities as maps
* @return List of maps representing entities
*/
List<Map<String, Object>> listMaps();
/**
* List entities as maps by query wrapper conditions
* @param queryWrapper Query conditions wrapper
* @return List of maps matching conditions
*/
List<Map<String, Object>> listMaps(Wrapper<T> queryWrapper);
/**
* List all entities as objects (single column selection)
* @return List of objects
*/
List<Object> listObjs();
/**
* List all entities as transformed objects
* @param mapper Function to transform each result
* @return List of transformed objects
*/
<V> List<V> listObjs(Function<? super Object, V> mapper);
/**
* List entities as objects by query wrapper conditions
* @param queryWrapper Query conditions wrapper
* @return List of objects matching conditions
*/
List<Object> listObjs(Wrapper<T> queryWrapper);
/**
* List entities as transformed objects by query wrapper conditions
* @param queryWrapper Query conditions wrapper
* @param mapper Function to transform each result
* @return List of transformed objects matching conditions
*/
<V> List<V> listObjs(Wrapper<T> queryWrapper, Function<? super Object, V> mapper);
/**
* Get paginated maps by query wrapper conditions
* @param page Pagination parameters
* @param queryWrapper Query conditions wrapper
* @return Paginated map results
*/
<E extends IPage<Map<String, Object>>> E pageMaps(E page, Wrapper<T> queryWrapper);
// ==================== Chain Query Operations ====================
/**
* Create a lambda query chain for fluent querying
* @return LambdaQueryChainWrapper instance
*/
default LambdaQueryChainWrapper<T> lambdaQuery() {
return ChainWrappers.lambdaQueryChain(getBaseMapper());
}
/**
* Create a lambda update chain for fluent updating
* @return LambdaUpdateChainWrapper instance
*/
default LambdaUpdateChainWrapper<T> lambdaUpdate() {
return ChainWrappers.lambdaUpdateChain(getBaseMapper());
}
/**
* Create a query chain for fluent querying
* @return QueryChainWrapper instance
*/
default QueryChainWrapper<T> query() {
return ChainWrappers.queryChain(getBaseMapper());
}
/**
* Create an update chain for fluent updating
* @return UpdateChainWrapper instance
*/
default UpdateChainWrapper<T> update() {
return ChainWrappers.updateChain(getBaseMapper());
}
// ==================== Mapper Access ====================
/**
* Get the underlying BaseMapper instance
* @return BaseMapper instance
*/
BaseMapper<T> getBaseMapper();
/**
* Get the entity class
* @return Entity class
*/
Class<T> getEntityClass();
}Abstract base class implementing IService interface with BaseMapper integration.
/**
* Service implementation base class
* @param <M> Mapper type extending BaseMapper
* @param <T> Entity type
*/
public class ServiceImpl<M extends BaseMapper<T>, T> implements IService<T> {
/**
* BaseMapper instance (injected automatically)
*/
@Autowired
protected M baseMapper;
/**
* Logger instance
*/
protected Log log = LogFactory.getLog(getClass());
/**
* Get the BaseMapper instance
* @return BaseMapper instance
*/
@Override
public M getBaseMapper() {
return baseMapper;
}
/**
* Get the entity class (automatically detected)
* @return Entity class
*/
@Override
public Class<T> getEntityClass() {
return (Class<T>) ReflectionKit.getSuperClassGenericType(this.getClass(), ServiceImpl.class, 1);
}
// All IService methods are implemented using BaseMapper operations
// with appropriate error handling and transaction management
}Static utility class providing direct access to service operations without dependency injection, useful for utility methods and static contexts.
/**
* Static service utility class for database operations
*/
public final class Db {
// ==================== Save Operations ====================
/**
* Save a single entity statically
* @param entity Entity to save
* @return true if successful
*/
public static <T> boolean save(T entity);
/**
* Save multiple entities in batch statically
* @param entityList List of entities to save
* @return true if all successful
*/
public static <T> boolean saveBatch(Collection<T> entityList);
/**
* Save multiple entities in batch with batch size control
* @param entityList List of entities to save
* @param batchSize Batch size for processing
* @return true if all successful
*/
public static <T> boolean saveBatch(Collection<T> entityList, int batchSize);
/**
* Save or update entity statically
* @param entity Entity to save or update
* @return true if successful
*/
public static <T> boolean saveOrUpdate(T entity);
/**
* Save or update multiple entities in batch statically
* @param entityList List of entities to save or update
* @return true if all successful
*/
public static <T> boolean saveOrUpdateBatch(Collection<T> entityList);
// ==================== Remove Operations ====================
/**
* Remove entity by primary key statically
* @param entity Entity with ID to remove (or just ID value)
* @return true if successful
*/
public static <T> boolean removeById(T entity);
/**
* Remove entity by primary key value statically
* @param id Primary key value
* @param entityClass Entity class
* @return true if successful
*/
public static <T> boolean removeById(Serializable id, Class<T> entityClass);
/**
* Remove multiple entities by primary keys statically
* @param idList Collection of primary key values
* @param entityClass Entity class
* @return true if successful
*/
public static <T> boolean removeByIds(Collection<? extends Serializable> idList, Class<T> entityClass);
// ==================== Update Operations ====================
/**
* Update entity by primary key statically
* @param entity Entity with primary key and new values
* @return true if successful
*/
public static <T> boolean updateById(T entity);
/**
* Update multiple entities by primary keys statically
* @param entityList List of entities with primary keys and new values
* @return true if all successful
*/
public static <T> boolean updateBatchById(Collection<T> entityList);
// ==================== Query Operations ====================
/**
* Get entity by primary key statically
* @param id Primary key value
* @param entityClass Entity class
* @return Entity or null if not found
*/
public static <T> T getById(Serializable id, Class<T> entityClass);
/**
* Get multiple entities by primary keys statically
* @param idList Collection of primary key values
* @param entityClass Entity class
* @return List of entities
*/
public static <T> List<T> listByIds(Collection<? extends Serializable> idList, Class<T> entityClass);
/**
* List all entities statically
* @param entityClass Entity class
* @return List of all entities
*/
public static <T> List<T> list(Class<T> entityClass);
/**
* List entities by query wrapper statically
* @param queryWrapper Query conditions wrapper
* @param entityClass Entity class
* @return List of matching entities
*/
public static <T> List<T> list(Wrapper<T> queryWrapper, Class<T> entityClass);
/**
* Count entities statically
* @param entityClass Entity class
* @return Total count
*/
public static <T> long count(Class<T> entityClass);
/**
* Count entities by query wrapper statically
* @param queryWrapper Query conditions wrapper
* @param entityClass Entity class
* @return Count of matching entities
*/
public static <T> long count(Wrapper<T> queryWrapper, Class<T> entityClass);
/**
* Get paginated entities statically
* @param page Pagination parameters
* @param entityClass Entity class
* @return Paginated results
*/
public static <T> IPage<T> page(IPage<T> page, Class<T> entityClass);
/**
* Get paginated entities by query wrapper statically
* @param page Pagination parameters
* @param queryWrapper Query conditions wrapper
* @param entityClass Entity class
* @return Paginated results
*/
public static <T> IPage<T> page(IPage<T> page, Wrapper<T> queryWrapper, Class<T> entityClass);
// ==================== Chain Operations ====================
/**
* Create lambda query chain statically
* @param entityClass Entity class
* @return LambdaQueryChainWrapper instance
*/
public static <T> LambdaQueryChainWrapper<T> lambdaQuery(Class<T> entityClass);
/**
* Create lambda update chain statically
* @param entityClass Entity class
* @return LambdaUpdateChainWrapper instance
*/
public static <T> LambdaUpdateChainWrapper<T> lambdaUpdate(Class<T> entityClass);
}Basic Service Setup:
// Entity class
@TableName("user")
public class User {
@TableId(type = IdType.AUTO)
private Long id;
private String name;
private Integer age;
private String email;
private LocalDateTime createTime;
// constructors, getters, setters...
}
// Mapper interface
public interface UserMapper extends BaseMapper<User> {
}
// Service interface
public interface UserService extends IService<User> {
// Custom business methods can be added here
}
// Service implementation
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
}Save Operations:
@Autowired
private UserService userService;
// Save single entity
User user = new User();
user.setName("John Doe");
user.setAge(25);
user.setEmail("john@example.com");
boolean success = userService.save(user);
// user.getId() will contain the generated ID
// Save multiple entities
List<User> users = Arrays.asList(
new User("Alice", 22, "alice@example.com"),
new User("Bob", 28, "bob@example.com"),
new User("Charlie", 35, "charlie@example.com")
);
boolean success = userService.saveBatch(users);
// Save with batch size control (for large datasets)
boolean success = userService.saveBatch(users, 100);
// Save or update (insert if new, update if exists)
User user = new User();
user.setId(1L); // If ID exists, it will update; otherwise insert
user.setName("Updated Name");
user.setAge(26);
boolean success = userService.saveOrUpdate(user);
// Batch save or update
List<User> users = getUsers(); // Mix of new and existing users
boolean success = userService.saveOrUpdateBatch(users);Remove Operations:
// Remove by ID
boolean success = userService.removeById(1L);
// Remove by conditions map
Map<String, Object> conditions = new HashMap<>();
conditions.put("age", 25);
conditions.put("status", 0);
boolean success = userService.removeByMap(conditions);
// Remove by query wrapper
boolean success = userService.remove(
Wrappers.<User>lambdaQuery()
.eq(User::getAge, 25)
.like(User::getName, "test")
);
// Remove multiple by IDs
List<Long> ids = Arrays.asList(1L, 2L, 3L);
boolean success = userService.removeByIds(ids);Update Operations:
// Update by ID (only non-null fields)
User user = new User();
user.setId(1L);
user.setAge(26); // Only age will be updated
boolean success = userService.updateById(user);
// Update by conditions
boolean success = userService.update(
Wrappers.<User>lambdaUpdate()
.set(User::getStatus, 1)
.set(User::getUpdateTime, LocalDateTime.now())
.eq(User::getAge, 25)
);
// Update with entity and conditions
User updateUser = new User();
updateUser.setStatus(1);
boolean success = userService.update(updateUser,
Wrappers.<User>lambdaQuery()
.eq(User::getDeptId, 10)
.gt(User::getAge, 18)
);
// Batch update by IDs
List<User> users = getUsersToUpdate();
boolean success = userService.updateBatchById(users);
// Batch update with batch size control
boolean success = userService.updateBatchById(users, 50);Query Operations:
// Get by ID
User user = userService.getById(1L);
// Get multiple by IDs
List<Long> ids = Arrays.asList(1L, 2L, 3L);
List<User> users = userService.listByIds(ids);
// Get by conditions map
Map<String, Object> conditions = new HashMap<>();
conditions.put("age", 25);
conditions.put("status", 1);
List<User> users = userService.listByMap(conditions);
// Get one by conditions (throws exception if multiple results)
User user = userService.getOne(
Wrappers.<User>lambdaQuery()
.eq(User::getEmail, "john@example.com")
);
// Get one without exception (returns first if multiple)
User user = userService.getOne(
Wrappers.<User>lambdaQuery()
.eq(User::getAge, 25), false
);
// Count operations
long totalUsers = userService.count();
long activeUsers = userService.count(
Wrappers.<User>lambdaQuery()
.eq(User::getStatus, 1)
);
// List operations
List<User> allUsers = userService.list();
List<User> activeUsers = userService.list(
Wrappers.<User>lambdaQuery()
.eq(User::getStatus, 1)
.orderByDesc(User::getCreateTime)
);
// Pagination
Page<User> page = new Page<>(1, 10);
IPage<User> userPage = userService.page(page,
Wrappers.<User>lambdaQuery()
.eq(User::getStatus, 1)
.orderByDesc(User::getCreateTime)
);
System.out.println("Total: " + userPage.getTotal());
System.out.println("Pages: " + userPage.getPages());
System.out.println("Current: " + userPage.getCurrent());
System.out.println("Records: " + userPage.getRecords().size());
// List as maps
List<Map<String, Object>> userMaps = userService.listMaps(
Wrappers.<User>lambdaQuery()
.select(User::getId, User::getName, User::getEmail)
.eq(User::getStatus, 1)
);
// List as objects (single column)
List<Object> names = userService.listObjs(
Wrappers.<User>lambdaQuery()
.select(User::getName)
.eq(User::getStatus, 1)
);
// List as transformed objects
List<String> upperNames = userService.listObjs(
Wrappers.<User>lambdaQuery()
.select(User::getName)
.eq(User::getStatus, 1),
obj -> obj.toString().toUpperCase()
);Chain Query Operations:
// Lambda query chain
List<User> users = userService.lambdaQuery()
.eq(User::getStatus, 1)
.gt(User::getAge, 18)
.orderByDesc(User::getCreateTime)
.list();
User user = userService.lambdaQuery()
.eq(User::getEmail, "john@example.com")
.one();
long count = userService.lambdaQuery()
.eq(User::getStatus, 1)
.count();
// Lambda update chain
boolean success = userService.lambdaUpdate()
.set(User::getStatus, 0)
.set(User::getUpdateTime, LocalDateTime.now())
.eq(User::getId, 1L)
.update();
// Query chain (string-based)
List<User> users = userService.query()
.eq("status", 1)
.gt("age", 18)
.orderByDesc("create_time")
.list();
// Update chain (string-based)
boolean success = userService.update()
.set("status", 0)
.set("update_time", LocalDateTime.now())
.eq("id", 1L)
.update();Custom Service Methods:
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
@Override
public List<User> findActiveUsersByDepartment(Long deptId) {
return this.lambdaQuery()
.eq(User::getDeptId, deptId)
.eq(User::getStatus, 1)
.orderByDesc(User::getCreateTime)
.list();
}
@Override
@Transactional
public boolean batchUpdateUserStatus(List<Long> userIds, Integer status) {
return this.lambdaUpdate()
.set(User::getStatus, status)
.set(User::getUpdateTime, LocalDateTime.now())
.in(User::getId, userIds)
.update();
}
@Override
public IPage<User> findUsersByCondition(UserQueryRequest request) {
LambdaQueryWrapper<User> wrapper = this.lambdaQuery()
.like(StringUtils.isNotBlank(request.getName()), User::getName, request.getName())
.eq(request.getStatus() != null, User::getStatus, request.getStatus())
.ge(request.getMinAge() != null, User::getAge, request.getMinAge())
.le(request.getMaxAge() != null, User::getAge, request.getMaxAge())
.orderByDesc(User::getCreateTime)
.getWrapper();
return this.page(new Page<>(request.getCurrent(), request.getSize()), wrapper);
}
}Static Db Utility Usage (MyBatis-Plus 3.5.7+):
import com.baomidou.mybatisplus.extension.toolkit.Db;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
// Save operations without service injection
User user = new User("John", 25, "john@example.com");
boolean success = Db.save(user);
// Batch save
List<User> users = Arrays.asList(
new User("Alice", 22, "alice@example.com"),
new User("Bob", 28, "bob@example.com")
);
boolean success = Db.saveBatch(users);
// Save or update
User existingUser = Db.getById(1L, User.class);
if (existingUser != null) {
existingUser.setAge(26);
Db.saveOrUpdate(existingUser);
}
// Query operations
User user = Db.getById(1L, User.class);
List<User> users = Db.listByIds(Arrays.asList(1L, 2L, 3L), User.class);
List<User> allUsers = Db.list(User.class);
// Query with conditions
LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(User::getStatus, 1).gt(User::getAge, 18);
List<User> activeAdults = Db.list(wrapper, User.class);
// Count operations
long totalUsers = Db.count(User.class);
long activeUsers = Db.count(
new LambdaQueryWrapper<User>().eq(User::getStatus, 1),
User.class
);
// Update operations
User user = new User();
user.setId(1L);
user.setAge(26);
boolean success = Db.updateById(user);
// Remove operations
boolean success = Db.removeById(1L, User.class);
List<Long> ids = Arrays.asList(1L, 2L, 3L);
boolean success = Db.removeByIds(ids, User.class);
// Pagination
Page<User> page = new Page<>(1, 10);
IPage<User> result = Db.page(page, User.class);
// Chain operations with static access
List<User> users = Db.lambdaQuery(User.class)
.eq(User::getStatus, 1)
.gt(User::getAge, 18)
.orderByDesc(User::getCreateTime)
.list();
boolean updateSuccess = Db.lambdaUpdate(User.class)
.set(User::getStatus, 0)
.eq(User::getId, 1L)
.update();
// Useful in utility classes or static methods
public class UserUtils {
public static List<User> getActiveUsersByDepartment(Long deptId) {
return Db.lambdaQuery(User.class)
.eq(User::getDeptId, deptId)
.eq(User::getStatus, 1)
.list();
}
public static boolean deactivateExpiredUsers() {
return Db.lambdaUpdate(User.class)
.set(User::getStatus, 0)
.lt(User::getExpireTime, LocalDateTime.now())
.update();
}
}Error Handling:
try {
boolean success = userService.save(user);
if (!success) {
throw new BusinessException("Failed to save user");
}
} catch (DuplicateKeyException e) {
throw new BusinessException("User email already exists", e);
} catch (DataIntegrityViolationException e) {
throw new BusinessException("Data integrity violation", e);
}
// Using getOne with exception control
try {
User user = userService.getOne(
Wrappers.<User>lambdaQuery().eq(User::getEmail, email)
);
} catch (TooManyResultsException e) {
throw new BusinessException("Multiple users found with same email", e);
}Performance Considerations:
saveBatch, updateBatchById) for multiple recordscount() instead of list().size() for countingselect() methodsgetOne(wrapper, false) to avoid exceptions when multiple results expectedInstall with Tessl CLI
npx tessl i tessl/maven-com-baomidou--mybatis-plus