Использование записей в качестве проекций в JPA
Записи не могут быть сущностями
Записи же могут использоваться только как проекции. Популярные реализации JPA, такие как Hibernate, используют для создания прокси сущности, не имеющие конструкторов аргументов, нефинальных полей, сеттеров и нефинальных классов, что либо не поощряется, либо явно запрещается записями.
Записи с использованием JPA
Если вы используете JPA непосредственно в своем приложении, то существует несколько различных способов включения записей в уровень доступа к данным.
Построитель критериев
Записи могут быть использованы с CriteriaBuilder. Как в примере ниже:
public List<AdvocateRecord> findAllWithCriteriaBuilder() {
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<AdvocateRecord> cq
= cb.createQuery(AdvocateRecord.class);
Root<AdvocateEntity> root = cq.from(AdvocateEntity.class);
cq.select(cb.construct(
AdvocateRecord.class,
root.get("id"),
root.get("fName"),
root.get("lName"),
root.get("region"),
root.get("twitterFollowers")));
TypedQuery<AdvocateRecord> q = em.createQuery(cq);
return q.getResultList();
}
Типовой запрос
Записи также могут использоваться с TypedQuery, при этом в запросе JPQL необходимо указать полностью квалифицированный конструктор.
public List<AdvocateNameRecord>
findAdvocateNamesByRegionTypedQuery(String region) {
TypedQuery<AdvocateNameRecord> query = em.createQuery("""
SELECT
new com.bk.records.AdvocateNameRecord(a.fName, a.lName)
FROM AdvocateEntity a
WHERE region = :region
""", AdvocateNameRecord.class);
query.setParameter("region", region);
return query.getResultList();
}
Родной запрос
Записи также могут использоваться с NativeQuery. Для сопоставления полей запроса с полями записи необходимо предоставить определение отображения, как это сделано в примере AdvocateNameRecordMapping ниже:
public List<AdvocateNameRecord>
findAdvocateNamesByIdNativeQuery(int id) {
Query query = em.createNativeQuery("""
SELECT
f_name, l_name
FROM advocates
WHERE id = :id
""",
"AdvocateNameRecordMapping");
query.setParameter("id", id);
return query.getResultList();
}
Определение отображения:
Отображение AdvocateNameRecordMapping определено в классе сущности AdvocateEntity:
@Entity
@Table(name = "advocates")
@SqlResultSetMapping(
name = "AdvocateNameRecordMapping",
classes = @ConstructorResult(
targetClass = AdvocateNameRecord.class,
columns = {
@ColumnResult(name = "f_name"),
@ColumnResult(name = "l_name")}))
public class AdvocateEntity {
...
Записи с использованием Spring Data
Записи также поддерживаются Spring Data, если вы используете JPA таким образом в своем приложении. Как и при использовании JPA напрямую, существует несколько способов использования записей при работе с Spring Data.
Автоматическое картирование
Spring Data может автоматически обрабатывать отображение возвращаемого запроса, если компоненты Record совпадают с полями отслеживаемой сущности, как в приведенном ниже примере:
public interface AdvocateRepo
extends CrudRepository<AdvocateEntity, Integer> {
Iterable<AdvocateRecord> findByRegion(String region);
}
Запись:
Здесь компоненты Record AdvocateRecord соответствуют полям @Entity класса AdvocateEntity:
public record AdvocateRecord(
int id,
String fName,
String lName,
String region,
int twitterFollowers) {}
Отслеживаемая сущность:
public class AdvocateEntity {
@Id
private int id;
private String fName;
private String lName;
private String region;
private int twitterFollowers;
...
Запрос
Spring Data также позволяет предоставлять JPQL-запросы в @Query:
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);
}
Индивидуальная реализация сделок РЕПО
Spring Data также поддерживает реализацию настраиваемых репо, которые также могут быть использованы для обработки отображения возврата запроса на класс Record. Для использования специализированной реализации репозитория необходимо определить интерфейс:
public interface CustomAdvocateRepo {
Iterable<AdvocateNameRecord> findAllNameRecords();
}
Добавьте интерфейс в extends репозитория Spring Data:
public interface AdvocateRepo
extends CrudRepository<AdvocateEntity, Integer>,
CustomAdvocateRepo {
}
И предоставить реализацию в репо. В данном примере для обработки отображения результатов запроса используется 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());
}
}