Related
I am looking into Spring Data JPA. Consider the below example where I will get all the crud and finder functionality working by default and if I want to customize a finder then that can be also done easily in the interface itself.
#Transactional(readOnly = true)
public interface AccountRepository extends JpaRepository<Account, Long> {
#Query("<JPQ statement here>")
List<Account> findByCustomer(Customer customer);
}
I would like to know how can I add a complete custom method with its implementation for the above AccountRepository? Since its an Interface I cannot implement the method there.
You need to create a separate interface for your custom methods:
public interface AccountRepository
extends JpaRepository<Account, Long>, AccountRepositoryCustom { ... }
public interface AccountRepositoryCustom {
public void customMethod();
}
and provide an implementation class for that interface:
public class AccountRepositoryImpl implements AccountRepositoryCustom {
#Autowired
#Lazy
AccountRepository accountRepository; /* Optional - if you need it */
public void customMethod() { ... }
}
See also:
4.6 Custom Implementations for Spring Data Repositories
Note that the naming scheme has changed between versions. See https://stackoverflow.com/a/52624752/66686 for details.
In addition to axtavt's answer, don't forget you can inject Entity Manager in your custom implementation if you need it to build your queries:
public class AccountRepositoryImpl implements AccountRepositoryCustom {
#PersistenceContext
private EntityManager em;
public void customMethod() {
...
em.createQuery(yourCriteria);
...
}
}
There's a slightly modified solution that does not require additional interfaces.
As specificed in the documented functionality, the Impl suffix allows us to have such clean solution:
Define in you regular #Repository interface, say MyEntityRepository the custom methods (in addition to your Spring Data methods)
Create a class MyEntityRepositoryImpl (the Impl suffix is the magic) anywhere (doesn't even need to be in the same package) that implements the custom methods only and annotate such class with #Component** (#Repository will not work).
This class can even inject MyEntityRepository via #Autowired for use in the custom methods.
Example:
Entity class (for completeness):
package myapp.domain.myentity;
#Entity
public class MyEntity {
#Id private Long id;
#Column private String comment;
}
Repository interface:
package myapp.domain.myentity;
#Repository
public interface MyEntityRepository extends JpaRepository<MyEntity, Long> {
// EXAMPLE SPRING DATA METHOD
List<MyEntity> findByCommentEndsWith(String x);
List<MyEntity> doSomeHql(Long id); // custom method, code at *Impl class below
List<MyEntity> useTheRepo(Long id); // custom method, code at *Impl class below
}
Custom methods implementation bean:
package myapp.infrastructure.myentity;
#Component // Must be #Component !!
public class MyEntityRepositoryImpl { // must have the exact repo name + Impl !!
#PersistenceContext
private EntityManager entityManager;
#Autowired
private MyEntityRepository myEntityRepository;
#SuppressWarnings("unused")
public List<MyEntity> doSomeHql(Long id) {
String hql = "SELECT eFROM MyEntity e WHERE e.id = :id";
TypedQuery<MyEntity> query = entityManager.createQuery(hql, MyEntity.class);
query.setParameter("id", id);
return query.getResultList();
}
#SuppressWarnings("unused")
public List<MyEntity> useTheRepo(Long id) {
List<MyEntity> es = doSomeHql(id);
es.addAll(myEntityRepository.findByCommentEndsWith("DO"));
es.add(myEntityRepository.findById(2L).get());
return es;
}
}
Usage:
// You just autowire the the MyEntityRepository as usual
// (the Impl class is just impl detail, the clients don't even know about it)
#Service
public class SomeService {
#Autowired
private MyEntityRepository myEntityRepository;
public void someMethod(String x, long y) {
// call any method as usual
myEntityRepository.findByCommentEndsWith(x);
myEntityRepository.doSomeHql(y);
}
}
And that's all, no need for any interfaces other than the Spring Data repo one you already have.
The only possible drawbacks I identified are:
The custom methods in the Impl class are marked as unused by the compiler, thus the #SuppressWarnings("unused") suggestion.
You have a limit of one Impl class. (Whereas in the regular fragment interfaces implementation the docs suggest you could have many.)
If you place the Impl class at a different package and your test uses only #DataJpaTest, you have to add #ComponentScan("package.of.the.impl.clazz") to your test, so Spring loads it.
The accepted answer works, but has three problems:
It uses an undocumented Spring Data feature when naming the custom implementation as AccountRepositoryImpl. The documentation clearly states that it has to be called AccountRepositoryCustomImpl, the custom interface name plus Impl
You cannot use constructor injection, only #Autowired, that are considered bad practice
You have a circular dependency inside of the custom implementation (that's why you cannot use constructor injection).
I found a way to make it perfect, though not without using another undocumented Spring Data feature:
public interface AccountRepository extends AccountRepositoryBasic,
AccountRepositoryCustom
{
}
public interface AccountRepositoryBasic extends JpaRepository<Account, Long>
{
// standard Spring Data methods, like findByLogin
}
public interface AccountRepositoryCustom
{
public void customMethod();
}
public class AccountRepositoryCustomImpl implements AccountRepositoryCustom
{
private final AccountRepositoryBasic accountRepositoryBasic;
// constructor-based injection
public AccountRepositoryCustomImpl(
AccountRepositoryBasic accountRepositoryBasic)
{
this.accountRepositoryBasic = accountRepositoryBasic;
}
public void customMethod()
{
// we can call all basic Spring Data methods using
// accountRepositoryBasic
}
}
This is limited in usage, but for simple custom methods you can use default interface methods like:
import demo.database.Customer;
import org.springframework.data.repository.CrudRepository;
public interface CustomerService extends CrudRepository<Customer, Long> {
default void addSomeCustomers() {
Customer[] customers = {
new Customer("Józef", "Nowak", "nowakJ#o2.pl", 679856885, "Rzeszów", "Podkarpackie", "35-061", "Zamknięta 12"),
new Customer("Adrian", "Mularczyk", "adii333#wp.pl", 867569344, "Krosno", "Podkarpackie", "32-442", "Hynka 3/16"),
new Customer("Kazimierz", "Dejna", "sobieski22#weebly.com", 996435876, "Jarosław", "Podkarpackie", "25-122", "Korotyńskiego 11"),
new Customer("Celina", "Dykiel", "celina.dykiel39#yahoo.org", 947845734, "Żywiec", "Śląskie", "54-333", "Polna 29")
};
for (Customer customer : customers) {
save(customer);
}
}
}
EDIT:
In this spring tutorial it is written:
Spring Data JPA also allows you to define other query methods by
simply declaring their method signature.
So it is even possible to just declare method like:
Customer findByHobby(Hobby personHobby);
and if object Hobby is a property of Customer then Spring will automatically define method for you.
Im using the following code in order to access generated find methods from my custom implementation. Getting the implementation through the bean factory prevents circular bean creation problems.
public class MyRepositoryImpl implements MyRepositoryExtensions, BeanFactoryAware {
private BrandRepository myRepository;
public MyBean findOne(int first, int second) {
return myRepository.findOne(new Id(first, second));
}
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
myRepository = beanFactory.getBean(MyRepository.class);
}
}
Considering your code snippet, please note that you can only pass Native objects to the findBy### method, lets say you want to load a list of accounts that belongs certain costumers, one solution is to do this,
#Query("Select a from Account a where a."#nameoffield"=?1")
List<Account> findByCustomer(String "#nameoffield");
Make sue the name of the table to be queried is thesame as the Entity class.
For further implementations please take a look at this
If you want to be able to do more sophisticated operations you might need access to Spring Data's internals, in which case the following works (as my interim solution to DATAJPA-422):
public class AccountRepositoryImpl implements AccountRepositoryCustom {
#PersistenceContext
private EntityManager entityManager;
private JpaEntityInformation<Account, ?> entityInformation;
#PostConstruct
public void postConstruct() {
this.entityInformation = JpaEntityInformationSupport.getMetadata(Account.class, entityManager);
}
#Override
#Transactional
public Account saveWithReferenceToOrganisation(Account entity, long organisationId) {
entity.setOrganisation(entityManager.getReference(Organisation.class, organisationId));
return save(entity);
}
private Account save(Account entity) {
// save in same way as SimpleJpaRepository
if (entityInformation.isNew(entity)) {
entityManager.persist(entity);
return entity;
} else {
return entityManager.merge(entity);
}
}
}
There is another issue to be considered here. Some people expect that adding custom method to your repository will automatically expose them as REST services under '/search' link. This is unfortunately not the case. Spring doesn't support that currently.
This is 'by design' feature, spring data rest explicitly checks if method is a custom method and doesn't expose it as a REST search link:
private boolean isQueryMethodCandidate(Method method) {
return isQueryAnnotationPresentOn(method) || !isCustomMethod(method) && !isBaseClassMethod(method);
}
This is a qoute of Oliver Gierke:
This is by design. Custom repository methods are no query methods as
they can effectively implement any behavior. Thus, it's currently
impossible for us to decide about the HTTP method to expose the method
under. POST would be the safest option but that's not in line with the
generic query methods (which receive GET).
For more details see this issue: https://jira.spring.io/browse/DATAREST-206
I liked Danila's solution and started using it but nobody else on the team liked having to create 4 classes for each repository. Danila's solution is the only one here that let's you use the Spring Data methods in the Impl class. However, I found a way to do it with just a single class:
public interface UserRepository extends MongoAccess, PagingAndSortingRepository<User> {
List<User> getByUsername(String username);
default List<User> getByUsernameCustom(String username) {
// Can call Spring Data methods!
findAll();
// Can write your own!
MongoOperations operations = getMongoOperations();
return operations.find(new Query(Criteria.where("username").is(username)), User.class);
}
}
You just need some way of getting access to your db bean (in this example, MongoOperations). MongoAccess provides that access to all of your repositories by retrieving the bean directly:
public interface MongoAccess {
default MongoOperations getMongoOperations() {
return BeanAccessor.getSingleton(MongoOperations.class);
}
}
Where BeanAccessor is:
#Component
public class BeanAccessor implements ApplicationContextAware {
private static ApplicationContext applicationContext;
public static <T> T getSingleton(Class<T> clazz){
return applicationContext.getBean(clazz);
}
public static <T> T getSingleton(String beanName, Class<T> clazz){
return applicationContext.getBean(beanName, clazz);
}
#Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
BeanAccessor.applicationContext = applicationContext;
}
}
Unfortunately, you can't #Autowire in an interface. You could autowire the bean into a MongoAccessImpl and provide a method in the interface to access it, but Spring Data blows up. I don't think it expects to see an Impl associated even indirectly with PagingAndSortingRepository.
I faced with this using mongo and spring. So let's assume we use MongoRepository to provided base crud operations, and let's say we need to implement some custom criteria query operation using mongoTemplate. To achieve one interface to inject repository for crud and custom we need to specify:
Custom interface:
public interface UserCustomRepository {
List<User> findAllUsersBySomeCriteria(UserCriteriaRequest criteriaRequest);
}
UserRepository interface 'must' first extends UserCustomRepository and then MongoRepository
#Repository
public interface UserRepository extends UserCustomRepository, MongoRepository<User, ObjectId> {
}
UserRepositoryImpl must have the same name as what crud interface with *Impl suffix.
#Component
#NoArgsConstructor
#AllArgsConstructor(onConstructor = #__(#Autowired))
public class UserRepositoryImpl implements UserCustomRepository {
private MongoTemplate mongoTemplate;
#Override
public List<User> findAllUsersBySomeCriteria(UserCriteriaRequest criteriaRequest){
//some impl
}
}
Let's impl some service - here we inject only UserRepository interface and use methods from crud repository and custom class impl.
#Service
#NoArgsConstructor
#AllArgsConstructor(onConstructor = #__(#Autowired))
public class UserService {
private UserRepository userReposityry;
public List<User> getUserByCriteria(UserCriteriaRequest request) {
userRepository.findById(request.getUserId); // Crud repository method
userRepository.findAllUsersBySomeCriteria(request); // custom method.
}
}
I extends the SimpleJpaRepository:
public class ExtendedRepositoryImpl<T extends EntityBean> extends SimpleJpaRepository<T, Long>
implements ExtendedRepository<T> {
private final JpaEntityInformation<T, ?> entityInformation;
private final EntityManager em;
public ExtendedRepositoryImpl(final JpaEntityInformation<T, ?> entityInformation,
final EntityManager entityManager) {
super(entityInformation, entityManager);
this.entityInformation = entityInformation;
this.em = entityManager;
}
}
and adds this class to #EnableJpaRepositoryries repositoryBaseClass.
I use SimpleJpaRepository as the base class of repository implementation and add custom method in the interface,eg:
public interface UserRepository {
User FindOrInsert(int userId);
}
#Repository
public class UserRepositoryImpl extends SimpleJpaRepository implements UserRepository {
private RedisClient redisClient;
public UserRepositoryImpl(RedisClient redisClient, EntityManager em) {
super(User.class, em);
this.redisClient = redisClient;
}
#Override
public User FindOrInsert(int userId) {
User u = redisClient.getOrSet("test key.. User.class, () -> {
Optional<User> ou = this.findById(Integer.valueOf(userId));
return ou.get();
});
…………
return u;
}
Out team is currently programming a JavaEE webapplication for use on a Tomcat appserver.
We want to handle persistence using Hibernate (5.0.1). To access the database entities, we use EntityManagers (not from JPA, they were implemented by us, see below) which provide methods to list, create and delete rows in the associated tables. The model classes use Hibernate Annotations for the mapping.
We also have a static class PersistenceController which initializes Hibernate's SessionFactory and provides a static method to get a newly opened session.
Of course we want to be able to use unit tests to test the functionality of our classes, so the PersistenceController is a little thorn in our eyes.
Someone else recommended me to move everything from the PersistenceController into the EntityManager base class. He wasn't sure if this would have any side consequences though.
So I thought "let's ask the hive mind". What would be the best practice in this case?
(If more code is needed, I'm happy to provide it)
PersistenceController
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.boot.MetadataSources;
import org.hibernate.boot.registry.StandardServiceRegistry;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
public class PersistenceController {
private static final SessionFactory sessionFactory;
static {
final StandardServiceRegistry registry = new StandardServiceRegistryBuilder()
.configure() // configures settings from hibernate.cfg.xml
.build();
try {
sessionFactory = new MetadataSources(registry).buildMetadata()
.buildSessionFactory();
} catch (Exception e) {
// The registry would be destroyed by the SessionFactory, but we had
// trouble building the SessionFactory
// so destroy it manually.
StandardServiceRegistryBuilder.destroy(registry);
throw e;
}
}
public static Session openSession() {
return sessionFactory.openSession();
}
}
EntityManager
import java.util.List;
public abstract class EntityManager<T extends PersistenceEntity> {
public abstract List<T> listAll();
public abstract void save(T entity);
public abstract void delete(T entity);
}
ProductManager
import java.util.ArrayList;
import java.util.List;
import org.hibernate.Session;
import org.hibernate.Transaction;
import etepat.model.product.Product;
public class ProductManager extends EntityManager<Product> {
public ProductManager() {
super();
}
#Override
public List<Product> listAll() {
try (Session session = PersistenceController.openSession()) {
Transaction transaction = null;
try {
transaction = session.beginTransaction();
#SuppressWarnings("unchecked")
List<Product> returned = session.createCriteria(Product.class)
.list();
transaction.commit();
return returned;
} catch (Exception e) {
if (transaction != null) {
transaction.rollback();
}
e.printStackTrace();
}
}
return new ArrayList<Product>();
}
#Override
public void save(Product entity) {
// TODO Auto-generated method stub
}
#Override
public void delete(Product entity) {
// TODO Auto-generated method stub
}
}
The idea of your EntityManager(better call BaseDao or GenericDao) is good but needs some improvements.
First, base CRUD methods don't have to be abstract. They can simply persist/load/delete/list on generic type T. That way you don't have to write these methods on each subclass. See this generic dao approach https://github.com/athanasiosem/Hibernate-Generic-Dao-Java/blob/master/src/main/java/com/demien/hibgeneric/dao/GenericDAOImpl.java
Second is, you're managing transactions manually, is there a good reason to do so?
With container managed transactions(using annotations) you don't need to, and they greatly simplify your code by eliminating the boilerplate try{...}catch{//rollback}.
Basically, with a GenericDao<T> and container managed transactions you don't need this code at all, your classes sublass the GenericDao<ConcreteType> with concrete types and they're ready to do CRUD on database without a single line of code.
Some time ago I started to learn java EE. I swiftly moved to spring(mvc)+hibernate. As I was learning about databases and integration with spring+hibernate I came up with an idea.
As far as I noticed(and understand) there's a common approach to build an objects structure including configuration files, entities, dao interface and dao implementation(as we're talking just about dbs, not controllers and other applications' layers). I decided to write a generic java class and call it BasicDao. It's a template which takes entity as a type.
This is actually working and I think it's much better than interfaces and implementations, because you need only one class for all entities(if you wanted to write separated implementations for each entity you might end up with a big amount of files).
I also wrote some template functions there so the class is very flexible(no exceptions with types passed to the db). Here's the code
package local.bb.dao;
import java.util.List;
import org.hibernate.SessionFactory;
import org.hibernate.criterion.Restrictions;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
#Repository(value = "basicDao")
#Transactional(propagation = Propagation.REQUIRED,readOnly = true)
public class BasicDao<ENTITY> {
private Class<ENTITY> data;
private SessionFactory sessionFactory;
public BasicDao() {
this.data = null;
}
#Transactional
public void addRecord(ENTITY t) {
this.sessionFactory.getCurrentSession().save(t);
}
#Transactional
public void removeRecord(ENTITY t) {
this.sessionFactory.getCurrentSession().delete(t);
}
#Transactional
public List<ENTITY> getAllRecords() {
return (List<ENTITY>)this.sessionFactory.getCurrentSession().createCriteria(this.data).list();
}
#Transactional
public <TYPE> ENTITY getRecordByParam(String param, TYPE value) {
return (ENTITY)this.sessionFactory.getCurrentSession().createCriteria(this.data).add(Restrictions.eq(param, value)).uniqueResult();
}
#Transactional
public <TYPE> List<ENTITY> getRecordsByParam(String param, TYPE value) {
return (List<ENTITY>)this.sessionFactory.getCurrentSession().createCriteria(this.data).add(Restrictions.like(param, value)).list();
}
// GETTERS / SETTERS
public SessionFactory getSessionFactory() {
return sessionFactory;
}
#Autowired
public void setSessionFactory(SessionFactory sessionFactory) {
this.sessionFactory = sessionFactory;
}
public Class<ENTITY> getData() {
return data;
}
public void setData(Class<ENTITY> data) {
this.data = data;
}
}
The question, finally, is: is it a good approach actually? Because I've never seen such code anywhere(speaking about tutorials on the Internet and books).
Spring likes interfaces since a couple of important mechanism are based on it, e.g. AOP, interceptors. So, if you decide to go without them you have to accept certain limits to Spring functionality. What's more, it will be harder to write Test-Mocks for other classes that depend on your DAOs.
If you want to save code, I suggest you lose the implementation rather than the interface. With Spring JPA you can annotate a DAO interface with as set of annotations, i.e. #Query, #Procedure, #Modifying etc to define how the data is accessed. If you then enable JPA repositories in your application context, Spring will supply the DAO implementation for you.
More information can be found here.
Yes you can write generic DAO in hibernate and if it's useful and helps you to reduce substantial amount of code and makes your codebase clean then you should go for it. I have used generic DAO but only for simple CRUD operations. Here is a good read on generic persistence layer.
I am looking into Spring Data JPA. Consider the below example where I will get all the crud and finder functionality working by default and if I want to customize a finder then that can be also done easily in the interface itself.
#Transactional(readOnly = true)
public interface AccountRepository extends JpaRepository<Account, Long> {
#Query("<JPQ statement here>")
List<Account> findByCustomer(Customer customer);
}
I would like to know how can I add a complete custom method with its implementation for the above AccountRepository? Since its an Interface I cannot implement the method there.
You need to create a separate interface for your custom methods:
public interface AccountRepository
extends JpaRepository<Account, Long>, AccountRepositoryCustom { ... }
public interface AccountRepositoryCustom {
public void customMethod();
}
and provide an implementation class for that interface:
public class AccountRepositoryImpl implements AccountRepositoryCustom {
#Autowired
#Lazy
AccountRepository accountRepository; /* Optional - if you need it */
public void customMethod() { ... }
}
See also:
4.6 Custom Implementations for Spring Data Repositories
Note that the naming scheme has changed between versions. See https://stackoverflow.com/a/52624752/66686 for details.
In addition to axtavt's answer, don't forget you can inject Entity Manager in your custom implementation if you need it to build your queries:
public class AccountRepositoryImpl implements AccountRepositoryCustom {
#PersistenceContext
private EntityManager em;
public void customMethod() {
...
em.createQuery(yourCriteria);
...
}
}
There's a slightly modified solution that does not require additional interfaces.
As specificed in the documented functionality, the Impl suffix allows us to have such clean solution:
Define in you regular #Repository interface, say MyEntityRepository the custom methods (in addition to your Spring Data methods)
Create a class MyEntityRepositoryImpl (the Impl suffix is the magic) anywhere (doesn't even need to be in the same package) that implements the custom methods only and annotate such class with #Component** (#Repository will not work).
This class can even inject MyEntityRepository via #Autowired for use in the custom methods.
Example:
Entity class (for completeness):
package myapp.domain.myentity;
#Entity
public class MyEntity {
#Id private Long id;
#Column private String comment;
}
Repository interface:
package myapp.domain.myentity;
#Repository
public interface MyEntityRepository extends JpaRepository<MyEntity, Long> {
// EXAMPLE SPRING DATA METHOD
List<MyEntity> findByCommentEndsWith(String x);
List<MyEntity> doSomeHql(Long id); // custom method, code at *Impl class below
List<MyEntity> useTheRepo(Long id); // custom method, code at *Impl class below
}
Custom methods implementation bean:
package myapp.infrastructure.myentity;
#Component // Must be #Component !!
public class MyEntityRepositoryImpl { // must have the exact repo name + Impl !!
#PersistenceContext
private EntityManager entityManager;
#Autowired
private MyEntityRepository myEntityRepository;
#SuppressWarnings("unused")
public List<MyEntity> doSomeHql(Long id) {
String hql = "SELECT eFROM MyEntity e WHERE e.id = :id";
TypedQuery<MyEntity> query = entityManager.createQuery(hql, MyEntity.class);
query.setParameter("id", id);
return query.getResultList();
}
#SuppressWarnings("unused")
public List<MyEntity> useTheRepo(Long id) {
List<MyEntity> es = doSomeHql(id);
es.addAll(myEntityRepository.findByCommentEndsWith("DO"));
es.add(myEntityRepository.findById(2L).get());
return es;
}
}
Usage:
// You just autowire the the MyEntityRepository as usual
// (the Impl class is just impl detail, the clients don't even know about it)
#Service
public class SomeService {
#Autowired
private MyEntityRepository myEntityRepository;
public void someMethod(String x, long y) {
// call any method as usual
myEntityRepository.findByCommentEndsWith(x);
myEntityRepository.doSomeHql(y);
}
}
And that's all, no need for any interfaces other than the Spring Data repo one you already have.
The only possible drawbacks I identified are:
The custom methods in the Impl class are marked as unused by the compiler, thus the #SuppressWarnings("unused") suggestion.
You have a limit of one Impl class. (Whereas in the regular fragment interfaces implementation the docs suggest you could have many.)
If you place the Impl class at a different package and your test uses only #DataJpaTest, you have to add #ComponentScan("package.of.the.impl.clazz") to your test, so Spring loads it.
The accepted answer works, but has three problems:
It uses an undocumented Spring Data feature when naming the custom implementation as AccountRepositoryImpl. The documentation clearly states that it has to be called AccountRepositoryCustomImpl, the custom interface name plus Impl
You cannot use constructor injection, only #Autowired, that are considered bad practice
You have a circular dependency inside of the custom implementation (that's why you cannot use constructor injection).
I found a way to make it perfect, though not without using another undocumented Spring Data feature:
public interface AccountRepository extends AccountRepositoryBasic,
AccountRepositoryCustom
{
}
public interface AccountRepositoryBasic extends JpaRepository<Account, Long>
{
// standard Spring Data methods, like findByLogin
}
public interface AccountRepositoryCustom
{
public void customMethod();
}
public class AccountRepositoryCustomImpl implements AccountRepositoryCustom
{
private final AccountRepositoryBasic accountRepositoryBasic;
// constructor-based injection
public AccountRepositoryCustomImpl(
AccountRepositoryBasic accountRepositoryBasic)
{
this.accountRepositoryBasic = accountRepositoryBasic;
}
public void customMethod()
{
// we can call all basic Spring Data methods using
// accountRepositoryBasic
}
}
This is limited in usage, but for simple custom methods you can use default interface methods like:
import demo.database.Customer;
import org.springframework.data.repository.CrudRepository;
public interface CustomerService extends CrudRepository<Customer, Long> {
default void addSomeCustomers() {
Customer[] customers = {
new Customer("Józef", "Nowak", "nowakJ#o2.pl", 679856885, "Rzeszów", "Podkarpackie", "35-061", "Zamknięta 12"),
new Customer("Adrian", "Mularczyk", "adii333#wp.pl", 867569344, "Krosno", "Podkarpackie", "32-442", "Hynka 3/16"),
new Customer("Kazimierz", "Dejna", "sobieski22#weebly.com", 996435876, "Jarosław", "Podkarpackie", "25-122", "Korotyńskiego 11"),
new Customer("Celina", "Dykiel", "celina.dykiel39#yahoo.org", 947845734, "Żywiec", "Śląskie", "54-333", "Polna 29")
};
for (Customer customer : customers) {
save(customer);
}
}
}
EDIT:
In this spring tutorial it is written:
Spring Data JPA also allows you to define other query methods by
simply declaring their method signature.
So it is even possible to just declare method like:
Customer findByHobby(Hobby personHobby);
and if object Hobby is a property of Customer then Spring will automatically define method for you.
Im using the following code in order to access generated find methods from my custom implementation. Getting the implementation through the bean factory prevents circular bean creation problems.
public class MyRepositoryImpl implements MyRepositoryExtensions, BeanFactoryAware {
private BrandRepository myRepository;
public MyBean findOne(int first, int second) {
return myRepository.findOne(new Id(first, second));
}
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
myRepository = beanFactory.getBean(MyRepository.class);
}
}
Considering your code snippet, please note that you can only pass Native objects to the findBy### method, lets say you want to load a list of accounts that belongs certain costumers, one solution is to do this,
#Query("Select a from Account a where a."#nameoffield"=?1")
List<Account> findByCustomer(String "#nameoffield");
Make sue the name of the table to be queried is thesame as the Entity class.
For further implementations please take a look at this
If you want to be able to do more sophisticated operations you might need access to Spring Data's internals, in which case the following works (as my interim solution to DATAJPA-422):
public class AccountRepositoryImpl implements AccountRepositoryCustom {
#PersistenceContext
private EntityManager entityManager;
private JpaEntityInformation<Account, ?> entityInformation;
#PostConstruct
public void postConstruct() {
this.entityInformation = JpaEntityInformationSupport.getMetadata(Account.class, entityManager);
}
#Override
#Transactional
public Account saveWithReferenceToOrganisation(Account entity, long organisationId) {
entity.setOrganisation(entityManager.getReference(Organisation.class, organisationId));
return save(entity);
}
private Account save(Account entity) {
// save in same way as SimpleJpaRepository
if (entityInformation.isNew(entity)) {
entityManager.persist(entity);
return entity;
} else {
return entityManager.merge(entity);
}
}
}
There is another issue to be considered here. Some people expect that adding custom method to your repository will automatically expose them as REST services under '/search' link. This is unfortunately not the case. Spring doesn't support that currently.
This is 'by design' feature, spring data rest explicitly checks if method is a custom method and doesn't expose it as a REST search link:
private boolean isQueryMethodCandidate(Method method) {
return isQueryAnnotationPresentOn(method) || !isCustomMethod(method) && !isBaseClassMethod(method);
}
This is a qoute of Oliver Gierke:
This is by design. Custom repository methods are no query methods as
they can effectively implement any behavior. Thus, it's currently
impossible for us to decide about the HTTP method to expose the method
under. POST would be the safest option but that's not in line with the
generic query methods (which receive GET).
For more details see this issue: https://jira.spring.io/browse/DATAREST-206
I liked Danila's solution and started using it but nobody else on the team liked having to create 4 classes for each repository. Danila's solution is the only one here that let's you use the Spring Data methods in the Impl class. However, I found a way to do it with just a single class:
public interface UserRepository extends MongoAccess, PagingAndSortingRepository<User> {
List<User> getByUsername(String username);
default List<User> getByUsernameCustom(String username) {
// Can call Spring Data methods!
findAll();
// Can write your own!
MongoOperations operations = getMongoOperations();
return operations.find(new Query(Criteria.where("username").is(username)), User.class);
}
}
You just need some way of getting access to your db bean (in this example, MongoOperations). MongoAccess provides that access to all of your repositories by retrieving the bean directly:
public interface MongoAccess {
default MongoOperations getMongoOperations() {
return BeanAccessor.getSingleton(MongoOperations.class);
}
}
Where BeanAccessor is:
#Component
public class BeanAccessor implements ApplicationContextAware {
private static ApplicationContext applicationContext;
public static <T> T getSingleton(Class<T> clazz){
return applicationContext.getBean(clazz);
}
public static <T> T getSingleton(String beanName, Class<T> clazz){
return applicationContext.getBean(beanName, clazz);
}
#Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
BeanAccessor.applicationContext = applicationContext;
}
}
Unfortunately, you can't #Autowire in an interface. You could autowire the bean into a MongoAccessImpl and provide a method in the interface to access it, but Spring Data blows up. I don't think it expects to see an Impl associated even indirectly with PagingAndSortingRepository.
I faced with this using mongo and spring. So let's assume we use MongoRepository to provided base crud operations, and let's say we need to implement some custom criteria query operation using mongoTemplate. To achieve one interface to inject repository for crud and custom we need to specify:
Custom interface:
public interface UserCustomRepository {
List<User> findAllUsersBySomeCriteria(UserCriteriaRequest criteriaRequest);
}
UserRepository interface 'must' first extends UserCustomRepository and then MongoRepository
#Repository
public interface UserRepository extends UserCustomRepository, MongoRepository<User, ObjectId> {
}
UserRepositoryImpl must have the same name as what crud interface with *Impl suffix.
#Component
#NoArgsConstructor
#AllArgsConstructor(onConstructor = #__(#Autowired))
public class UserRepositoryImpl implements UserCustomRepository {
private MongoTemplate mongoTemplate;
#Override
public List<User> findAllUsersBySomeCriteria(UserCriteriaRequest criteriaRequest){
//some impl
}
}
Let's impl some service - here we inject only UserRepository interface and use methods from crud repository and custom class impl.
#Service
#NoArgsConstructor
#AllArgsConstructor(onConstructor = #__(#Autowired))
public class UserService {
private UserRepository userReposityry;
public List<User> getUserByCriteria(UserCriteriaRequest request) {
userRepository.findById(request.getUserId); // Crud repository method
userRepository.findAllUsersBySomeCriteria(request); // custom method.
}
}
I extends the SimpleJpaRepository:
public class ExtendedRepositoryImpl<T extends EntityBean> extends SimpleJpaRepository<T, Long>
implements ExtendedRepository<T> {
private final JpaEntityInformation<T, ?> entityInformation;
private final EntityManager em;
public ExtendedRepositoryImpl(final JpaEntityInformation<T, ?> entityInformation,
final EntityManager entityManager) {
super(entityInformation, entityManager);
this.entityInformation = entityInformation;
this.em = entityManager;
}
}
and adds this class to #EnableJpaRepositoryries repositoryBaseClass.
I use SimpleJpaRepository as the base class of repository implementation and add custom method in the interface,eg:
public interface UserRepository {
User FindOrInsert(int userId);
}
#Repository
public class UserRepositoryImpl extends SimpleJpaRepository implements UserRepository {
private RedisClient redisClient;
public UserRepositoryImpl(RedisClient redisClient, EntityManager em) {
super(User.class, em);
this.redisClient = redisClient;
}
#Override
public User FindOrInsert(int userId) {
User u = redisClient.getOrSet("test key.. User.class, () -> {
Optional<User> ou = this.findById(Integer.valueOf(userId));
return ou.get();
});
…………
return u;
}
my question is: Are your service layer bound to tecnology you use?
For example, if you using hibernate, you put into your service layer some hql-queries or criteria-queries that are only hibernate features or you call simply DAO(and dao has hibernate implementation, and maybe jdbc implementation etc..) ?
I have some trouble to build an efficent layered architecture for my software.
EDIT
This is a simple service...i think it's a service... without bound to tecnlogy i using (hibernate)
#Repository
public class PersonHibernateDAO implements PersonDAO {
#Autowired
SessionFactory sessionFactory;
... dao crud operations(implementation of PersonDAO interface) using sessionfactory ...
//and some hibernate features methods
public Person findByCriteria(Criterion criterion){
// code
}
}
#Service
public class PersonService {
#Autowired
private PersonDAO personDao;
#Autowired
private AccessDAO accessDao;
#Transactional
public boolean hasPermission(String username, String accessCode){
Person p=personDao.findByUsername(username);
Access a=accessDao.findByCode(accessCode);
... etc ...
}
}
And this is a service with use Dao implementation
#Service
public class PersonService {
#Autowired
private PersonDAO personDao;
#Autowired
private AccessDAO accessDao;
#Transactional
public boolean hasPermission(String username, String password){
Person p=((PersonHibernateDao)personDao).findByCriteria(Restrictions.eq("username", username);
... etc ...
}
}
Wich of these two approach is right?
EDIT2
So, to summarize what I understood:
// BASE DAO INTERFACE
public interface DAOInterface<EntityClass, IDType extends Serializable> {
EntityClass get(IDType id);
EntityClass findById(IDType id);
EntityClass save(EntityClass entity);
EntityClass update(EntityClass entity);
void delete(EntityClass entity);
}
// AN HIBERNATE IMPLEMENTATION
public abstract class HibernateDAO<EntityClass, IDType extends Serializable> implements DAOInterface<EntityClass, IDType> {
#Autowired
private SessionFactory sessionFactory;
public void setSessionFactory(SessionFactory sessionFactory){
this.sessionFactory=sessionFactory;
}
public void getSessionFactory(){
return this.sessionFactory;
}
// Implements all DAOInterface method using sessionFactory
}
// PERSON DAO INTERFACE
public interface PersonDAO extends DAOInterface<Person, Long>{
Person findByName(String name, String surname);
List<Person> getInAgeRange(int year1, int year2);
}
// PERSON HIBERNATE DAO IMPLEMENTATION
public PersonHDAO extends HibernateDAO<Person, Long> implements PersonDAO{
// Implements the methods of PersonDAO interface using sessionFactory
}
#Service
public class PersonService {
//spring inject the correct DAO by its xml config(in this case PersonHDAO
#Autowired
private PersonDAO personDAO;
// spring manage the transaction
#Transactional
public List<Person> getInAgeRange(int year1, int year2){
return personDAO.getInAgeRange(year1, year2);
}
}
// NOW... HOW USE IT
//let's assume i have a button, pressing it a table will be populated with all persons in age range
private void actionPerfom(ActionEvent e){
List<Person> list=personService.getInAgeRange(age1Spinner.getValue(), age2Spinner.getValue());
//Load a table with list
}
Sorry for this wall of text, maybe can be useful for others i hope, im go in the right direction?
My service layer need an interface?
Is all corectly layered? I need a control layer too?
Thanks.
My suggestion:
for larger projects, use a dedicated, interface based DAO layer. Don't let your service layer know anything about the underlying persistence technology. Use Hibernate / JPA / JDBC / JDO / whatever only in the DAO layer.
for smaller projects it may be okay to have a service layer only (especially given the fact that both Hibernate Session and JPA EntityManager expose most standard DAO behavior out of the box.
Basic rule of thumb: if you're making a technology change, make sure you only need to change one layer of your application
Update: here's a sample DAO interface. Your service layer would only code against this interface, and the implementation would do the session / entityManager / jdbc calls without the service layer needing to know.
public interface CustomerDao extends CommonDao<Customer>{
Customer getCustomerByEmail(String emailAddress);
List<Customer> getCustomersWithinAgeRange(int lowerBound, int upperBound);
}
The key: in your service layer, specify your dependencies interface-based, i.e.
private CustomerDao customerDao;
public void setCustomerDao(CustomerDao customerDao){
this.customerDao = customerDao;
}
instead of
// this is horrible, it ties the service layer to implementation
// details of the dao layer
private HibernateCustomerDaoImpl customerDao;
public void setCustomerDao(HibernateCustomerDaoImpl customerDao){
this.customerDao = customerDao;
}
The DAO is the place for any database specific querying - JDBC or Hibernate in your case.
The service tier is meant for offering an API to consumers like a Presentation tier or others. There would be no reason to pollute the service tier with database specifics. Your service tier might have business logic which is fine, but it should not be aware of the underlying DB implementation IMO