In this section, we are going to wire the business logic we need for our application.
Because we have set up the configuration for the JPA and Spring Data JPA, and because we have defined our entities and their relationships, we can now use this model for time and energy-saving.
The following steps will guide you through the changes:
edu.zipcloud.cloudstreetmarket.core.daos
package, we can find the following two interfaces: public interface HistoricalIndexRepository { Iterable<HistoricalIndex> findIntraDay(String code, Date of); Iterable<HistoricalIndex> findLastIntraDay(String code); HistoricalIndex findLastHistoric(String code); } public interface TransactionRepository { Iterable<Transaction> findAll(); Iterable<Transaction> findByUser(User user); Iterable<Transaction> findRecentTransactions(Date from); Iterable<Transaction> findRecentTransactions(int nb); }
HistoricalIndexRepositoryImpl
implementation out of the two is defined as follows:@Repository public class HistoricalIndexRepositoryImpl implements HistoricalIndexRepository{ @PersistenceContext private EntityManager em; @Override public Iterable<HistoricalIndex> findIntraDay(String code,Date of){ TypedQuery<HistoricalIndex> sqlQuery = em.createQuery("from HistoricalIndex h where h.index.code = ? and h.fromDate >= ? and h.toDate <= ? ORDER BY h.toDate asc", HistoricalIndex.class); sqlQuery.setParameter(1, code); sqlQuery.setParameter(2, DateUtil.getStartOfDay(of)); sqlQuery.setParameter(3, DateUtil.getEndOfDay(of)); return sqlQuery.getResultList(); } @Override public Iterable<HistoricalIndex> findLastIntraDay(String code) { return findIntraDay(code,findLastHistoric(code).getToDate()); } @Override public HistoricalIndex findLastHistoric(String code){ TypedQuery<HistoricalIndex> sqlQuery = em.createQuery("from HistoricalIndex h where h.index.code = ? ORDER BY h.toDate desc", HistoricalIndex.class); sqlQuery.setParameter(1, code); return sqlQuery.setMaxResults(1).getSingleResult(); } }
And the TransactionRepositoryImpl
implementation is as follows:
@Repository public class TransactionRepositoryImpl implements TransactionRepository{ @PersistenceContext private EntityManager em; @Autowired private TransactionRepositoryJpa repo; @Override public Iterable<Transaction> findByUser(User user) { TypedQuery<Transaction> sqlQuery = em.createQuery("from Transaction where user = ?", Transaction.class); return sqlQuery.setParameter(1, user).getResultList(); } @Override public Iterable<Transaction> findRecentTransactions(Date from) { TypedQuery<Transaction> sqlQuery = em.createQuery("from Transaction t where t.quote.date >= ?", Transaction.class); return sqlQuery.setParameter(1, from).getResultList(); } @Override public Iterable<Transaction> findRecentTransactions(int nb) { TypedQuery<Transaction> sqlQuery = em.createQuery("from Transaction t ORDER BY t.quote.date desc", Transaction.class); return sqlQuery.setMaxResults(nb).getResultList(); } @Override public Iterable<Transaction> findAll() { return repo.findAll(); } }
dao
package don't have explicitly defined implementations.<jdbc:initialize-database data-source="dataSource"> <jdbc:script location="classpath:/META-INF/db/init.sql"/> </jdbc:initialize-database>
init.sql
file on startup.cloudstreetmarket-core
module has been added in its pom.xml
file, a dependency to zipcloud-core
for the DateUtil
class that we created.CommunityServiceImpl
and MarketServiceImpl
implementations have been created.cloudstreetmarket-webapp
module, the DefaultController
has been modified in its @Autowired
field to target these new implementations and no longer the dummy ones. This is achieved by specifying the @Qualifier
annotations on the @Autowired
fields.http://localhost:8080/portal/index
, should log a couple of SQL queries into the console:Also, the Welcome page should remain the same.
Let's see the breakdown of this recipe with the following sections.
We saw in the first recipe of this chapter that the configuration of the entityManagerFactory
bean reflects the persistence unit's configuration.
Historically created by the container, EntityManagers need to handle transactions (user or container-manager transactions).
The @PersistenceContext
annotation is a JPA annotation. It allows us to inject an instance of EntityManager, whose lifecycle is managed by the container. In our case, Spring handles this role. With an EntityManager, we can interact with the persistence context, get managed or detached entities, and indirectly query the database.
Using Java Persistence Query Language (JPQL) is a standardized way of querying the persistence context and, indirectly, the database. JPQL looks like SQL in the syntax, but operates on the JPA-managed entities.
You must have noticed the following query in the repositories:
from Transaction where user = ?
The select part of the query is optional. Parameters can be injected into the query and this step is managed by the persistence providers’ implementation. Those implementations offer protections against SQL injection (using Prepared Statements) With the example here, take a look at how practical it is to filter a subentity attribute:
from Transaction t where t.quote.date >= ?
It avoids declaring a join when the situation is appropriate. We can still declare a JOIN
though:
from HistoricalIndex h where h.index.code = ? ORDER BY h.toDate desc
A couple of keywords (such as ORDER
) can be used as part of JPQL to operate functions that are usually available in SQL. Find the full list of keywords in the JPQL grammar from the JavaEE 6 tutorial at http://docs.oracle.com/javaee/6/tutorial/doc/bnbuf.html.
JPQL has been inspired from the earlier-created Hibernate Query Language (HQL).
We have discussed in the How to do it… section that some of our repository interfaces don't have explicitly defined implementations. This is a very powerful feature of Spring Data JPA.
Our UserRepository
interface is defined as follows:
@Repository public interface UserRepository extends JpaRepository<User, String>{ User findByUserName(String username); User findByUserNameAndPassword(String username, String password); }
We have made it extend the JpaRepository
interface, passing through the generic types User
(the entity type this repository will relate to) and String
(the type of the user's identifier field).
By extending JpaRepository
, UserRepository
gets from Spring Data JPA capability to define query methods from Spring Data JPA by simply declaring their method signature. We have done this with the methods findByUserName
and findByUserNameAndPassword
.
Spring Data JPA transparently creates an implementation of our UserRepository
interface at runtime. It infers the JPA queries from the way we have named our methods in the interface. Keywords and field names are used for this inference.
Find the following keywords table from the Spring Data JPA doc:
Without specifying anything in the configuration, we have fallen back to the configuration by default for JPA repositories, which injects an instance of our single EntityManagerFactory
bean and of our single TransactionManager
bean.
Our custom TransactionRepositoryImpl
is an example that uses both custom JPQL queries and a JpaRepository
implementation. As you might guess, the TransactionRepositoryJpa
implementation , which is autowired in TransactionRepositoryImpl
, inherits several methods for saving, deleting, and finding, Transaction
Entities.
We will also use interesting paging features offered with these methods. The findAll()
method, which we have pulled, is one of them.
Spring Data JPA also specifies the following:
Saving an entity can be performed via the CrudRepository.save(…)
method. It will persist or merge the given entity using the underlying JPA EntityManager. If the entity has not been persisted yet, Spring Data JPA will save the entity via a call to the entityManager.persist(…)
method; otherwise, the entityManager.merge(…)
will be called.
This is interesting behavior that we will use to prevent again, a significant amount of boilerplate code.
There are more aspects that can be explored around this topic.
We haven't made use of native SQL queries yet, but we will. It is important to know how to implement them because bypassing the JPA layer can sometimes be a better option performance-wise.
The following link points to an article from the Oracle website, which is interesting as it relates to native SQL queries:
http://www.oracle.com/technetwork/articles/vasiliev-jpql-087123.html
We haven't applied any specific transaction configuration to our repository implementations. Refer to Chapter 7, Developing CRUD Operations and Validations, for more details about transactions.
TransactionRepositoryImpl
example, by redefining the methods we need from TransactionRepositoryJpa
, we present a pattern for creating custom implementations of data repositories. It somehow forces us to maintain an intermediate proxy. The related Spring document proposes a different technique that solves this issue. This technique is detailed online at http://docs.spring.io/spring-data/jpa/docs/current/reference/html/#repositories.custom-implementations.