0
# Active Record Pattern
1
2
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.
3
4
## Core Components
5
6
### Model Base Class
7
8
The abstract `Model<T>` class that entities can extend to gain Active Record capabilities.
9
10
```java { .api }
11
public abstract class Model<T extends Model<?>> implements Serializable, Cloneable {
12
13
// Insert operations
14
public boolean insert();
15
public boolean insertOrUpdate();
16
17
// Delete operations
18
public boolean deleteById();
19
public boolean deleteById(Serializable id);
20
public boolean delete(Wrapper<T> queryWrapper);
21
22
// Update operations
23
public boolean updateById();
24
public boolean update(Wrapper<T> updateWrapper);
25
26
// Select operations
27
public T selectById();
28
public T selectById(Serializable id);
29
public T selectOne(Wrapper<T> queryWrapper);
30
public List<T> selectAll();
31
public List<T> selectList(Wrapper<T> queryWrapper);
32
public List<T> selectByIds(Collection<? extends Serializable> idList);
33
public List<T> selectByMap(Map<String, Object> columnMap);
34
public <E extends IPage<T>> E selectPage(E page, Wrapper<T> queryWrapper);
35
public long selectCount(Wrapper<T> queryWrapper);
36
37
// Utility operations
38
public SqlRunner sql();
39
protected abstract Serializable pkVal();
40
}
41
```
42
43
## Usage Examples
44
45
### Entity Definition
46
47
```java
48
@TableName("user")
49
public class User extends Model<User> {
50
51
@TableId(type = IdType.AUTO)
52
private Long id;
53
54
private String name;
55
private String email;
56
private Integer age;
57
private Boolean active;
58
private LocalDateTime createdTime;
59
private LocalDateTime updatedTime;
60
61
// Constructors
62
public User() {}
63
64
public User(String name, String email) {
65
this.name = name;
66
this.email = email;
67
this.active = true;
68
this.createdTime = LocalDateTime.now();
69
}
70
71
// Required: implement pkVal() method
72
@Override
73
protected Serializable pkVal() {
74
return this.id;
75
}
76
77
// Getters and setters
78
public Long getId() { return id; }
79
public void setId(Long id) { this.id = id; }
80
81
public String getName() { return name; }
82
public void setName(String name) { this.name = name; }
83
84
public String getEmail() { return email; }
85
public void setEmail(String email) { this.email = email; }
86
87
public Integer getAge() { return age; }
88
public void setAge(Integer age) { this.age = age; }
89
90
public Boolean getActive() { return active; }
91
public void setActive(Boolean active) { this.active = active; }
92
93
public LocalDateTime getCreatedTime() { return createdTime; }
94
public void setCreatedTime(LocalDateTime createdTime) { this.createdTime = createdTime; }
95
96
public LocalDateTime getUpdatedTime() { return updatedTime; }
97
public void setUpdatedTime(LocalDateTime updatedTime) { this.updatedTime = updatedTime; }
98
}
99
```
100
101
### Insert Operations
102
103
```java
104
// Create and insert new user
105
User newUser = new User("John Doe", "john@example.com");
106
newUser.setAge(30);
107
108
boolean inserted = newUser.insert();
109
if (inserted) {
110
System.out.println("User inserted with ID: " + newUser.getId());
111
}
112
113
// Insert or update based on primary key
114
User user = new User("Jane Smith", "jane@example.com");
115
user.setId(1L); // If ID exists, it will update; otherwise insert
116
boolean success = user.insertOrUpdate();
117
```
118
119
### Select Operations
120
121
```java
122
// Select by primary key
123
User user = new User();
124
User foundUser = user.selectById(1L);
125
126
if (foundUser != null) {
127
System.out.println("Found user: " + foundUser.getName());
128
}
129
130
// Select current instance by its ID
131
User user = new User();
132
user.setId(1L);
133
User reloaded = user.selectById(); // Uses the current instance's ID
134
135
// Select all users
136
User user = new User();
137
List<User> allUsers = user.selectAll();
138
139
// Select with conditions
140
User user = new User();
141
List<User> activeUsers = user.selectList(
142
new QueryWrapper<User>().eq("active", true)
143
);
144
145
// Select single user with conditions
146
User user = new User();
147
User activeUser = user.selectOne(
148
new QueryWrapper<User>()
149
.eq("active", true)
150
.eq("email", "john@example.com")
151
);
152
153
// Select by IDs
154
User user = new User();
155
List<User> users = user.selectByIds(Arrays.asList(1L, 2L, 3L));
156
157
// Select by column map
158
User user = new User();
159
Map<String, Object> conditions = new HashMap<>();
160
conditions.put("active", true);
161
conditions.put("age", 30);
162
List<User> users = user.selectByMap(conditions);
163
```
164
165
### Update Operations
166
167
```java
168
// Update by primary key
169
User user = new User();
170
user.setId(1L);
171
user.setName("Updated Name");
172
user.setEmail("updated@example.com");
173
user.setUpdatedTime(LocalDateTime.now());
174
175
boolean updated = user.updateById();
176
if (updated) {
177
System.out.println("User updated successfully");
178
}
179
180
// Update with conditions
181
User user = new User();
182
user.setActive(false);
183
user.setUpdatedTime(LocalDateTime.now());
184
185
boolean updated = user.update(
186
new UpdateWrapper<User>().eq("age", 25)
187
);
188
```
189
190
### Delete Operations
191
192
```java
193
// Delete by primary key (current instance)
194
User user = new User();
195
user.setId(1L);
196
boolean deleted = user.deleteById();
197
198
// Delete by specific ID
199
User user = new User();
200
boolean deleted = user.deleteById(2L);
201
202
// Delete with conditions
203
User user = new User();
204
boolean deleted = user.delete(
205
new QueryWrapper<User>()
206
.eq("active", false)
207
.lt("created_time", LocalDateTime.now().minusYears(1))
208
);
209
```
210
211
### Count Operations
212
213
```java
214
// Count all records
215
User user = new User();
216
long totalUsers = user.selectCount(null);
217
218
// Count with conditions
219
User user = new User();
220
long activeUsers = user.selectCount(
221
new QueryWrapper<User>().eq("active", true)
222
);
223
224
long adultUsers = user.selectCount(
225
new QueryWrapper<User>().ge("age", 18)
226
);
227
```
228
229
### Pagination with Active Record
230
231
```java
232
// Paginated selection
233
User user = new User();
234
Page<User> page = new Page<>(1, 10);
235
236
Page<User> result = user.selectPage(page,
237
new QueryWrapper<User>().eq("active", true).orderByDesc("created_time")
238
);
239
240
List<User> users = result.getRecords();
241
long total = result.getTotal();
242
System.out.println("Found " + total + " active users");
243
244
// Pagination without conditions
245
Page<User> allUsersPage = user.selectPage(new Page<>(1, 20), null);
246
```
247
248
## Advanced Usage
249
250
### Custom Entity Methods
251
252
```java
253
@TableName("user")
254
public class User extends Model<User> {
255
// ... fields and basic methods ...
256
257
// Custom business methods using Active Record
258
public boolean deactivate() {
259
this.active = false;
260
this.updatedTime = LocalDateTime.now();
261
return this.updateById();
262
}
263
264
public boolean activate() {
265
this.active = true;
266
this.updatedTime = LocalDateTime.now();
267
return this.updateById();
268
}
269
270
public List<User> findSimilarUsers() {
271
return this.selectList(
272
new QueryWrapper<User>()
273
.eq("age", this.age)
274
.ne("id", this.id)
275
.eq("active", true)
276
);
277
}
278
279
public boolean isAdult() {
280
return this.age != null && this.age >= 18;
281
}
282
283
public long countOlderUsers() {
284
if (this.age == null) return 0;
285
return this.selectCount(
286
new QueryWrapper<User>().gt("age", this.age)
287
);
288
}
289
290
public User findByEmail(String email) {
291
return this.selectOne(
292
new QueryWrapper<User>().eq("email", email)
293
);
294
}
295
}
296
```
297
298
### Usage of Custom Methods
299
300
```java
301
// Using custom entity methods
302
User user = new User("John", "john@example.com");
303
user.setAge(25);
304
user.insert();
305
306
// Deactivate user
307
boolean deactivated = user.deactivate();
308
309
// Find similar users
310
List<User> similarUsers = user.findSimilarUsers();
311
312
// Count older users
313
long olderCount = user.countOlderUsers();
314
315
// Find by email
316
User foundUser = new User().findByEmail("john@example.com");
317
```
318
319
### SQL Runner Integration
320
321
```java
322
@TableName("user")
323
public class User extends Model<User> {
324
// ... other methods ...
325
326
public List<Map<String, Object>> getDetailedStats() {
327
SqlRunner sqlRunner = this.sql();
328
return sqlRunner.selectList(
329
"SELECT age, COUNT(*) as count FROM user WHERE active = ? GROUP BY age ORDER BY age",
330
true
331
);
332
}
333
334
public boolean updateLastLoginTime() {
335
SqlRunner sqlRunner = this.sql();
336
int affected = sqlRunner.update(
337
"UPDATE user SET last_login = NOW() WHERE id = ?",
338
this.getId()
339
);
340
return affected > 0;
341
}
342
}
343
```
344
345
### Complex Queries
346
347
```java
348
// Complex query example
349
public class User extends Model<User> {
350
351
public List<User> findRecentActiveUsers(int days, int limit) {
352
return this.selectList(
353
new QueryWrapper<User>()
354
.eq("active", true)
355
.ge("last_login", LocalDateTime.now().minusDays(days))
356
.orderByDesc("last_login")
357
.last("LIMIT " + limit)
358
);
359
}
360
361
public Page<User> searchUsers(String keyword, int page, int size) {
362
return this.selectPage(
363
new Page<>(page, size),
364
new QueryWrapper<User>()
365
.and(wrapper -> wrapper
366
.like("name", keyword)
367
.or()
368
.like("email", keyword)
369
)
370
.eq("active", true)
371
.orderByDesc("created_time")
372
);
373
}
374
}
375
```
376
377
## Working with Relationships
378
379
While Active Record pattern works best with single entities, you can still handle relationships:
380
381
```java
382
@TableName("user")
383
public class User extends Model<User> {
384
// ... basic fields ...
385
386
// Get user's orders (assuming Order entity exists)
387
public List<Order> getOrders() {
388
Order orderModel = new Order();
389
return orderModel.selectList(
390
new QueryWrapper<Order>().eq("user_id", this.getId())
391
);
392
}
393
394
// Get user's active orders
395
public List<Order> getActiveOrders() {
396
Order orderModel = new Order();
397
return orderModel.selectList(
398
new QueryWrapper<Order>()
399
.eq("user_id", this.getId())
400
.eq("status", "ACTIVE")
401
.orderByDesc("created_time")
402
);
403
}
404
405
// Count user's orders
406
public long getOrderCount() {
407
Order orderModel = new Order();
408
return orderModel.selectCount(
409
new QueryWrapper<Order>().eq("user_id", this.getId())
410
);
411
}
412
}
413
```
414
415
## Best Practices
416
417
### 1. Always Implement pkVal()
418
419
```java
420
@Override
421
protected Serializable pkVal() {
422
return this.id; // Return the primary key value
423
}
424
```
425
426
### 2. Handle Null Checks
427
428
```java
429
public boolean updateIfExists() {
430
if (this.getId() == null) {
431
return false; // Cannot update without ID
432
}
433
return this.updateById();
434
}
435
436
public User reloadFromDatabase() {
437
if (this.getId() == null) {
438
return null;
439
}
440
return this.selectById();
441
}
442
```
443
444
### 3. Use Transactions for Complex Operations
445
446
```java
447
@Transactional
448
public boolean transferToInactiveStatus() {
449
// Update user status
450
this.active = false;
451
this.updatedTime = LocalDateTime.now();
452
boolean userUpdated = this.updateById();
453
454
if (!userUpdated) {
455
throw new RuntimeException("Failed to update user");
456
}
457
458
// Update related data using SQL Runner
459
SqlRunner sqlRunner = this.sql();
460
int affected = sqlRunner.update(
461
"UPDATE user_sessions SET active = 0 WHERE user_id = ?",
462
this.getId()
463
);
464
465
return affected >= 0;
466
}
467
```
468
469
### 4. Validation Methods
470
471
```java
472
public boolean isValid() {
473
return this.name != null && !this.name.trim().isEmpty() &&
474
this.email != null && this.email.contains("@");
475
}
476
477
public boolean saveIfValid() {
478
if (!isValid()) {
479
return false;
480
}
481
return this.insert();
482
}
483
```
484
485
## Error Handling
486
487
```java
488
public class User extends Model<User> {
489
490
public boolean safeUpdate() {
491
try {
492
if (this.getId() == null) {
493
System.err.println("Cannot update user without ID");
494
return false;
495
}
496
497
return this.updateById();
498
499
} catch (Exception e) {
500
System.err.println("Error updating user: " + e.getMessage());
501
return false;
502
}
503
}
504
505
public User safeSelectById(Long id) {
506
try {
507
return this.selectById(id);
508
} catch (Exception e) {
509
System.err.println("Error selecting user by ID " + id + ": " + e.getMessage());
510
return null;
511
}
512
}
513
}
514
```
515
516
## Performance Considerations
517
518
- Active Record is convenient but may not be suitable for bulk operations
519
- For large datasets, consider using service layer methods instead
520
- Use `selectList()` with conditions rather than `selectAll()` and filtering in Java
521
- Be cautious with custom methods that might cause N+1 query problems
522
- Cache frequently accessed read-only data at the application level