CRUD ( create-read-update-delete ) — это повторяющийся код в проектах Java EE, но его можно выделить в уникальном классе с помощью аннотаций JPA и Generics — класса, который я называю CRUDEntityFacade . Это не новый шаблон, и цель этой записи в блоге — просто подготовить вас к чтению моих следующих записей о JAXB и JPA вместе. Я делаю действительно хорошие вещи с Джерси и Глассфишом, и прежде чем раскрывать сложные изобретения, я решил зарегистрировать основы слоя персистентности (также очень хороший для моей будущей ссылки).
Завершите образцы проектов
Код, который я публикую здесь, просто для того, чтобы вы не извлекли полный проект и не покопались, чтобы проверить мою стратегию CRUD. Но если вы хотите увидеть эту технику в действии, пожалуйста, будьте моим гостем, чтобы проверить один из моих проектов с открытым исходным кодом. Проекты создаются Maven и создаются в Eclipse, но вы должны иметь возможность запускать его в предпочитаемой среде IDE без каких-либо проблем. Оба проекта требуют минимальной Java 6.
-
Пример # 1 : проект Footprint , который включает в себя сервис RESTful, закодированный в рамках структуры Джерси. Проверка Subversion:
svn checkout https://footprint.dev.java.net/svn/footprint/trunk footprint — имя пользователя username
- Пример # 2 : проект Cejug-Ads , первый веб-сервис WSDL, закодированный с помощью инфраструктуры JAX-WS. Проверка Subversion:
svn checkout https://cejug-classifieds.dev.java.net/svn/cejug-classifieds/trunk cejug-adss — имя пользователя имя пользователя
username — это ваш логин java.net, или вы можете использовать guest без пароля, чтобы получить копию только для чтения.
Общий CRUD, реализованный с использованием обобщений и аннотаций JPA
-
Определение интерфейса сохраняемости, содержащего операции постоянства, который мы хотим разделить между всеми сущностями. Обратите внимание, что я реплицировал исключения времени выполнения, потому что это интерфейс, и мы ожидаем, что интерфейсы будут самыми документными и понятными артефактами в нашем проекте.
public interface FootprintEntityFacade {
T create(T entity) throws EntityExistsException, IllegalStateException,
IllegalArgumentException, TransactionRequiredException;
T read(Serializable primaryKey) throws IllegalStateException,
IllegalArgumentException;
void update(T entity) throws IllegalStateException,
IllegalArgumentException, TransactionRequiredException;
void deleteO(T entity) throws IllegalStateException,
IllegalArgumentException, TransactionRequiredException,
PersistenceException;
} -
Определить суперкласс всех сущностей. Это важный шаг, так как мы хотим использовать обобщенные типы, у нас должен быть уникальный тип для передачи в нашей реализации интерфейса. Сопоставленный суперкласс также полезен для совместного использования атрибута ID.
@MappedSuperclass
public abstract class AbstractFootprintEntity implements Serializable {
@Transient public static final long serialVersionUID = 196919661993L;
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private long id;
// getters & setters
} -
Тогда нам нужны некоторые сущности. Я буду использовать пример объекта Footprint, который представляет события (собрания, курсы или конференции JUG).
@Entity
public class FpEvent extends AbstractFootprintEntity {
@Transient private static final long serialVersionUID = 196919661993L;
@Column(nullable = false)
private String name;
@Column(nullable = true)
private String website;
@Version
private long updatedTime;
// getters & setters
} -
Теперь важный шаг, реализация нашего общего интерфейса CRUD для нашего Event Entity. В теории этот пустой интерфейс не нужен, но мои эксперименты доказали, что это лучший путь. Это открывает возможность для настройки интерфейса персистентности и — главная причина — избегает конфликтов между различными экземплярами сущностей, использующих один и тот же интерфейс. У вас будет 1 пустой интерфейс для каждой сущности в вашем проекте, странность, от которой я не смог избавиться — если вы знаете, как этого избежать, скажите, пожалуйста.
@Local
public interface EventFacadeLocal extends FootprintEntityFacade<FpEvent> {
} -
Теперь нам просто нужно внедрить CRUD. Особое примечание о пустом конструкторе, который использует отражение, чтобы получить класс универсального типа — это скрытый трюк, который делает магию возможной.
@Stateless
public class CRUDEntityFacade<T extends AbstractFootprintEntity> implements FootprintEntityFacade<T> {
private transient final Class entityClass;
@SuppressWarnings("unchecked")
public CRUDEntityFacade() {
entityClass = (Class) ((java.lang.reflect.ParameterizedType) this
.getClass().getGenericSuperclass()).getActualTypeArguments()[0];
}
@PersistenceContext(name = "footprint")
protected transient EntityManager manager;
public T create(final T entity) throws EntityExistsException,
IllegalStateException, IllegalArgumentException,
TransactionRequiredException {
manager.persist(entity);
manager.flush();
return entity;
}
public T read(final Serializable primaryKey) throws IllegalStateException,
IllegalArgumentException {
return manager.find(entityClass, primaryKey);
}
public void update(final T entity) throws IllegalStateException,
IllegalArgumentException, TransactionRequiredException {
manager.merge(entity);
manager.flush();
}
public void delete(final T entity) throws IllegalStateException,
IllegalArgumentException, TransactionRequiredException,
PersistenceException {
manager.remove(entity);
manager.flush();
}
} -
Это сделано, теперь мы можем сохранить любую сущность типа AbstractFootprintEntity (не забудьте создать пустой подчиненный интерфейс для каждой новой сущности, которую вы хотите сохранить). Ниже вы найдете пример использования CRUD на ресурсе Джерси:
@Path("/event")
public class EventResource {
@EJB
private EventFacadeLocal eventFacade;
@Produces( { MediaType.APPLICATION_XML })
@Consumes(MediaType.APPLICATION_XML)
@POST
@Path("/test2")
public FpEvent postJAXBElement(FpEvent e) {
return eventFacade.create(e);
}
}
В моих следующих записях я покажу вам, как использовать двойную аннотацию (JAXB + JPA), чтобы минимизировать несоответствие импеданса между постоянным уровнем и сериализацией элемента, используемого в конечных точках служб — REST или SOAP.