CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-com-baomidou--mybatis-plus-extension

MyBatis-Plus Extension module providing advanced features including service layers, Kotlin extensions, SQL parsers, caching mechanisms, and various interceptors for enhanced ORM functionality

Pending
Overview
Eval results
Files

active-record.mddocs/

Active Record Pattern

MyBatis-Plus Extension provides Active Record pattern support through the Model base class, enabling entity objects to perform their own database operations. This pattern allows entities to encapsulate both data and database behavior, providing a more object-oriented approach to data access.

Core Components

Model Base Class

The abstract Model<T> class that entities can extend to gain Active Record capabilities.

public abstract class Model<T extends Model<?>> implements Serializable, Cloneable {
    
    // Insert operations
    public boolean insert();
    public boolean insertOrUpdate();
    
    // Delete operations
    public boolean deleteById();
    public boolean deleteById(Serializable id);
    public boolean delete(Wrapper<T> queryWrapper);
    
    // Update operations
    public boolean updateById();
    public boolean update(Wrapper<T> updateWrapper);
    
    // Select operations
    public T selectById();
    public T selectById(Serializable id);
    public T selectOne(Wrapper<T> queryWrapper);
    public List<T> selectAll();
    public List<T> selectList(Wrapper<T> queryWrapper);
    public List<T> selectByIds(Collection<? extends Serializable> idList);
    public List<T> selectByMap(Map<String, Object> columnMap);
    public <E extends IPage<T>> E selectPage(E page, Wrapper<T> queryWrapper);
    public long selectCount(Wrapper<T> queryWrapper);
    
    // Utility operations
    public SqlRunner sql();
    protected abstract Serializable pkVal();
}

Usage Examples

Entity Definition

@TableName("user")
public class User extends Model<User> {
    
    @TableId(type = IdType.AUTO)
    private Long id;
    
    private String name;
    private String email;
    private Integer age;
    private Boolean active;
    private LocalDateTime createdTime;
    private LocalDateTime updatedTime;
    
    // Constructors
    public User() {}
    
    public User(String name, String email) {
        this.name = name;
        this.email = email;
        this.active = true;
        this.createdTime = LocalDateTime.now();
    }
    
    // Required: implement pkVal() method
    @Override
    protected Serializable pkVal() {
        return this.id;
    }
    
    // Getters and setters
    public Long getId() { return id; }
    public void setId(Long id) { this.id = id; }
    
    public String getName() { return name; }
    public void setName(String name) { this.name = name; }
    
    public String getEmail() { return email; }
    public void setEmail(String email) { this.email = email; }
    
    public Integer getAge() { return age; }
    public void setAge(Integer age) { this.age = age; }
    
    public Boolean getActive() { return active; }
    public void setActive(Boolean active) { this.active = active; }
    
    public LocalDateTime getCreatedTime() { return createdTime; }
    public void setCreatedTime(LocalDateTime createdTime) { this.createdTime = createdTime; }
    
    public LocalDateTime getUpdatedTime() { return updatedTime; }
    public void setUpdatedTime(LocalDateTime updatedTime) { this.updatedTime = updatedTime; }
}

Insert Operations

// Create and insert new user
User newUser = new User("John Doe", "john@example.com");
newUser.setAge(30);

boolean inserted = newUser.insert();
if (inserted) {
    System.out.println("User inserted with ID: " + newUser.getId());
}

// Insert or update based on primary key
User user = new User("Jane Smith", "jane@example.com");
user.setId(1L); // If ID exists, it will update; otherwise insert
boolean success = user.insertOrUpdate();

Select Operations

// Select by primary key
User user = new User();
User foundUser = user.selectById(1L);

if (foundUser != null) {
    System.out.println("Found user: " + foundUser.getName());
}

// Select current instance by its ID
User user = new User();
user.setId(1L);
User reloaded = user.selectById(); // Uses the current instance's ID

// Select all users
User user = new User();
List<User> allUsers = user.selectAll();

// Select with conditions
User user = new User();
List<User> activeUsers = user.selectList(
    new QueryWrapper<User>().eq("active", true)
);

// Select single user with conditions
User user = new User();
User activeUser = user.selectOne(
    new QueryWrapper<User>()
        .eq("active", true)
        .eq("email", "john@example.com")
);

// Select by IDs
User user = new User();
List<User> users = user.selectByIds(Arrays.asList(1L, 2L, 3L));

// Select by column map
User user = new User();
Map<String, Object> conditions = new HashMap<>();
conditions.put("active", true);
conditions.put("age", 30);
List<User> users = user.selectByMap(conditions);

Update Operations

// Update by primary key
User user = new User();
user.setId(1L);
user.setName("Updated Name");
user.setEmail("updated@example.com");
user.setUpdatedTime(LocalDateTime.now());

boolean updated = user.updateById();
if (updated) {
    System.out.println("User updated successfully");
}

// Update with conditions
User user = new User();
user.setActive(false);
user.setUpdatedTime(LocalDateTime.now());

boolean updated = user.update(
    new UpdateWrapper<User>().eq("age", 25)
);

Delete Operations

// Delete by primary key (current instance)
User user = new User();
user.setId(1L);
boolean deleted = user.deleteById();

// Delete by specific ID
User user = new User();
boolean deleted = user.deleteById(2L);

// Delete with conditions
User user = new User();
boolean deleted = user.delete(
    new QueryWrapper<User>()
        .eq("active", false)
        .lt("created_time", LocalDateTime.now().minusYears(1))
);

Count Operations

// Count all records
User user = new User();
long totalUsers = user.selectCount(null);

// Count with conditions
User user = new User();
long activeUsers = user.selectCount(
    new QueryWrapper<User>().eq("active", true)
);

long adultUsers = user.selectCount(
    new QueryWrapper<User>().ge("age", 18)
);

Pagination with Active Record

// Paginated selection
User user = new User();
Page<User> page = new Page<>(1, 10);

Page<User> result = user.selectPage(page, 
    new QueryWrapper<User>().eq("active", true).orderByDesc("created_time")
);

List<User> users = result.getRecords();
long total = result.getTotal();
System.out.println("Found " + total + " active users");

// Pagination without conditions
Page<User> allUsersPage = user.selectPage(new Page<>(1, 20), null);

Advanced Usage

Custom Entity Methods

@TableName("user")
public class User extends Model<User> {
    // ... fields and basic methods ...
    
    // Custom business methods using Active Record
    public boolean deactivate() {
        this.active = false;
        this.updatedTime = LocalDateTime.now();
        return this.updateById();
    }
    
    public boolean activate() {
        this.active = true;
        this.updatedTime = LocalDateTime.now();
        return this.updateById();
    }
    
    public List<User> findSimilarUsers() {
        return this.selectList(
            new QueryWrapper<User>()
                .eq("age", this.age)
                .ne("id", this.id)
                .eq("active", true)
        );
    }
    
    public boolean isAdult() {
        return this.age != null && this.age >= 18;
    }
    
    public long countOlderUsers() {
        if (this.age == null) return 0;
        return this.selectCount(
            new QueryWrapper<User>().gt("age", this.age)
        );
    }
    
    public User findByEmail(String email) {
        return this.selectOne(
            new QueryWrapper<User>().eq("email", email)
        );
    }
}

Usage of Custom Methods

// Using custom entity methods
User user = new User("John", "john@example.com");
user.setAge(25);
user.insert();

// Deactivate user
boolean deactivated = user.deactivate();

// Find similar users
List<User> similarUsers = user.findSimilarUsers();

// Count older users
long olderCount = user.countOlderUsers();

// Find by email
User foundUser = new User().findByEmail("john@example.com");

SQL Runner Integration

@TableName("user")
public class User extends Model<User> {
    // ... other methods ...
    
    public List<Map<String, Object>> getDetailedStats() {
        SqlRunner sqlRunner = this.sql();
        return sqlRunner.selectList(
            "SELECT age, COUNT(*) as count FROM user WHERE active = ? GROUP BY age ORDER BY age",
            true
        );
    }
    
    public boolean updateLastLoginTime() {
        SqlRunner sqlRunner = this.sql();
        int affected = sqlRunner.update(
            "UPDATE user SET last_login = NOW() WHERE id = ?",
            this.getId()
        );
        return affected > 0;
    }
}

Complex Queries

// Complex query example
public class User extends Model<User> {
    
    public List<User> findRecentActiveUsers(int days, int limit) {
        return this.selectList(
            new QueryWrapper<User>()
                .eq("active", true)
                .ge("last_login", LocalDateTime.now().minusDays(days))
                .orderByDesc("last_login")
                .last("LIMIT " + limit)
        );
    }
    
    public Page<User> searchUsers(String keyword, int page, int size) {
        return this.selectPage(
            new Page<>(page, size),
            new QueryWrapper<User>()
                .and(wrapper -> wrapper
                    .like("name", keyword)
                    .or()
                    .like("email", keyword)
                )
                .eq("active", true)
                .orderByDesc("created_time")
        );
    }
}

Working with Relationships

While Active Record pattern works best with single entities, you can still handle relationships:

@TableName("user")
public class User extends Model<User> {
    // ... basic fields ...
    
    // Get user's orders (assuming Order entity exists)
    public List<Order> getOrders() {
        Order orderModel = new Order();
        return orderModel.selectList(
            new QueryWrapper<Order>().eq("user_id", this.getId())
        );
    }
    
    // Get user's active orders
    public List<Order> getActiveOrders() {
        Order orderModel = new Order();
        return orderModel.selectList(
            new QueryWrapper<Order>()
                .eq("user_id", this.getId())
                .eq("status", "ACTIVE")
                .orderByDesc("created_time")
        );
    }
    
    // Count user's orders
    public long getOrderCount() {
        Order orderModel = new Order();
        return orderModel.selectCount(
            new QueryWrapper<Order>().eq("user_id", this.getId())
        );
    }
}

Best Practices

1. Always Implement pkVal()

@Override
protected Serializable pkVal() {
    return this.id; // Return the primary key value
}

2. Handle Null Checks

public boolean updateIfExists() {
    if (this.getId() == null) {
        return false; // Cannot update without ID
    }
    return this.updateById();
}

public User reloadFromDatabase() {
    if (this.getId() == null) {
        return null;
    }
    return this.selectById();
}

3. Use Transactions for Complex Operations

@Transactional
public boolean transferToInactiveStatus() {
    // Update user status
    this.active = false;
    this.updatedTime = LocalDateTime.now();
    boolean userUpdated = this.updateById();
    
    if (!userUpdated) {
        throw new RuntimeException("Failed to update user");
    }
    
    // Update related data using SQL Runner
    SqlRunner sqlRunner = this.sql();
    int affected = sqlRunner.update(
        "UPDATE user_sessions SET active = 0 WHERE user_id = ?",
        this.getId()
    );
    
    return affected >= 0;
}

4. Validation Methods

public boolean isValid() {
    return this.name != null && !this.name.trim().isEmpty() &&
           this.email != null && this.email.contains("@");
}

public boolean saveIfValid() {
    if (!isValid()) {
        return false;
    }
    return this.insert();
}

Error Handling

public class User extends Model<User> {
    
    public boolean safeUpdate() {
        try {
            if (this.getId() == null) {
                System.err.println("Cannot update user without ID");
                return false;
            }
            
            return this.updateById();
            
        } catch (Exception e) {
            System.err.println("Error updating user: " + e.getMessage());
            return false;
        }
    }
    
    public User safeSelectById(Long id) {
        try {
            return this.selectById(id);
        } catch (Exception e) {
            System.err.println("Error selecting user by ID " + id + ": " + e.getMessage());
            return null;
        }
    }
}

Performance Considerations

  • Active Record is convenient but may not be suitable for bulk operations
  • For large datasets, consider using service layer methods instead
  • Use selectList() with conditions rather than selectAll() and filtering in Java
  • Be cautious with custom methods that might cause N+1 query problems
  • Cache frequently accessed read-only data at the application level

Install with Tessl CLI

npx tessl i tessl/maven-com-baomidou--mybatis-plus-extension

docs

active-record.md

condition-builders.md

index.md

kotlin-extensions.md

pagination.md

plugin-system.md

service-layer.md

static-utilities.md

tile.json