There is a persistence interface and abstract base broken into read-only and persistent operations. For standard persistence based on Spring Data JPA repositories, just extending one of the base classes will handle basic persistence.
Example 5. PersistenceFindService
The find service takes a generic response and find response. The generic response is a wrapper, with messages, for a single result. The find response is for a standard and paginated search. It also can have messages.
public interface PersistenceFindService<R extends EntityResponseResult, FR extends EntityFindResponseResult> { /** * Find a record with an id. */ public R findById(Integer id); /** * Find all records. */ public FR find(); /** * Find a paginated record set. */ public FR find(int page, int pageSize); }
Example 6. PersistenceService
Besides a generic response and find response, the PersistenceService
also takes the entity bean the persistence class handles.
public interface PersistenceService<V extends PkEntityBase, R extends EntityResponseResult, FR extends EntityFindResponseResult> extends PersistenceFindService<R, FR> { /** * Creates a record. */ public R create(V request); /** * Updates a record. */ public R update(V request); /** * Deletes person. */ public ResponseResult delete(Integer id); }
Example 7. AbstractPersistenceFindService
The abstract persistence find service adds another generic value representing a
Spring Data JPA entity.
It expects a JpaRepository
, Converter
, and a MessageHelper
for it's constructor. The MessageHelper
is just a helper bean for accessing
the Spring MessageSource
.
The @Transactional
annotation is set on the class to be read-only.
Any method will automatically have a read-only transaction in any subclass unless marked otherwise.
The converter is used to convert the Spring Data JPA
results into ws beans, then an abstract method is used to create the response.
@Transactional(readOnly=true) public abstract class AbstractPersistenceFindService<T extends AbstractPersistable<Integer>, V extends PkEntityBase, R extends EntityResponseResult, FR extends EntityFindResponseResult> extends AbstractService implements PersistenceFindService<R, FR> { protected final JpaRepository<T, Integer> repository; protected final ListConverter<T, V> converter; public AbstractPersistenceFindService(JpaRepository<T, Integer> repository, ListConverter<T, V> converter, MessageHelper messageHelper) { super(messageHelper); this.repository = repository; this.converter = converter; } @Override public R findById(Integer id) { T bean = repository.findOne(id); V result = (bean != null ? converter.convertTo(bean) : null); return createResponse(result); } @Override public FR find() { List<V> results = converter.convertListTo(repository.findAll(createDefaultSort())); return createFindResponse(results); } @Override public FR find(int page, int pageSize) { Page<T> pageResults = repository.findAll(new PageRequest(page, pageSize, createDefaultSort())); List<V> results = converter.convertListTo(pageResults.getContent()); return createFindResponse(results, pageResults.getTotalElements()); } /** * Create a response. */ protected abstract R createResponse(V result); /** * Create a find response with the count representing the size of the list. */ protected abstract FR createFindResponse(List<V> results); /** * Create a find response with the results representing the page request * and the count representing the size of the query. */ protected abstract FR createFindResponse(List<V> results, long count); /** * Whether or not the primary key is valid (greater than zero). */ protected boolean isPrimaryKeyValid(V request) { return DBUtil.isPrimaryKeyValid(request); } /** * Creates default sort. */ private Sort createDefaultSort() { return new Sort("lastName", "firstName"); } }
Example 8. AbstractPersistenceService
The AbstractPersistenceService
extends AbstractPersistenceFindService
and adds the methods create
/update
/delete
.
The create
& update
methods are separated
they can have different Spring Security
annotations applied, even though they both call into the same method for actually saving (doSave
).
The create
/update
/delete
methods also all are marked
with @Transactional
to override the default transactional configuration
since they are not read-only transactions.
public abstract class AbstractPersistenceService<T extends AbstractPersistable<Integer>, V extends PkEntityBase, R extends EntityResponseResult, FR extends EntityFindResponseResult> extends AbstractPersistenceFindService<T, V, R, FR> implements PersistenceService<V, R, FR> { protected static final String DELETE_MSG = "delete.msg"; public AbstractPersistenceService(JpaRepository<T, Integer> repository, ListConverter<T, V> converter, MessageHelper messageHelper) { super(repository, converter, messageHelper); } @Override @Transactional public R create(V request) { Assert.isTrue(!isPrimaryKeyValid(request), "Create should not have a valid primary key."); return doSave(request); } @Override @Transactional public final R update(V request) { Assert.isTrue(isPrimaryKeyValid(request), "Update should have a valid primary key."); return doSave(request); } @Override @Transactional public ResponseResult delete(Integer id) { return doDelete(id); } /** * Processes save. Can be overridden for custom save logic. */ protected R doSave(V request) { V result = null; T convertedRequest = converter.convertFrom(request); // issues with lock version updating if flush isn't called T bean = repository.saveAndFlush(convertedRequest); result = converter.convertTo(bean); return createSaveResponse(result); } /** * Processes delete. Can be overridden for custom save logic. */ protected ResponseResult doDelete(long id) { repository.delete((int) id); repository.flush(); return createDeleteResponse(); } /** * Create a save response. */ protected abstract R createSaveResponse(V result); protected ResponseResult createDeleteResponse() { return new ResponseResult().withMessageList(new Message().withMessageType(MessageType.INFO) .withMessageKey(DELETE_MSG).withMessage(getMessage(DELETE_MSG))); } }