How to make a custom method in Spring Data JPA - java

Currently, I'm learning about Spring Data JPA, and I found that I need to create a custom interface to make a custom methods. I wonder if someone can explain to me why this is so. Let's say I have entity User with fields: userId and name. Also, I have UserRepository:
public interface UserRepository extends JpaRepository<User, Long> {...}
I found that I need to make new interface - UserRepositoryCustom where I should make custom abstract methods. Also, after that, I found that I need to make class UserRepositoryImpl which implements UserRepositoryCustom. Let's take a look at the code I made:
#Repository
public class UserRepositoryImpl implements UserRepositoryCustom{
#PersistenceContext
private EntityManager entityManager;
#Override
public List<User> findUserByName(String name) {
Query query = entityManager.createNativeQuery("SELECT em.* FROM User em " +
"WHERE em.name = ?1", User.class);
query.setParameter(1, name);
return query.getResultList();
}
}
Can you please explain why I need UserRepositoryCustom and UserRepositoryImpl in foreground, And why do I need EntityManager and #PersistenceContext above it? Every piece of information is of great importance to me, because that way I will be able to extract what is most important for me, which is to understand.
I found this code, which is working fine, online, but I need to understand it.

You don't need to do this. The direct equivalent would be a method in your UserRepository interface defined as List<User> findByName(String name). You can also use JPA named queries, the #Query annotation, etc.
The usage of additional interfaces and implementation classes is intended for advanced use cases that are not (easily) possible in the queries Spring Data JPA generates for you based on method names, annotations, etc. It is not intended for easy use cases like the query you show.
And the reason why, is because that is how Spring Data JPA is designed. I highly recommend you read the full Spring Data JPA documentation. You'll notice that the solution in your question is just a minor part of the documentation, which makes clear that this is an escape hatch, not the primary way of using Spring Data JPA.

Related

Fetch data by id AND String from single database

I am totally new to Hibernate, Spring and postgres database world. There may be difference in "terms" using to describe.
So issue is, i want to fetch a data where it matches given "id" AND "string" using hibernate, JPA annotations in spring boot.
I know basic like repository.findOne(id) but i am not aware of how to get data with two parameters.
I guess i need something like, i am not sure as i am new to db world
SELECT *
FROM student
WHERE type='commerce'
AND id='1'"
I thank you in advance and tutorials to study further is most welcome.
Spring boot provides for you some automatic repositories, so you just create a interface that extends "JpaRepository" and then, create methods with a natural language.
Something like:
#Repository
public interface StudentRepository extends JpaRepository<Student, Long> {
public List<Student> findByIdAndType(Long id, String type);
}
Later on, let's say you want to use this class:
#Autowired
private StudentRepository studentRepository;
public void doSomething() {
List<Student> students = studentRepository.findByIdAndType(1, "commerce");
}
And no, there is no need to provide an implementation to the interface "StudentRepository", as spring data will provide it to you in behind the scenes.
More information on how this works, you can find on proper spring-data documentarion
Cheers, Nikolas

Spring data repositories - parameter what to retrieve all records?

#Repository
public interface UserDao extends User {
public List<User> findByFirstname(String firstname);
}
How could I use above code to retrieve all records?
I tried findByFistname(null);, it doesn't work...
I don't want to use findByFirstname(); because it's possible to have parameter.
Hope you all understand.
Have you considered using a spring data specification? In spring data a specification is a way to wrap the JPA criteria api.
The idea behind the JPA Criteria api is to generate queries programatically, by defining query objects.
Once you have encapsulated the criteria in a specification objects, a single findAll method can be used in a number of scenarios. For example programatically add criteria based input form the user, such as additional search filters etc.
To use this feature a repo will need to extend "JpaSpecificationExecutor"
public interface UserRepository extends JpaRepository<User>, JpaSpecificationExecutor {
List<T> findAll(Specification<T> spec);
}
The find method can then be called with multiple criteria, or the criteria can be built dynamically based on the situation:
List<User> users = userRepository.findAll(where(userLastNameIs("John")).and(userIsArchived()));
Alternatively you can also try query by exampe. The idea here is to provide the actual domain object with the populated search fields to an example matcher. Configure the example matcher to control the search and pass it to the findAll method.
Again the repo will need to implement an interface. Check the documentation for the detailed pros/cons of each approach.
Person person = new Person();
person.setFirstname("Dave");
ExampleMatcher matcher = ExampleMatcher.matching()
.withIgnorePaths("lastname")
.withIncludeNullValues()
.withStringMatcherEnding();
Example<Person> example = Example.of(person, matcher);
List<Person> people = personRepository.findAll(example);
You should extend your repository from JpaRepository. Be careful with name of repository (It should follow convention). After you inject your UserRepository bean you will have already implemeted by spring data crud methods like findOne(), findAll(), delete() etc.
#Repository
public interface UserRepository extends JpaRepository<User, Long> {
//assume your primary key is Long type
}
Also will be useful documentation
As I got from comments you're trying to achieve ignorance of null values of passed parameters (instead of retrieving all records by findAll()).
Unfortunately, currently, it's not supported by Spring .
You could leverage the #Query annotation and write the query manually in such manner:
#Query("select u from User u where "
+ "(:firstname is null or u.firstname = :firstname)"
+ "(:lastname is null or u.lastname = :lastname)"
)
public List<User> findUserByFirstNameAndLastName(
#Param("firstname") String firstname,
#Param("lastname") String lastname
);
https://spring.io/blog/2011/02/10/getting-started-with-spring-data-jpa/
This is very good tutorial of Spring Data. I suggest you to start with it. tutorial of Spring Data. If you want to go deeper you can read the documentation.
http://docs.spring.io/spring-data/data-commons/docs/1.6.1.RELEASE/reference/html/repositories.html

How to create generic DAO that delivers crud operation without the need to extend it by custom DAO that doesn't deliver others methods

I've generic DAO:
#NoRepositoryBean
interface ICrudDao<T, ID extends Serializable> extends Repository<T, ID> {
void delete(T deleted);
List<T> findAll();
T findOne(ID id);
T save(T persisted);
}
To allow services to work on that I have to create interface that allows custom entities get persistence, f.e.:
interface TodoDao extends ICrudDao<Todo, Long> {
}
I've a lot of daos like TodoDao. Then don't deliver any special methods.
Creating a lot of empty interfaces seems a dumb idea. How can create a Generic one?
Edit:
I don't think what you are trying to do is a good idea. At first to register a repository for each Entity seems like boiler plate code, but as the application grows, it will help you to maintain it. Imagine your application to evolve over time like this:
You create a simple entity Person and the Interface PersonRepository. Luckily all basic CRUD operations are included, so far it fits your needs so there is nothing else to do.
As your application grows, Person gets a lot of associated relations, like Address, Job, Hobbies and it would be very inefficient to fetch all associated data everytime you access it, because not always every association is needed. To encounter that, you create your own method in PersonRepository which executes your own NamedQuery to only load certain fields and store it in your DTO needed for the specific view ("SELECT new package.PersonDto(x,y) FROM PERSON WHERE ...).
As time passes by, you find yourself in a situation where you need queries to get executed in dynamic fashion, like pagination or restrictions that only need to be added on certain conditions. So you create a new interface PersonCustomRepository and PersonCustomRepositoryImpl where you write queries in a programatic way:
#PersistenceContext
private EntityManager entityManager;
#Transactional
public List<Person> foo() {
// example for accessing hibernate directly, you could also use QueryDSL and so on
Criteria basicCriteria = entityManager.unwrap().createCriteria(Person.class);
if (someCondition) {
criteria.add(Restrictions.eq("foo", foo));
...
}
...
return criteria.list();
}
Bottom line: Spring data repositories already do a lot of work for you and they are easy to extend, don't try to fight your framework, even it maybe saves you some clicks in the first place.
You can avoid this by making your entities generic.
//you can annotated with #MappedSuperclass
public class BaseBean{
//you can specify the id here
}
public class Todo extends BaseBean {
}
#NoRepositoryBean
interface ICrudDao<T exntends BaseBean, ID extends Serializable> extends Repository<T, ID> {
void delete(T deleted);
List<T> findAll();
T findOne(ID id);
T save(T persisted);
}
I don't think it's possible. See How to create a Generic DAO class using Hibernate Context sessions and Hibernate: CRUD Generic DAO these might help.
I can also think of the Hibernate Session as an example of a single class that deals with the persistence of all types of objects, it just deals with Object type.

Use Spring Data JPA, QueryDSL to update a bunch of records

I'm refactoring a code base to get rid of SQL statements and primitive access and modernize with Spring Data JPA (backed by hibernate). I do use QueryDSL in the project for other uses.
I have a scenario where the user can "mass update" a ton of records, and select some values that they want to update. In the old way, the code manually built the update statement with an IN statement for the where for the PK (which items to update), and also manually built the SET clauses (where the options in SET clauses can vary depending on what the user wants to update).
In looking at QueryDSL documentation, it shows that it supports what I want to do. http://www.querydsl.com/static/querydsl/4.1.2/reference/html_single/#d0e399
I tried looking for a way to do this with Spring Data JPA, and haven't had any luck. Is there a repostitory interface I'm missing, or another library that is required....or would I need to autowire a queryFactory into a custom repository implementation and very literally implement the code in the QueryDSL example?
You can either write a custom method or use #Query annotation.
For custom method;
public interface RecordRepository extends RecordRepositoryCustom,
CrudRepository<Record, Long>
{
}
public interface RecordRepositoryCustom {
// Custom method
void massUpdateRecords(long... ids);
}
public class RecordRepositoryImpl implements RecordRepositoryCustom {
#Override
public void massUpdateRecords(long... ids) {
//implement using em or querydsl
}
}
For #Query annotation;
public interface RecordRepository extends CrudRepository<Record, Long>
{
#Query("update records set someColumn=someValue where id in :ids")
void massUpdateRecords(#Param("ids") long... ids);
}
There is also #NamedQuery option if you want your model class to be reusable with custom methods;
#Entity
#NamedQuery(name = "Record.massUpdateRecords", query = "update records set someColumn=someValue where id in :ids")
#Table(name = "records")
public class Record {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
//rest of the entity...
}
public interface RecordRepository extends CrudRepository<Record, Long>
{
//this will use the namedquery
void massUpdateRecords(#Param("ids") long... ids);
}
Check repositories.custom-implementations, jpa.query-methods.at-query and jpa.query-methods.named-queries at spring data reference document for more info.
This question is quite interesting for me because I was solving this very problem in my current project with the same technology stack mentioned in your question. Particularly we were interested in the second part of your question:
where the options in SET clauses can vary depending on what the user
wants to update
I do understand this is the answer you probably do not want to get but we did not find anything out there :( Spring data is quite cumbersome for update operations especially when it comes to their flexibility.
After I saw your question I tried to look up something new for spring and QueryDSL integration (you know, maybe something was released during past months) but nothing was released.
The only thing that brought me quite close is .flush in entity manager meaning you could follow the following scenario:
Get ids of entities you want to update
Retrieve all entities by these ids (first actual query to db)
Modify them in any way you want
Call entityManager.flush resulting N separate updates to database.
This approach results N+1 actual queries to database where N = number of ids needed to be updated. Moreover you are moving the data back and forth which is actually not good too.
I would advise to
autowire a queryFactory into a custom repository
implementation
Also, have a look into spring data and querydsl example. However you will find only lookup examples.
Hope my pessimistic answer helps :)

Extend spring data's default syntax

In my current project almost every entity has a field recordStatus which can have 2 values:
A for Active
D for Deleted
In spring data one can normally use:
repository.findByLastName(lastName)
but with the current data model we have to remember about the active part in every repository call, eg.
repository.findByLastNameAndRecordStatus(lastName, A)
The question is: is there any way to extend spring data in such a way it would be able to recognize the following method:
repository.findActiveByLastName(lastName)
and append the
recordStatus = 'A'
automatically?
Spring Data JPA provides 2 additional options for you dealing with circumstances that their DSL can't handle by default.
The first solution is custom queries with an #Query annotation
#Query("select s from MyTable s where s.recordStatus like 'A%'")
public MyObect findActiveByLastName(String lastName);
The second solution is to add a completely custom method the "Old Fashion Way" You can create a new class setup like: MyRepositoryImpl The Impl is important as it is How spring knows to find your new method (Note: you can avoid this, but you will have to manually link things the docs can help you with that)
//Implementation
public class MyRepositoryImpl implements MyCustomMethodInterface {
#PersistenceContext
EntityManager em;
public Object myCustomJPAMethod() {
//TODO custom JPA work similar to this
String myQuery = "TODO";
return em.createQuery(myQuery).execute();
}
}
//Interface
public interface MyCustomMethodInterface {
public Object myCustomJPAMethod();
}
//For clarity update your JPA repository as well so people see your custom work
public interface MySuperEpicRepository extends JPARepository<Object, String>, MyCustomMethodInterface {
}
These are just some quick samples so feel free to go read their Spring Data JPA docs if you would like to get a bit more custom with it.
http://docs.spring.io/spring-data/jpa/docs/current/reference/html/
Finally just a quick note. Technically this isn't a built in feature from Spring Data JPA, but you can also use Predicates. I will link you to a blog on this one since I am not overly familiar on this approach.
https://spring.io/blog/2011/04/26/advanced-spring-data-jpa-specifications-and-querydsl/
You can use Spring Data's Specifications. Take a look at this article.
If you create a 'Base'-specification with the recordStatus filter, and deriving all other specifications form this one.
Of course, everybody in your team should use the specifactions api, and not the default spring data api.
I am not sure you can extend the syntax unless you override the base class (SimpleReactiveMongoRepository; this is for reactive mongo but you can find the class for your DB type), what I can suggest you is to extend the base methods and then make your method be aware of what condition you want to execute. If you check this post you get the idea that I did for the patch operation for all entities.
https://medium.com/#ghahremani/extending-default-spring-data-repository-methods-patch-example-a23c07c35bf9

Categories

Resources