Skip to content

数据访问

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=secret
java
@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:

  1. 使用连接池
  2. 正确关闭资源
  3. 使用 try-with-resources
  4. 监控连接池状态

Q: 事务不生效怎么办?

A: 检查以下几点:

  1. 方法是否是 public
  2. 是否是自调用(同一个类内部调用)
  3. 异常是否被捕获
  4. 是否配置了事务管理器

下一步

Released under the Apache License 2.0