Service Class

Generally we define the Service class as the following:

ExpenseService.java

package service;

import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;

import resources.specification.ExpenseSpecification;
import sendto.ExpenseSendto;

public interface ExpenseService {

    public ExpenseSendto retrieve(long id);
    public void delete(long id);
    public ExpenseSendto save(ExpenseSendto expense);
    public Page<ExpenseSendto> findAll(ExpenseSpecification spec, Pageable pageable);
    public ExpenseSendto update(long id, ExpenseSendto expense);

}

But here we are going to use a Spring data's Specification interface where I was able to approximate the use of query by example next Chapter. The additional interface carries methods that allow you to execute Specifications in a variety of ways.

For example, the findAll method will return all entities that match the specification:

public Page<ExpenseSendto> findAll(Specification<Expense> spec,
            Pageable pageable);

Specifications can easily be used to build an extensible set of predicates on top of an entity that then can be combined and used with JpaRepository without the need to declare a query (method) for every needed combination. Here's an example:

package service;

import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.domain.Specification;

import sendto.ExpenseSendto;
import entity.Expense;

public interface ExpenseService {

    public ExpenseSendto retrieve(Specification<Expense> spec);
    public void delete(Specification<Expense> spec);
    public ExpenseSendto save(ExpenseSendto expense);
    public Page<ExpenseSendto> findAll(Specification<Expense> spec,
            Pageable pageable);
    public ExpenseSendto update(Specification<Expense> spec,
            ExpenseSendto expense);
}

We’re using @Transactional annotation at the top of the Service Implementation class. That basically means, that DAO methods will run within transcations. To make it work, as we have defined in our application-context.xml file and declare there transaction manager, which will be handling the transactions.So now lets declare the ServiceImpl class.

The @Transactional("transactionManager") annotation is not mandatory, but I needed this to get rid of several warnings at tests (or application) startup, like WARNING: transaction manager not running? in the logs.

ExpenseServiceImpl.java

package service.impl;

import java.util.ArrayList;
import java.util.List;

import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.transaction.annotation.Transactional;

import sendto.ExpenseSendto;
import service.ExpenseService;
import dao.ExpenseDao;
import dao.ExpenseTypeDao;
import dao.ReportDao;
import entity.Expense;
import exceptions.ExpenseNotFoundException;
import exceptions.ExpenseTypeNotFoundException;
import exceptions.ReportNotFoundException;

public class ExpenseServiceImpl implements ExpenseService {

    private ExpenseDao expenseDao;
    private ExpenseTypeDao expenseTypeDao;
    private ReportDao reportDao;

    public ExpenseServiceImpl(ExpenseDao expenseDao,
            ExpenseTypeDao expenseTypeDao, ReportDao reportDao) {
        this.expenseDao = expenseDao;
        this.expenseTypeDao = expenseTypeDao;
        this.reportDao = reportDao;
    }

    @Transactional("transactionManager")
    @Override
    public ExpenseSendto retrieve(Specification<Expense> spec) {
        Expense expense = expenseDao.findOne(spec);
        if (expense == null) {
            throw new ExpenseNotFoundException();
        }

        return toExpenseSendto(expense);
    }

    private ExpenseSendto toExpenseSendto(Expense expense) {
        ExpenseSendto ret = new ExpenseSendto();
        ret.setId(expense.getId());
        ret.setTotalAmount(expense.getTotalAmount());
        ret.setTaxAmount(expense.getTaxAmount());
        ret.setComment(expense.getComment());
        ExpenseSendto.Report rpt = new ExpenseSendto.Report();
        rpt.setId(expense.getReport().getId());
        ret.setReport(rpt);
        ExpenseSendto.ExpenseType expType = new ExpenseSendto.ExpenseType();
        expType.setId(expense.getExpenseType().getId());
        ret.setExpenseType(expType);
        return ret;
    }

    @Transactional("transactionManager")
    @Override
    public void delete(Specification<Expense> spec) {
        Expense expense = expenseDao.findOne(spec);
        if (expense == null) {
            throw new ExpenseNotFoundException();
        }
        expenseDao.delete(expense);
    }

    @Transactional("transactionManager")
    @Override
    public ExpenseSendto save(ExpenseSendto expense) {
        expense.setId(null);
        Expense newEntry = new Expense();
        setUpExpense(expense, newEntry);
        return toExpenseSendto(expenseDao.save(newEntry));
    }

    private void setUpExpense(ExpenseSendto sendto, Expense newEntry) {
        if (sendto.isCommentSet()) {
            newEntry.setComment(sendto.getComment());
        }
        if (sendto.isTotalAmountSet()) {
            newEntry.setTotalAmount(sendto.getTotalAmount());
        }
        if (sendto.isTaxAmountSet()) {
            newEntry.setTaxAmount(sendto.getTaxAmount());
        }
        if (sendto.isReportSet()) {
            if (sendto.getReport().isIdSet()) {
                entity.Report report = reportDao.findOne(sendto.getReport()
                        .getId());
                if (report == null) {
                    throw new ReportNotFoundException(sendto.getReport()
                            .getId());
                }
                newEntry.setReport(report);
            }
        }
        if (sendto.isExpenseTypeSet()) {
            if (sendto.getExpenseType().isIdSet()) {
                entity.ExpenseType expenseType = expenseTypeDao.findOne(sendto
                        .getExpenseType().getId());
                if (expenseType == null) {
                    throw new ExpenseTypeNotFoundException(sendto
                            .getExpenseType().getId());
                }
                newEntry.setExpenseType(expenseType);
            }
        }

    }

    @Transactional("transactionManager")
    @Override
    public Page<ExpenseSendto> findAll(Specification<Expense> spec,
            Pageable pageable) {
        List<ExpenseSendto> sendto = new ArrayList<ExpenseSendto>();
        Page<Expense> expenses = expenseDao.findAll(spec, pageable);
        for (Expense exp : expenses) {
            sendto.add(toExpenseSendto(exp));
        }
        Page<ExpenseSendto> rets = new PageImpl<ExpenseSendto>(sendto,
                pageable, expenses.getTotalElements());
        return rets;
    }

    @Transactional("transactionManager")
    @Override
    public ExpenseSendto update(Specification<Expense> spec,
            ExpenseSendto updated) {
        Expense exp = expenseDao.findOne(spec);
        if (exp == null) {
            throw new ExpenseNotFoundException();
        }
        setUpExpense(updated, exp);
        return toExpenseSendto(expenseDao.save(exp));
    }
}

The power of Specifications really shines when you combine them to create new Specification objects. You can achieve this through the Specifications helper class we provide to build expressions. For example in this btrs system using this specification it helps either the Admin, Accountant or the Applicant incharge of created report to do any kind of modification.

@DELETE
    @Path(value = "{reportid}")
    public Response deleteReport(@PathParam("id") long id,
            @PathParam("reportid") long reportId) {
        ReportSpecification spec = new ReportSpecification();
        spec.setId(reportId);
        reportService.delete(spec);
        return Response.status(Status.OK).type(MediaType.APPLICATION_JSON)
                .build();
    }

results matching ""

    No results matching ""