Использование записей в качестве проекций в 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());

	}
}
+1
0
+1
1
+1
0
+1
0
+1
0

Ответить

Ваш адрес email не будет опубликован. Обязательные поля помечены *