Что такое хороший дизайн для запроса “layer” для Java JPA
Вопрос
В JPA объекты представляют собой красивые аннотированные простые старые объекты Java.Но я не нашел хорошего способа взаимодействовать с ними и базой данных.
В моем текущем приложении мой базовый дизайн всегда заключается в том, чтобы иметь идентификатор на основе последовательности в качестве первичного ключа, поэтому мне обычно приходится искать объекты по другим свойствам, кроме PK.
И для каждой сущности у меня есть EJB без состояния
@Stateless
public class MyEntApiBean implements MyEntApi {
@PersistenceContext(unitName = "xxx") @Inject EntityManager entityManager;
с методами запроса, которые все являются некоторой вариацией
/**
* @return A List of all MyEnts that have some property
* @param someProp some property
*/
public List<MyEnt> getAllMyEntsFromProp(final String someProp) {
try {
final Query query = entityManager.createQuery("select me from MyEnt me where me.someProp = :someProp");
query.setParameter("someProp", someProp);
return query.getResultList();
} catch(final NoResultException nre) {
log.warn("No MyEnts found");
}
return new ArrayList<MyEnt>();
}
Итак:
Я действительно ненавижу иметь эти методы в EJB, потому что они, кажется, принадлежат самим сущностям, а локальные интерфейсы EJB раздражают меня до чертиков.
Я ненавижу дублирование, которое у меня есть в каждом методе с помощью "try, CreateQuery, getResultList, catch, log, return" (в основном это следствие отсутствия замыканий или "with statement" или чего-то подобного в Java).
Есть ли у кого-нибудь предложения по лучшему способу взаимодействия с сущностями и базой данных, который решает одну или обе мои проблемы?
В настоящее время я подумываю о создании некоторых базовых методов с обобщениями и отражением, чтобы получить некоторые общие методы запросов для уменьшения дублирования (проблема 2) (прототип я выставлю на обзор позже).
Спасибо, Андерс
Решение
Попробуй наложить шов.В Объекты запроса сделайте большую часть работы за вас, и они легко расширяются.Или же вы всегда могли бы реализовать аналогичный шаблон.
В целом, Seam делает много полезного, чтобы преодолеть разрыв между JPA и вашими представлениями и бизнес-уровнями.Вам не обязательно использовать JSF для того, чтобы Seam был полезен.
Другие советы
Ты излишне многословен.Во-первых, getResultList() не генерирует исключение, когда строки не возвращаются (по крайней мере, не в Eclipse или Toplink - я не могу представить, чтобы другой поставщик чем-то отличался).getSingleResult() выполняет, getResultList() - нет.Кроме того, вы можете использовать шаблон builder, чтобы:
@SuppressWarnings("unchecked")
public List<MyEnt> getAllMyEntsFromProp(final String someProp) {
return entityManager.createQuery("select me from MyEnt me where me.someProp = :someProp")
.setParameter("someProp", someProp);
.getResultList();
}
должно быть достаточно, чтобы вернуть Список результатов, если таковые имеются, или пустой список, если таковых нет.Следует отметить две вещи:
@SuppressWarnings("непроверенный") не нужен, но он избавляет от предупреждения, неизбежного в противном случае, при приведении результата не общего списка из getResultList() к общему списку;и
Вероятно, стоит заменить вызов CreateQuery() на @NamedQuery в MyEnt (обычно).Во-первых, это позволит проводить проверку во время развертывания и другие полезные вещи.
Он достаточно лаконичен и полон.
Если вы выполняете много текстовых поисковых запросов, возможно, вам также следует рассмотреть некоторые рамки индексации, такие как Компас.
Я не знаю, подходит ли это вашему приложению, но если да, то это может улучшить дизайн кода и производительность.
На самом деле я использую Seam.И предложение объекта запроса привело меня к поиску спящих режимов Запросы по критериям (Запрос на примере) функциональность.Это кажется очень близким к тому, что я искал.
Может быть, в базовом классе, и с тире дженериков....?
Моин!
Вот моя версия для отдельных результатов (я использую ее в своих настольных приложениях JPA с TopLink essentials):
public class JPA {
@SuppressWarnings ("unchecked")
public static <T> T querySingle(
EntityManager em,
Class<T> clazz,
String namedQuery,
Pair... params)
{
Query q = em.createNamedQuery(namedQuery);
for (Pair pair : params) {
q.setParameter(pair.key, pair.value);
}
List<T> result = q.getResultList();
if ( result.size() == 0 ) {
return null;
}
if ( result.size() == 1 ) {
return result.get(0);
}
throw new
IllegalStateException(
"To many result rows for query: "
+ namedQuery
+ " where "
+ Arrays.toString(params));
}
public static class Pair {
String key;
Object value;
public static Pair param (String key, Object value) {
return new Pair (key, value);
}
public Pair (String key, Object value) {
this.key = key;
this.value = value;
}
@Override
public String toString() {
return key + "=" + value;
}
}
}
И использование:
import static org.sepix.JPA.*;
...
String id = ...
Customer customer = querySingle (em, Customer.class,
"Customer.findByID", Pair.param ("id", id));
или:
String inquiryID = ...
Boolean current = Boolean.TRUE;
Inquiry inq = querySingle (em, Inquiry.class,
"Inquiry.findCurrent",
Pair.param ("inquiry", inquiryID),
Pair.param ("current", current));
с наилучшими пожеланиями, джош.
Я предпочитаю использовать Spring's JpaDaoSupport , который помогает работать с JPA.Хороший пример находится здесь http://github.com/rafalrusin/jpaqb/blob/master/src/test/java/jpaqb/CarDao.java.
Хорошим разделением логики является наличие класса DAO (объект доступа к данным) и DTO (объект передачи данных).DAO обычно содержит все необходимые запросы, а DTO - это объекты с полями.