Using Couchbase Enterprise Edition 5.0.0 build 2873 and Spring Data Couchbase 2.1.2, I am getting the error explained at the bottom of the question. Go forward if you just need the short story.
If you want a little more explanation, here it comes:
Imagine that the CrudRepository methods are fine to me. I mean, I don't need to add any more methods.
How the repository would look like? Would I declare an empty repository just like this?
#Repository
public interface PersonRepository extends CrudRepository<Person, String> {
// Empty
}
Or would I use directly CrudRepositoy as my Base repository. I don't think so, because you need to pass Type and ID to the CrudRepository.
But the question is not that. Here comes the question:
How Spring knows how to instantiate PersonRepository, taking into account that there is no implementation of that base repository? Take a look to PersonService and PersonServiceImpl interface/implementation.
Interface:
#Service
public interface PersonService {
Person findOne (String id);
List<Person> findAll();
//...
}
Implementation:
public class PersonServiceImpl implements PersonService {
// This is the variable for the repository
#Autowired
private PersonRepository personRepository;
public Person findOne(String id){
return personRepository(id);
}
public List<Person> findAll(){
List<Hero> personList = new ArrayList<>();
Iterator<Person> it = personRepository.iterator();
while (it.hasNext()){
personList.add(it.next());
}
return personList;
}
//...
}
Is it really enough with declaring an empty PersonRepository extending from CrudRepository? No need to implement and say anything about each method of CrudRepository?. At least something to tell Spring about some constructor...
This doubt are all because I am getting this error when Spring tries to inject the personRepository variable:
Error creating bean with name 'personRepository': Could not resolve matching constructor (hint: specify index/type/name arguments for simple parameters to avoid type ambiguities).
Apparently, it is asking to have some class defining at least the constructor of the implementation. How do I tell Spring to avoid those type ambiguities mentioned in the error?
As for the repository you'll need to extend CouchbaseRepository if you're strictly working with Couchbase, by that your repository will expose lower level couchbase objects/functionality.
For example
public interface PersonRepository extends CouchbaseRepository<Person, String> {
}
For your service, you don't need to define findOne() and findAll(), those are strictly the responsibility of the repository.
For example
public interface PersonService {
void doOperationOnPerson(String personId);
}
#Service
public class PersonServiceImpl implements PersonService {
#Autowired
PersonRepository personRepo;
#Override
void doOperationOnPerson(String personId) {
Person person = personRepo.findById(personId);
//do operation
}
}
Note that the #Service annotation goes on the implementation. (It actually should work either way but I think having the annotation on the implementation is more proper)
If you need to define custom queries then it should be done inside the repository itself.
You also might need to define an empty constructor on your Person class if you have a non-default constructor.
I suggest you to read more about Spring Data.
Related
This is my project structure
MODEL
TodoItem.java - this is an interface
TodoType1 - this implements the interface
TodoType2 - this implements the interface
Repo
TodoRepo.java - extends JPA repo with <TodoItem, Integer>
Controller (uses the TodoRepo for CRUD operations)
request 1 - needs to work with todotype1
request 2 - needs to work with todotype2
I am a bit confused, how should i go about using qualifiers here? Should I create different Repositories for each type?
TodoRepo.java - extends JPA repo with <TodoItem, Integer>
Here TodoItem is an interface. Springboot JPA gets confused about which entity it is going to handle(two class implements the TodItem interface). Instead of Interface, declaring a specified entity class won't throw the error.
I think you need to create two different repositories. And then you can use the #Autowired annotation to inject the desired bean into your controller.
This will inject the appropriate repository implementation (TodoType1Repo or TodoType2Repo) into your controller based on the value of the #Qualifier annotation.
more about #Qualifier https://www.baeldung.com/spring-qualifier-annotation
#Qualifier("todoType1Repo")
#Repository
public class TodoType1Repo extends JpaRepository<TodoType1, Integer> {}
#Qualifier("todoType2Repo")
#Repository
public class TodoType2Repo extends JpaRepository<TodoType2, Integer> {}
#Autowired
#Qualifier("todoType1Repo")
private TodoRepo todoType1Repo;
#Autowired
#Qualifier("todoType2Repo")
private TodoRepo todoType2Repo;
public void handleRequest1() {
// Use todoType1Repo to perform CRUD operations on TodoType1 objects
}
public void handleRequest2() {
// Use todoType2Repo to perform CRUD operations on TodoType2 objects
}
I am writing repository for variables table and wish to access specific rows with it. For this I am trying to autowire main repository into custom implementation, like this:
public interface VariableRepo extends CrudRepository<Variable, Long>, VariableRepoCustom {
Variable getByName(String name);
}
public interface VariableRepoCustom {
...
Variable getPopulationSingle();
...
}
public class VariableRepoCustomImpl implements VariableRepoCustom {
private final VariableRepo variableRepo;
#Autowired
public VariableRepoCustomImpl(VariableRepo variableRepo) {
this.variableRepo = variableRepo;
}
#Override
public Variable getPopulationSingle() {
return getByName("Population single");
}
...
}
Unfortunately, Spring like to go crazy with this, throwing an exception:
Error creating bean with name 'variableRepo': Invocation of init
method failed; nested exception is
org.springframework.data.mapping.PropertyReferenceException: No
property getPopulationSingle found for type Variable!
I.e. it tries to find repository method inside entity class (Variable) which of course should not contained inside.
How to solve this?
What you named VariableRepoCustom/VariableRepoCustomImpl should be in fact a service (not a repository in terms of spring-data-jpa).
The interface VariableRepoCustom should not be present in JPA searchpath, to prevent JPA to generate an implementation
I think might be your repository impl does not have #Repository annotation. Please make both repository and impl as #Repository. it should fix your problem.
For best practice, No need to autowire VariableRepo interface into impl class.
Edit:
Also remove custom from impl. The name will be RepoName by append impl. it is definition like VariableRepoImpl
So I was following the tutorial here: https://spring.io/guides/gs/accessing-data-jpa/
and it works fine, I'm trying to implement it in my application (Because it makes JPA so easy to use), but I'm confused.
Where it has
#Bean
public CommandLineRunner demo(CustomerRepository repository)
and then it acts on the repository
repository.save(new Customer("Jack", "Bauer"));
repository.save(new Customer("Chloe", "O'Brian"));
How can it act on an interface? CustomerRepository is an interface, and I can't instantiate it
CustomerRepository c = new CustomerRepository()
cant be done, and I don't have any classes that implement the interface. I just wanted to do something like
CustomerRepository c = new CustomerRepository()
c.save(new Customer("whatever", "whatever")
but I can only use it in the CommandLineRunner bean method. Why can I do this with commandlinerunner but cant do it with another class?
I was thinking I could just make a class that extends CustomerRepository, but then I read that the interface actually does all the method implementation itself (JPA does it) so you don't have to worry about it.
public interface CustomerRepository extends CrudRepository<Customer, Long> {
List<Customer> findByLastName(String lastName);
}
so if I extended it, wouldn't I have to override the findbylastname() method, meaning JPA wouldn't do it itself?
Thanks for any assistance.
so if I extended it, wouldn't I have to override the findbylastname()
method, meaning JPA wouldn't do it itself?
No, it is not JPA which does the job but Spring which generates by APO some JPA processings.
With Spring Repository, you have multiples ways of doing :
write your own SQL/JPQL query.
use naming convention in the method name to write the query
In both cases, you don't need to implement directly the interface you declare here :
public interface CustomerRepository extends CrudRepository<Customer, Long> {
List<Customer> findByLastName(String lastName);
}
Because as you understood, the job is performed for you by Spring.
For example, in the case you quote, you use naming convention in the method name to write the query.
When Spring inspects your interface and sees the method findByLastName(String lastName), it associates the method name to a query which does a find with a match by lastName field. So, it generate a JPQL query like that :
select c from Customer c where c.lastName=%lastName
and it sets the lastName param with the effective parameter used when the method is call.
I extended it, wouldn't I have to override the findbylastname()
method, meaning JPA wouldn't do it itself ?
No, you don't need to implement the methods as spring-data-jpa will take care of it, you can look here on how Spring data repository interfaces are actually implemented by proxy at runtime.
The main point that you need to remember is that spring data has got few rules to derive the sql queries from the method names (like findByLastName(), findByLastnameOrderByFirstnameAsc(), etc..), you can look here to understand how method names work and they are related to field names of your entity bean.
If you wanted to write some complex queries which can't be derived from method names you can use #Query for your methods.
If I made a class public class Cust implements CustomerRepository what
would I do when it asks me I have to implement the
findByLastName(String lastName); method that JPA is supposed to take
care of ?
If you wanted to implement repository to provide your custom behaviour for few of the methods for few of your methods, you can do that (like class Cust implements CustomerRepository), you can refer the section "Using JpaContext in custom implementations", it is well explained in the ref doc.
I am confused. I could not find out, how to define together custom "search" methods with methods that were loaded with help of spring-data-rest.
Could you answer me, does the framework has this possibility "out-of-box"?
And if has, could you tell me, where can i find it?
For a deeper understanding my situation i describe my issue:
class UserService {
public String getListOfWaitingUsers() {
return userRepository.findAll(UserSpecification.isWaiting());
}
}
public interface UserRepository extends PagingAndSortingRepository<User, Long>{
Page<User> findByNameLike(#Param("name") String name, Pageable pageable);
}
I want that it to be like:
/user/search/
findByNameLike
findWaitingUsers
How to implement that my methods of specifications or services (there is not method in repository) will define with path "/resource_name/search/METHOD_NAME" ( methods of repository + ( METHODS SERVICES OR SPECIFICATIONS)
Spring Data REST framework is based on Spring Data Respository, so your service class can be ignored here.
All methods that are not part of CRUD/Paging Repository as exposed as "search" methods provided you annotated all parameters with #Param annotation. So in your case, you need to implement your method following the conventions outline in Spring Data commons docs. So once you have implementation for findByNameLike method, the method would be exposed as ../search/findByNameLike URL. If needed, you could customize the rel and path with #RestResource annotation.
Also note your UserRepository should ideally be working only on User object and hence the methods you defined. In your case, UserRepository is returning Process/ProcessContext objects? Instead it should be like below
public interface UserRepository extends PagingAndSortingRepository<User, Long>{
Page<User> findByNameLike(#Param("name") String name, Pageable pageable);
}
So lets say we have a couple of entities we want to persist using DAO objects. So we implement the right interface so we end up with
class JdbcUserDao implements UserDao{
//...
}
class JdbcAddressDao implements AddressDao{
//...
}
So if I want to be able to switch persistance implementations from JDBC to JPA (for example) and vice versa, I'd need to have JPAUserDao and JPAAddressDao... Meaning if I had 20 entities, and decided to switch implementations(using DI container), I'd have to switch every Jdbc implementation with JPA in code.
Now it could be that I misunderstood how DAO works, but... If I just had
class JdbcDaoImpl implements UserDao,AddressDao{
//...
}
I'd then have all the JDBC implementations in one class, and switching implementations would be a piece of cake. Also, DaoImpl count is equal to number of Dao interfaces. Why not just group them by implementation (jdbc, JTA, JPA...) and have everything under one class?
Thanks in advance.
Having a single class implement every DAO interface in your entire application would be a rather bad design.
A more typical pattern is to have a BaseDAO interface (also often called GenericDAO) and have a JPABaseDAO, JDBCBaseDAO etc. These base classes will contain methods like find/get/read, save/store/persist, update/modify and delete/remove/purge.
Specific DAO interfaces like UserDAO then inherit from BaseDAO and concrete implementations like JPAUserDAO extends from JPABaseDAO.
A BaseDAO interface could look like this:
public interface BaseDAO <T> {
T getByID(Long ID);
T save(T type);
T update(T type);
void delete(T type);
}
And a UserDAO interface:
public interface UserDAO extends BaseDAO<User> {
List<User> getAllAuthorized();
}
Bare bones example of a JPABaseDAO implementing this interface:
#Stateless
public class JPABaseDAO<T> implements BaseDAO<T> {
#PersistenceContext
private EntityManager entityManager;
private final Class<T> entityType;
#SuppressWarnings("unchecked")
public JPABaseDAO() {
this.entityType = ((Class<T>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0]);
}
#Override
public T getByID(Long ID) {
return entityManager.find(entityType, ID);
}
#Override
public T save(T type) {
return entityManager.persist(type);
}
#Override
public T update(T type) {
return entityManager.merge(type);
}
#Override
public void delete(T type) {
entityManager.remove(entityManager.contains(type) ? type : entityManager.merge(type));
}
}
And some sample UserDAO implementation that would inherit from it:
#Stateless
public class JPAUserDAO extends JPABaseDAO<User> implements UserDAO {
#PersistenceContext
private EntityManager entityManager;
#Override
public List<User> getAllAuthorized() {
return entityManager.createNamedQuery("User.getAllAuthorized", User.class)
.getResultList();
}
}
In practice the base class can often do some other things transparently, for instance checking if an entity implements some kind of Auditable interface, and automatically setting the date and user that modified it, etc.
When using EJB to implement your DAOs, one strategy to change implementations would be to put all JDBC implementations in one package and all JPA implementations in the other. Then just include only one implementation package in your build.
The whole point of Dependency Injection is to make switching between implementation easier and to decouple the user from the provider. Hence all DI frameworks provide some way to "group" several implementations (here your JDBC-group and your JPA-group) and switch them in one place.
Also: Usually the number of consumers (in your case: some business logic working on users and addresses) is usually higher than the number of DAOs the DI framework will uncouple most of the stuff for you anyway. Assume: 50 business beans, two interfaces and two implementations for each interface (4 total): even basic DI will take care for the 50. Using grouping will halve that remaining rest for you.
There are definitely possibilities to implement the DAO pattern in a widely technology agnostic way such that switching persistence technology or even mixing multiple technologies becomes feasible. This article presents one implementation scheme including source code on github.
http://codeblock.engio.net/?p=180