Java 16 中引入的 Java Records 允许轻松定义透明数据载体。对于维护依赖于 JPA 、Spring Data的应用程序的开发人员来说,Records 可能是使用数据库投影的绝佳选择。
记录不是实体:记录只能用作投影。
流行的 JPA 实现(如 Hibernate)依赖于无参数构造函数、非最终字段、设置器和非最终类的实体来创建代理,而记录不鼓励或明确禁止所有这些操作。
Spring Data 有多种使用记录的方法:
自动映射
如果记录的组件与跟踪实体的字段匹配,Spring Data 可以自动处理查询返回映射,如下例所示:
public interface AdvocateRepo extends CrudRepository<AdvocateEntity, Integer> { Iterable<AdvocateRecord> findByRegion(String region); }
|
记录Record
这里的组件 Record AdvocateRecord 与 以@Entity 标注的AdvocateEntity类的字段相匹配:
public record AdvocateRecord( int id, String fName, String lName, String region, int twitterFollowers) {}
|
以@Entity 标注的AdvocateEntity类
public class AdvocateEntity { @Id private int id; private String fName; private String lName; private String region; private int twitterFollowers; ...
|
查询
Spring Data 还允许在 @Query 中提供 JPQL 查询:
public interface AdvocateRepo extends CrudRepository<AdvocateEntity, Integer> { @Query(""" SELECT new com.bk.records.AdvocateNameRecord(a.fName, a.lName) FROM AdvocateEntity a WHERE region = ?1 """) Iterable<AdvocateNameRecord> findNamesByRegion(String region); }
|
自定义 repo 实现
Spring Data 还支持自定义 repo 实现,它也可用于处理查询返回到 Record 类的映射。要使用自定义 repo 实现,请定义一个接口:
public interface CustomAdvocateRepo { Iterable<AdvocateNameRecord> findAllNameRecords(); }
|
在对Spring Data repo的extends中添加接口:
public interface AdvocateRepo extends CrudRepository<AdvocateEntity, Integer>, CustomAdvocateRepo { }
|
并提供 repo 的实现。本例中使用了 RowMapper 来处理查询结果的映射:
public class CustomAdvocateRepoImpl implements CustomAdvocateRepo { private JdbcTemplate jdbcTemplate;
protected CustomAdvocateRepoImpl(JdbcTemplate jdbcTemplate) { this.jdbcTemplate = jdbcTemplate; }
class AdvocateRecordDtoRowMapper implements RowMapper<AdvocateNameRecord> { @Override public AdvocateNameRecord mapRow(ResultSet rs, int rowNum) throws SQLException { return new AdvocateNameRecord( rs.getString("f_name"), rs.getString("l_name")); } }
@Override public Iterable<AdvocateNameRecord> findAllNameRecords() { return jdbcTemplate.query( "SELECT f_name, l_name FROM advocates", new AdvocateRecordDtoRowMapper());
} }
|