数据访问
LCGYL Framework 提供灵活的数据访问抽象,支持多种数据库和 ORM 框架。
数据源配置
基本配置
properties
# application.properties
datasource.url=jdbc:mysql://localhost:3306/mydb
datasource.username=root
datasource.password=secret
datasource.driver-class-name=com.mysql.cj.jdbc.Driver连接池配置
properties
# HikariCP 连接池
datasource.pool.minimum-idle=5
datasource.pool.maximum-pool-size=20
datasource.pool.idle-timeout=300000
datasource.pool.max-lifetime=1800000
datasource.pool.connection-timeout=30000多数据源
properties
# 主数据源
datasource.primary.url=jdbc:mysql://localhost:3306/primary
datasource.primary.username=root
datasource.primary.password=secret
# 从数据源
datasource.secondary.url=jdbc:mysql://localhost:3306/secondary
datasource.secondary.username=root
datasource.secondary.password=secretjava
@Component
public class DataSourceConfig {
@Bean
@Primary
public DataSource primaryDataSource() {
return DataSourceBuilder.create()
.url(primaryUrl)
.username(primaryUsername)
.password(primaryPassword)
.build();
}
@Bean
public DataSource secondaryDataSource() {
return DataSourceBuilder.create()
.url(secondaryUrl)
.username(secondaryUsername)
.password(secondaryPassword)
.build();
}
}Repository 模式
定义 Repository
java
public interface UserRepository extends Repository<User, Long> {
User findById(Long id);
List<User> findByName(String name);
List<User> findByStatus(UserStatus status);
void save(User user);
void delete(Long id);
}实现 Repository
java
@Component
public class JdbcUserRepository implements UserRepository {
@Inject
private JdbcTemplate jdbcTemplate;
@Override
public User findById(Long id) {
return jdbcTemplate.queryForObject(
"SELECT * FROM users WHERE id = ?",
new UserRowMapper(),
id
);
}
@Override
public List<User> findByName(String name) {
return jdbcTemplate.query(
"SELECT * FROM users WHERE name LIKE ?",
new UserRowMapper(),
"%" + name + "%"
);
}
@Override
public void save(User user) {
if (user.getId() == null) {
jdbcTemplate.update(
"INSERT INTO users (name, email, status) VALUES (?, ?, ?)",
user.getName(),
user.getEmail(),
user.getStatus().name()
);
} else {
jdbcTemplate.update(
"UPDATE users SET name = ?, email = ?, status = ? WHERE id = ?",
user.getName(),
user.getEmail(),
user.getStatus().name(),
user.getId()
);
}
}
@Override
public void delete(Long id) {
jdbcTemplate.update("DELETE FROM users WHERE id = ?", id);
}
}RowMapper
java
public class UserRowMapper implements RowMapper<User> {
@Override
public User mapRow(ResultSet rs, int rowNum) throws SQLException {
User user = new User();
user.setId(rs.getLong("id"));
user.setName(rs.getString("name"));
user.setEmail(rs.getString("email"));
user.setStatus(UserStatus.valueOf(rs.getString("status")));
user.setCreatedAt(rs.getTimestamp("created_at").toLocalDateTime());
return user;
}
}JdbcTemplate
查询操作
java
@Component
public class UserDao {
@Inject
private JdbcTemplate jdbcTemplate;
// 查询单个对象
public User findById(Long id) {
return jdbcTemplate.queryForObject(
"SELECT * FROM users WHERE id = ?",
new UserRowMapper(),
id
);
}
// 查询列表
public List<User> findAll() {
return jdbcTemplate.query(
"SELECT * FROM users",
new UserRowMapper()
);
}
// 查询单个值
public int countUsers() {
return jdbcTemplate.queryForObject(
"SELECT COUNT(*) FROM users",
Integer.class
);
}
// 查询 Map
public Map<String, Object> findUserAsMap(Long id) {
return jdbcTemplate.queryForMap(
"SELECT * FROM users WHERE id = ?",
id
);
}
}更新操作
java
@Component
public class UserDao {
@Inject
private JdbcTemplate jdbcTemplate;
// 插入
public void insert(User user) {
jdbcTemplate.update(
"INSERT INTO users (name, email) VALUES (?, ?)",
user.getName(),
user.getEmail()
);
}
// 插入并返回 ID
public Long insertAndGetId(User user) {
KeyHolder keyHolder = new GeneratedKeyHolder();
jdbcTemplate.update(connection -> {
PreparedStatement ps = connection.prepareStatement(
"INSERT INTO users (name, email) VALUES (?, ?)",
Statement.RETURN_GENERATED_KEYS
);
ps.setString(1, user.getName());
ps.setString(2, user.getEmail());
return ps;
}, keyHolder);
return keyHolder.getKey().longValue();
}
// 更新
public int update(User user) {
return jdbcTemplate.update(
"UPDATE users SET name = ?, email = ? WHERE id = ?",
user.getName(),
user.getEmail(),
user.getId()
);
}
// 删除
public int delete(Long id) {
return jdbcTemplate.update(
"DELETE FROM users WHERE id = ?",
id
);
}
}批量操作
java
@Component
public class UserDao {
@Inject
private JdbcTemplate jdbcTemplate;
// 批量插入
public int[] batchInsert(List<User> users) {
return jdbcTemplate.batchUpdate(
"INSERT INTO users (name, email) VALUES (?, ?)",
new BatchPreparedStatementSetter() {
@Override
public void setValues(PreparedStatement ps, int i) throws SQLException {
User user = users.get(i);
ps.setString(1, user.getName());
ps.setString(2, user.getEmail());
}
@Override
public int getBatchSize() {
return users.size();
}
}
);
}
// 简化的批量插入
public int[] batchInsertSimple(List<User> users) {
List<Object[]> batchArgs = users.stream()
.map(u -> new Object[]{u.getName(), u.getEmail()})
.collect(Collectors.toList());
return jdbcTemplate.batchUpdate(
"INSERT INTO users (name, email) VALUES (?, ?)",
batchArgs
);
}
}事务管理
声明式事务
java
@Component
public class UserService {
@Inject
private UserRepository userRepository;
@Inject
private OrderRepository orderRepository;
@Transactional
public void createUserWithOrder(User user, Order order) {
userRepository.save(user);
order.setUserId(user.getId());
orderRepository.save(order);
// 如果任何操作失败,整个事务回滚
}
@Transactional(readOnly = true)
public User findUser(Long id) {
return userRepository.findById(id);
}
@Transactional(rollbackFor = BusinessException.class)
public void processOrder(Order order) {
// 只有 BusinessException 会触发回滚
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void auditLog(String action) {
// 在新事务中执行
}
}事务传播行为
java
public enum Propagation {
REQUIRED, // 默认,有则加入,无则创建
REQUIRES_NEW, // 总是创建新事务
SUPPORTS, // 有则加入,无则非事务执行
NOT_SUPPORTED, // 非事务执行,挂起当前事务
MANDATORY, // 必须在事务中,否则抛异常
NEVER, // 必须非事务,否则抛异常
NESTED // 嵌套事务
}编程式事务
java
@Component
public class UserService {
@Inject
private TransactionTemplate transactionTemplate;
public User createUser(User user) {
return transactionTemplate.execute(status -> {
try {
userRepository.save(user);
sendWelcomeEmail(user);
return user;
} catch (Exception e) {
status.setRollbackOnly();
throw e;
}
});
}
}分页查询
分页参数
java
public record PageRequest(
int page,
int size,
String sortBy,
String sortDirection
) {
public int getOffset() {
return page * size;
}
}
public record Page<T>(
List<T> content,
int page,
int size,
long totalElements,
int totalPages
) {
public static <T> Page<T> of(List<T> content, PageRequest request, long total) {
return new Page<>(
content,
request.page(),
request.size(),
total,
(int) Math.ceil((double) total / request.size())
);
}
}分页查询实现
java
@Component
public class UserRepository {
@Inject
private JdbcTemplate jdbcTemplate;
public Page<User> findAll(PageRequest request) {
// 查询总数
long total = jdbcTemplate.queryForObject(
"SELECT COUNT(*) FROM users",
Long.class
);
// 查询数据
String sql = String.format(
"SELECT * FROM users ORDER BY %s %s LIMIT ? OFFSET ?",
request.sortBy(),
request.sortDirection()
);
List<User> content = jdbcTemplate.query(
sql,
new UserRowMapper(),
request.size(),
request.getOffset()
);
return Page.of(content, request, total);
}
}动态查询
条件构建器
java
public class QueryBuilder {
private final StringBuilder sql = new StringBuilder();
private final List<Object> params = new ArrayList<>();
private boolean hasWhere = false;
public QueryBuilder select(String columns) {
sql.append("SELECT ").append(columns);
return this;
}
public QueryBuilder from(String table) {
sql.append(" FROM ").append(table);
return this;
}
public QueryBuilder where(String condition, Object... values) {
if (!hasWhere) {
sql.append(" WHERE ");
hasWhere = true;
} else {
sql.append(" AND ");
}
sql.append(condition);
params.addAll(Arrays.asList(values));
return this;
}
public QueryBuilder whereIf(boolean condition, String clause, Object... values) {
if (condition) {
where(clause, values);
}
return this;
}
public QueryBuilder orderBy(String column, String direction) {
sql.append(" ORDER BY ").append(column).append(" ").append(direction);
return this;
}
public QueryBuilder limit(int limit, int offset) {
sql.append(" LIMIT ? OFFSET ?");
params.add(limit);
params.add(offset);
return this;
}
public String getSql() {
return sql.toString();
}
public Object[] getParams() {
return params.toArray();
}
}使用条件构建器
java
@Component
public class UserRepository {
@Inject
private JdbcTemplate jdbcTemplate;
public List<User> search(UserSearchCriteria criteria) {
QueryBuilder builder = new QueryBuilder()
.select("*")
.from("users")
.whereIf(criteria.getName() != null, "name LIKE ?", "%" + criteria.getName() + "%")
.whereIf(criteria.getEmail() != null, "email = ?", criteria.getEmail())
.whereIf(criteria.getStatus() != null, "status = ?", criteria.getStatus().name())
.whereIf(criteria.getCreatedAfter() != null, "created_at >= ?", criteria.getCreatedAfter())
.orderBy("created_at", "DESC");
return jdbcTemplate.query(
builder.getSql(),
new UserRowMapper(),
builder.getParams()
);
}
}缓存集成
缓存注解
java
@Component
public class UserService {
@Inject
private UserRepository userRepository;
@Cacheable(value = "users", key = "#id")
public User findById(Long id) {
return userRepository.findById(id);
}
@CachePut(value = "users", key = "#user.id")
public User save(User user) {
userRepository.save(user);
return user;
}
@CacheEvict(value = "users", key = "#id")
public void delete(Long id) {
userRepository.delete(id);
}
@CacheEvict(value = "users", allEntries = true)
public void clearCache() {
// 清除所有缓存
}
}审计功能
审计字段
java
public abstract class BaseEntity {
private Long id;
@CreatedDate
private LocalDateTime createdAt;
@LastModifiedDate
private LocalDateTime updatedAt;
@CreatedBy
private String createdBy;
@LastModifiedBy
private String updatedBy;
// Getters and Setters
}
public class User extends BaseEntity {
private String name;
private String email;
// ...
}审计监听器
java
@Component
public class AuditingListener {
@Inject
private SecurityContext securityContext;
@PrePersist
public void prePersist(BaseEntity entity) {
LocalDateTime now = LocalDateTime.now();
String currentUser = securityContext.getCurrentUser().getUsername();
entity.setCreatedAt(now);
entity.setUpdatedAt(now);
entity.setCreatedBy(currentUser);
entity.setUpdatedBy(currentUser);
}
@PreUpdate
public void preUpdate(BaseEntity entity) {
entity.setUpdatedAt(LocalDateTime.now());
entity.setUpdatedBy(securityContext.getCurrentUser().getUsername());
}
}最佳实践
1. 使用 Repository 模式
java
// ✅ 推荐:使用 Repository 抽象
@Component
public class UserService {
@Inject
private UserRepository userRepository;
public User findById(Long id) {
return userRepository.findById(id);
}
}
// ❌ 不推荐:直接使用 JdbcTemplate
@Component
public class UserService {
@Inject
private JdbcTemplate jdbcTemplate;
public User findById(Long id) {
return jdbcTemplate.queryForObject(...);
}
}2. 合理使用事务
java
// ✅ 推荐:只在需要的地方使用事务
@Transactional
public void createOrder(Order order) {
// 多个数据库操作
}
@Transactional(readOnly = true)
public User findUser(Long id) {
// 只读操作
}
// ❌ 不推荐:所有方法都加事务
@Transactional
public User findUser(Long id) {
// 简单查询不需要事务
}3. 使用参数化查询
java
// ✅ 推荐:参数化查询
jdbcTemplate.query(
"SELECT * FROM users WHERE name = ?",
new UserRowMapper(),
name
);
// ❌ 不推荐:字符串拼接(SQL 注入风险)
jdbcTemplate.query(
"SELECT * FROM users WHERE name = '" + name + "'",
new UserRowMapper()
);4. 批量操作
java
// ✅ 推荐:批量插入
jdbcTemplate.batchUpdate(sql, batchArgs);
// ❌ 不推荐:循环单条插入
for (User user : users) {
jdbcTemplate.update(sql, user.getName());
}常见问题
Q: 如何处理大数据量查询?
A: 使用分页或流式查询。
java
// 分页查询
Page<User> page = userRepository.findAll(new PageRequest(0, 100));
// 流式查询
jdbcTemplate.query(
"SELECT * FROM users",
rs -> {
while (rs.next()) {
processUser(mapRow(rs));
}
}
);Q: 如何处理数据库连接泄漏?
A:
- 使用连接池
- 正确关闭资源
- 使用 try-with-resources
- 监控连接池状态
Q: 事务不生效怎么办?
A: 检查以下几点:
- 方法是否是 public
- 是否是自调用(同一个类内部调用)
- 异常是否被捕获
- 是否配置了事务管理器