I use spring-data-jpa to access my data. I need a way to detach an Object and store it as a new database row. My approach is currently to add a detach method to the repository, but for that, I need a EntityManager. And I haven't found a (nice) way of obtaining it... Any ideas?
#Repository
public interface InteractionRepository
extends JpaRepository<Interaction, Long>,
DetatchableItemRepository{}
public interface DetatchableItemRepository {
void detach(Interaction clone);
}
public class DetatchableItemRepositoryImpl implements DetatchableItemRepository {
#Autowired
EntityManager em;
public void detach(Interaction clone) {
em.detach(clone);
clone.id=null;
em.persist(clone);
}
}
However, spring dies with this error:
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'interactionRepository': Invocation of init method failed; nested exception is org.springframework.data.mapping.PropertyReferenceException: No property detach found for type Interaction!
Caused by: org.springframework.data.mapping.PropertyReferenceException: No property detach found for type Interaction!
you use wrong name convention for custom repository , try this :
public interface DetatchableItemRepositoryCustom {
void detach(Interaction clone);
}
public interface DetatchableItemRepository extends JpaRepository<Interaction, Long>,
DetatchableItemRepositoryCustom {
}
public class DetatchableItemRepositoryImpl implements DetatchableItemRepositoryCustom {
}
spring data use name convention for custom repository and main repository.(see about name Adding custom behavior to single repositories)
If you have some SomeRepository , that extends some base spring data repository , and want to add custom behavior then it should be like :
interface SomeRepositoryCustom{
someMethod();
}
//XXXRepository - any base spring data repository
interface SomeRepository extends<T ,ID> extend XXXRepository , SomeRepositoryCustom {
.......
}
public class ARepositoryImpl implement SomeRepositoryCustom{
#Overide
someMethod(){
....
}
Related
I try to extends SimpleJpaRepository and I have got an error :
[ERROR] constructor org.springframework.data.jpa.repository.support.SimpleJpaRepository.SimpleJpaRepository(org.springframework.data.jpa.repository.support.JpaEntityInformation<com.bnpparibas.dsibddf.ap00437.successione2e.domain.entity.gedeo.Contrat,?>,javax.persistence.EntityManager) is not applicable
[ERROR] (actual and formal argument lists differ in length)
[ERROR] constructor org.springframework.data.jpa.repository.support.SimpleJpaRepository.SimpleJpaRepository(java.lang.Class<com.bnpparibas.dsibddf.ap00437.successione2e.domain.entity.gedeo.Contrat>,javax.persistence.EntityManager) is not applicable
[ERROR] (actual and formal argument lists differ in length)
I tried the #enableJpaRepositories solution like described here :
Extend SimpleJpaRepository
But it does not work.
I put the annotation in the repository class.
Is there something else I can do?
#Configuration
#EnableJpaRepositories(repositoryBaseClass = ContratRepository.class , basePackages = {"com.bnpparibas.dsibddf.ap00437.successione2e.infrastructure.repository"})
#Repository
public class ContratRepository extends SimpleJpaRepository<Contrat,Integer> implements IContratRepository {
#Override
public Contrat save(Contrat contrat) {
return null;
}
}
if I had as suggested by eclipse the constructor 2:
public ContratRepository(Class<Contrat> domainClass, EntityManager em) {
super(domainClass, em);
}
Contrat class
package com.bnpparibas.dsibddf.ap00437.successione2e.domain.entity.gedeo;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
#Entity
public class Contrat {
#Id
#GeneratedValue(strategy = GenerationType. IDENTITY )
private String idContratTech;
private String contractExternalId;
private String code;
private String libelle;
private String contractStatus;
public String getIdContratTech() {
return idContratTech;
}
public void setIdContratTech(String idContratTech) {
this.idContratTech = idContratTech;
}
public String getContractExternalId() {
return contractExternalId;
}
public void setContractExternalId(String contractExternalId) {
this.contractExternalId = contractExternalId;
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
public String getLibelle() {
return libelle;
}
public void setLibelle(String libelle) {
this.libelle = libelle;
}
public String getContractStatus() {
return contractStatus;
}
public void setContractStatus(String contractStatus) {
this.contractStatus = contractStatus;
}
public String getNature() {
return nature;
}
public void setNature(String nature) {
this.nature = nature;
}
private String nature;
public void foo() {
}
}
I have got the error:
NoSuchBeanDefinitionException: No qualifying bean of type 'java.lang.Class<com.bnpparibas.dsibddf.ap00437.successione2e.domain.entity.gedeo.Contrat>' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {}
that I don't understand
thanks for your help
First of all, You should not put #Repository and #Configuration on the same class.
Secondly, if you are to extend a SimpleJpaRepository, you need to create matching constructors and call the parent constructors.
Thirdly, I would like to note that customizing a SimpleJpaRepository is NOT ADVISED FOR A SINGLE ENTITY (e.g. Contrat). In that case, use a regular repository implementation! If you do customize your SimpleJpaRepository to be only compatible with a single entity, you will not be able to create other repositories extending SimpleJpaRepository!
1. About beans and configuration
In Spring, a Repository is a type of bean. This means that it this class can be automatically instantiated by the Spring framework when you need an instance of it (ever noticed that you never need to do new MyRepository()?). This is done through a process called autowiring (the general principle of this is called Dependency Injection. You can read more about beans and autowiring in spring here. Other typical types of beans are #Component, #Controller and #Service.
A Configuration class on the other hand is a whole different kind of beast. Configuration classes are used to provide logic about how certain beans are to be created.
An example could for instance be that you want to configure a bean that holds a database connection, and in order to create that bean you need variables that are read from the environment (Environment Variables). In that example the configuration class would be the one reading out the environment variables and creating the database connection bean through a #Bean method.
In essence configuration classes are thus used to instantiate beans that either 1) are not able to be instantiated automatically or 2) need special bean configuration.
2. About matching constructors
Spring needs to be able to instantiate the parent class SimpleJpaRepository. Therefore, you should assure that you implement matching constructors.
Furthermore, it is a general best practice to split your repository in a Repository and a RepositoryImpl.
So for the constructor matching, this gives:
CustomSimpleRepositoryImpl
public class CustomSimpleRepositoryImpl(<T, ID extends Serializable>
extends SimpleJpaRepository<T, ID> extends CustomSimpleRepository<T, ID> {
private EntityManager em;
private JpaEntityInformation<T, ?> ei;
protected CustomSimpleRepositoryImpl(#NonNull Class<T> entityClass, EntityManager em) {
super(domainClass, em);
this.em = em;
this.ei= JpaEntityInformationSupport.getEntityInformation(entityClass, em);
}
public CustomSimpleRepositoryImpl(JpaEntityInformation<T, ?> entityInformation, EntityManager entityManager) {
super(entityInformation, entityManager);
this.em = entityManager;
this.ei = entityInformation;
}
/* Whatever you want to put in your repository */
}
For the split:
CustomSimpleRepository
#NoRepositoryBean
public interface CustomJpaRepository<T, ID extends Serializable> extends JpaRepository<T, ID> {
/* Whatever you want to put in your repository */
}
3. Customizing repositories
Note that in the above example I did not use #Repository whatsoever. Instead I used #NoRepositoryBean. This is because in my example I implemented a generic SimpleJpaRepository replacement. This repository is not to be instantiated on its own!
3.1 A plain, custom repository
If you rather want to customize a regular Repository, please only use an interface CustomRepository and an implementation called CustomRepositoryImpl.
Example implementations can be found in 3.2.
3.2 A custom repository extending SimpleJpaRepository
This is a difficult one: you cannot implement an interface without providing all of its unimplemented methods. So if you really want a repository where you can have both:
JPA methods, e.g. findById, findByFieldContainsText(String text)...
Custom methods, e.g. findAllCountsGroupByParentId(), ...
It is not possible by creating one single repository interface or class. Instead, you need to create a custom repository where you offload your "custom methods" (2) and a repository that extends SimpleJpaRepository where you define your "JPA methods" (1):
CustomJpaRepository.java
#Repository
public interface CustomJpaRepository
extends JpaRepository<CustomEntity, Long>
extends CustomRepository {
/* Your desired JPA methods, e.g. */
// findByNameContainsText(String text);
}
CustomRepository.java
public interface CustomRepository {
/* Your desired custom methods, e.g. */
// findAllCountsWhereContractFinishedGroupByBusinessUnitId();
}
CustomRepositoryImpl.java
#Repository
public class CustomRepositoryImpl {
protected EntityManager em;
CustomRepositoryImpl(final EntityManager em) {
this.em = em;
}
/* Your desired custom methods, e.g. */
// public findAllCountsWhereContractFinishedGroupByBusinessUnitId() {
// ...
// }
}
Since your CustomJpaRepository extends JpaRepository, you can use the JPA methods.
Since it also extends CustomRepository, it inherits the custom methods.
Result: you can call both kinds of methods (custom and jpa) combined in CustomJpaRepository.
Let's say that i have two Classes: Subject and Client, Subject is base-class.
#Entity
public class Client extends Subject
Now i want to add customized Jpa base interface, so methods will be accessible in subinterfaces:
#NoRepositoryBean
public interface SubjectRepository <T extends Subject> extends
JpaRepository<T, Long>, CustomSubjectRepository<T> {}
CustomSubjectRepository looks like:
public interface CustomSubjectRepository<T extends Subject> {
void saveEncrypted(T subject);
}
I need implementation so i declare class:
#Repository
#Transactional
public class CustomSubjectRepositoryImpl<T extends Subject> implements
CustomSubjectRepository<T> {
#PersistenceContext
private EntityManager entityManager;
#Override
public void saveEncrypted(T subject) {
//implementation
}
}
Then wanted to create ClientRepository and inherit from SubjectRepository to have access to saveEncrypted method.
#Repository
public interface ClientRepository extends SubjectRepository<Client> {
}
But when it comes to compile i get:
Error creating bean with name 'clientRepository': Invocation of init method failed; nested exception is java.lang.IllegalArgumentException: Failed to create query for method public abstract void com.path.repositories.CustomSubjectRepository.saveEncrypted(com.path.models.Subject)! No property saveEncrypted found for type Client!
You are extending the interface, this way Spring will try to create a query named saveEncrypted instead of using the customized method.
I believe the best solution is to extend the class CustomSubjectRepositoryImpl.
#Repository
public class ClientRepository extends CustomSubjectRepositoryImpl<Client> {
}
I have an Interface name
public interface ScoreDao {
public int storeScore(OverallScore overallScore);
public void storeIndividualScore(ScoreTO scoreTO);
}
The implementation class is like below
#Repository("scoreDao")
public class ScoreDaoImpl implements ScoreDao {
#Override
public int storeScore(OverallScore overallScore) {
//Implementation
}
#Override
public void storeIndividualScore(ScoreTO scoreTO){
//Implementation
}
}
The caller is using the service like below
#Service("scoreService")
public class scoreServiceImpl implements IScoreService {
#Autowired
private ScoreDao scoreDao;
#Override
public int storeScore(OverallScore overallScore) {
return scoreDao.storeOverallScore(overallScore);
}
#Override
public void storeIndividualScore(ScoreTO scoreTO) {
scoreDao.storeIndividualScore(scoreTO);
}
}
I'm using spring 4.x, while deploying I'm getting bean conflict error as below.
Caused by: java.lang.RuntimeException:
org.springframework.context.annotation.ConflictingBeanDefinitionException:
Annotation-specified bean name 'ScoreDao' for bean class [ScoreDao]
conflicts with existing, non-compatible bean definition of same name
and class [ScoreDaoImpl]
When I change the interface name to IScoreDao, it is working. Is it because of service name #Repository("scoreDao") same as Interface name?
The simple answer is Yes,it was due to that you have an interface called ScoreDao and you make the implementation of it as #Repository("scoreDao")
two ways to solve it:
Rename ScoreDao to other name
Change #Repository("scoreDao") to #Repository so that it will use the default name
there is one another solution mention #Repository on your interface so that you don't need to mention on your implementation class because you implements that interface. so it creates the bean automatically with the interface name.
I am using Spring 3 and Hibernate 4
I have the following class structure
public interface GenericDAO<T> {
public void create(T entity);
public void update(T entity);
public void delete(T entity);
}
DAO class
public interface EmployeeDAO extends GenericDAO<Employee> {
public void findEmployee(EmployeeQueryData data);
}
DAO Implementation class
#Repository("employeeDAO")
public abstract class EmployeeDAOImpl implements EmployeeDAO {
protected EntityManager entityManager;
#Override
public void findEmployee(EmployeeQueryData data) {
...... code
}
The problem I am facing is when I try to deploy, I am getting the following exception.
If I remove abstract from EmployeeDAOImpl and remove extends GenericDAO<Employee> from EmployeeDAO then application gets deployed without errors. So it is not possible to have abstract class for EmployeeDAOImpl or I have need to implement all methods of GenericDAO in DAO implementation without abstract?
Error creating bean with
name 'employeeService': Injection of autowired dependencies failed; \
nested exception is org.springframework.beans.factory.BeanCreationException:
Could not autowire field: test.dao.EmployeeDAO
test.service.EmployeeServiceImpl.employeeDAO; nested exception is
org.springframework.beans.factory.NoSuchBeanDefinitionException:
No matching bean of type [test.dao.EmployeeDAO] found for dependency:
expected at least 1 bean which qualifies as autowire candidate for
this dependency. Dependency annotations:
{#javax.inject.Inject()}.
Edit 1
GenericDAOImpl
public class GenericDAOImpl<T> implements GenericDAO<T> {
public void create(T entity) {
}
public void update(T entity) {
}
public void delete(T entity) {
}
EmployeeDAOImpl
public class EmployeeDAOImpl extends GenericDAOImpl<Employee> implements EmployeeDAO {
Java (and consequently Spring) cannot create instances of abstract classes: every method must have an implementation before Java will let you create an instance, otherwise you would get a runtime error when you tried to call the method. You need to remove "abstract" from EmployeeDAOImpl and implement the methods inherited from GenericDAO.
Why do you want to declare a class implementation as abstract? Conceptually it's a contradiction. Obviously Spring cannot instantiate it and fails.
Confirm if your EmployeeDAOImpl or other annotated class packages are mentioned in spring context xml in following tag. Unless this is done, annotations won't get read and will not be initialized.
<context:component-scan base-package="com.app.service" />
I use Spring data jpa and I am trying to add custom behaviour to all repositories as described here:
http://static.springsource.org/spring-data/data-jpa/docs/current/reference/html/#repositories.custom-behaviour-for-all-repositories
I encountered several issues:
-First, there is no such method as getDomainClass in the RepositoryMetadata class as described in the Spring documentation (see below):
protected Object getTargetRepository(RepositoryMetadata metadata) {
return new MyRepositoryImpl<T, I>((Class<T>) metadata.getDomainClass(), entityManager);
}
I used the following method instead: getDomainType() Is this right?
-Second my application throws exceptions when tomcat starts. Here is the full stack trace:
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'globalRepositoryImpl' defined in file [E:\users\jumartin\dev_sts\.metadata\.plugins\org.eclipse.wst.server.core\
tmp0\wtpwebapps\SuiviTRC\WEB-INF\classes\trc\suivi\repository\GlobalRepositoryImpl.class]: Instantiation of bean failed; nested exception is org.springframework.beans.BeanInstantiationException: Could
not instantiate bean class [trc.suivi.repository.GlobalRepositoryImpl]: No default constructor found; nested exception is java.lang.NoSuchMethodException: trc.suivi.repository.GlobalRepositoryImpl.<i
nit>()
Here is my custom global repository code:
public class GlobalRepositoryImpl<T, ID extends Serializable> extends SimpleJpaRepository<T, ID> implements GlobalRepository<T, ID> {
private EntityManager em;
public GlobalRepositoryImpl(Class<T> domainClass, EntityManager em) {
super(domainClass, em);
this.em = em;
}
public void sharedCustomMethod(ID id) {
}
}
Here is my xml config:
<repositories base-package="trc.suivi.repository" factory-class="trc.suivi.repository.GlobalRepositoryFactoryBean">
<repository id="pliRepository" />
<repository id="globalRepository" />
</repositories>
I was not able to find any other sample on the web. Can anyone please help?
I have made a full example of how to add custom behaviour to all repositories.
http://borislam.blogspot.hk/2012/07/customizing-spring-data-jpa-repository.html
You could add features of different JPA implementation (e.g. hibernate, openJPA) to your base repository. I have made another tutorial on that.
http://borislam.blogspot.hk/2012/07/adding-hibernate-native-sql-features.html
I finally got some help and was able to get my repository to work by using the #NoRepositoryBean annotation on the intermediate interface.
Further info is available here: http://forum.springsource.org/showthread.php?128536-Several-issues-with-quot-adding-custom-behaviour-to-all-repositories-quot-in-spring-data-jpa
As of Spring Data JPA 1.9.M1 it's become easier to add custom methods to all repositories managed by Spring Data. This example has all the details.
In your case the example would look like:
1) Configuration
#Configuration
#EnableAutoConfiguration
#EnableJpaRepositories(repositoryBaseClass = GlobalRepositoryImpl.class)
class CustomRepositoryConfig {}
2) Custom base repository:
public class GlobalRepositoryImpl<T, ID extends Serializable> extends SimpleJpaRepository<T, ID> implements GlobalRepository<ID> {
public GlobalRepositoryImpl(JpaEntityInformation<T, ?> entityInformation, EntityManager entityManager) {
super(entityInformation, entityManager);
}
#Override
public void sharedCustomMethod(ID id) {
}
}
3) Some repository:
public interface SomeRepository extends GlobalRepository<User, Long> {}
Of course GlobalRepository still needs to be annotated with #NoRepositoryBean