4. Persistence Service Code

Persistence Service Interface Code

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 in Spring by Example REST Module

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 in Spring by Example REST Module

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 R delete(V request);

}
                    
                

Persistence Service Abstract Code

Example 7. AbstractPersistenceFindService in Spring by Example REST Module

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 in Spring by Example REST Module

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 to a service interface, 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 R delete(V request) {
        return doDelete(request);
    }

    /**
     * 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 R doDelete(V request) {
        repository.delete(request.getId());
        repository.flush();

        return createDeleteResponse();
    }

    /**
     * Create a save response.
     */
    protected abstract R createSaveResponse(V result);

    /**
     * Create a delete response.
     */
    protected abstract R createDeleteResponse();

}