Spring Data JPA/Boot: findBy ... or - java

I want to write a finder method in my repository to find an object based on one field OR another one while supplying one parameter like:
#RepositoryDefinition(domainClass = Person.class, idClass = Long.class)
public interface PersonRepository extends CrudRepository<Person, Long> {
List<Person> findAllByIdOrAnotherId(someId);
}
How can I do that without using SQL?

I added a second parameter to the method and it worked.
List<Transaction> findAllByIdOrParentId(Long id, Long parentId);
This is just a definition for the method because I pass the same parameter to the method from the service as:
List<Transaction> transactions = transactionRepository.findAllByIdOrParentId(transactionId, transactionId);

One of the cool things about Spring Data JPA is the fact you can define custom queries as abstract methods in your interface.
In a nutshell, you can define a new search criteria just by specifying the #Entity field name you want to query by. So, let's say i want to findAll() by Id and CustomId. Both Id and CustomId are fields on my domain class. I would do:
List<Person> findAllByIdOrCustomId(someId, someCustomId);
For more info, research the link below:
Spring Data JPA - DEFINE QUERY METHODS

Related

Return List<String> or String in hibernate query without query annotation

I am using Spring Boot application. I am using following method to return List<String> using #Query
annotation.
#Query("select c.name from Customer c")
List<String> findAllNames();
Is there a way to get the above result without #Query or #NativeQuery annotations?
Spring Data JPA supports this quite well. You can define a POJO that represents the (reduced) object (the projection) you want of the entity, then define a method that describes the query, returns the appropriate type, and Spring Data will generate all the code necessary to execute the query and map the results.
Here's an example of a custom repository interface that uses this technique (untested pseudo-code):
public class CustomerSummary {
private String name;
// constructor and getters
}
#Repository
public interface CustomerRepository extends CrudRepository<Customer, Long> {
List<CustomerSummary> getAllSummaries();
}
I'm not sure if you can use "simple" types such as String in the return type, but I wouldn't be surprised if that is supported.
Also, you might have to tweak the exact method naming of `` to make sure Spring Data correctly interprets it as you want. Here is some additional reference documentation.
You could use projection to select only name property:
interface CustomerNameProjection {
String getName();
}
and then use it in Repository class:
List<CustomerNameProjection> findAllProjectedBy();
edit: corrected repository method name
Inject EntityManager and call createQuery.
entityManager.createQuery("select c.name from Customer c")
.getResultList()

How to sort By Name and status Active for JPA repository query?

I am developing Spring Boot + Spring Data JPA + Postgres + Lombok example. In this example, I want to fetch all student firstName is ASC order and whose status in Active.
I developed below query, which works fine, but I dont see a way to also use status=Active here in JpaRepository query.
NOTE: In my case, status field is Enum in Java.
Is there any way if we can do the same ? I know I can fetch all students and then using streams can easily filter, but using JpaRepository query, is there any way?
List<Student> students = studentRepository.findAll(new Sort(Sort.Direction.ASC, "studentName"));
In your StudentRepository interface, that extends Repository / JpaRepository you can add a method signature like that:
public interface StudentRepository extends ....{
List<Student> findAllByStatusOrderByStudentNameAsc(String status);
}
Just put the following method signature in your repository and call it where you need with argument 'Active'.
List<Student> findAllByStatusOrderByStudentNameAsc(String status);

Spring Data MongoRepositories DtoInstantiatingConverter

I have
#Document
public class Employee
{
#Id
Long empCode;
String empSurname;
String address;
// getters setters
}
I have written a query using Mongo Repositories like
public interface EmployeeRepository extends MongoRepository<Employee, Long>
{
List<Employee> findEmployeesByEmpCode(int empCode);
#Query(value="{ 'empCode' : ?0 }", fields="{ 'address' : 1}")
List<String> findAddressByEmpCode(int empCode);
The first query works but the second fails. It works only if I change the returned type of List to Employee.
The error at failure is
java.lang.IllegalArgumentException: [Assertion failed] - this argument is required; it must not be null
at org.springframework.util.Assert.notNull(Assert.java:115)
at org.springframework.util.Assert.notNull(Assert.java:126)
at org.springframework.data.convert.EntityInstantiators.getInstantiatorFor(EntityInstantiators.java:86)
at org.springframework.data.mongodb.repository.query.DtoInstantiatingConverter.<init>(DtoInstantiatingConverter.java:61)
at org.springframework.data.mongodb.repository.query.MongoQueryExecution$ResultProcessingConverter.convert(MongoQueryExecution.java:376)
at org.springframework.data.mongodb.repository.query.MongoQueryExecution$ResultProcessingExecution.execute(MongoQueryExecution.java:345)
at org.springframework.data.mongodb.repository.query.AbstractMongoQuery.execute(AbstractMongoQuery.java:91)
Obviously, it cannot work out how to convert the values from the contained Employee object to String.
How can I implement this ? Any example would be much appreciated.
Thanks,
The fields attribute is used to filter properties which are not set into your domain objects (Employee). It has not much to do with MongoDB projection mechanism.
In order to use projection here, see projections in the reference documentation.
You will have to create a projection interface:
interface AddressesProjection { String getAddress(); }
and change the repository method signature accordingly:
List<AddressesProjection> findByEmpCode(int empCode);
Projections declare a contract between the underlying type and the method signatures related to the exposed properties. Hence it is required to name getter methods according to the property name of the underlying type.
This is the closest you can get, afaik.

Should JPA Repositories be created by Table or Object?

What is the best practice for creating JPA Repositories?
Right now I have two tables: Media and Ratings.
To find media that is similar a query has to be made to the Rating table to find the interconnections between the different media. This query then returns a list of Media objects.
Should this query be placed in the Rating repository (as it queries the Rating table),
or in the Media repository (as it returns a collection of Media objects with the IDs set)?
I have tried searching for best-practices for this particular use-case but haven't found anything relevant.
Update:
The SQL query is defined like this:
#Query(value=[SQL query with several joins],nativeQuery=true)
List<Media> mySQLQuery()
it returns a list of mediaId's which can be returned from the function as Media objects.
Well, probably neither.
You see when you implement a Repository and do for example findAll() you will get a List of all Objects from the Entity used in the Repository creation.
interface MyRepo implements<Media, Long>....
myRepo.findAll() will return a List of Media objects.
What you are trying to do is out of scope from a Repository as it acts on only that particular Entity with a finit operations on that particular Entity.
Also it seems to me that Media and Ratings are connected with a OneToMany or ManyToMany, then this definitely should go to a separate DAO method.
I solved it by creating a custom implementation of the repository, implementing a custom interface.
First I had to declare the interface:
public interface CustomQuery {
public List<Integer>myCustomQuery(int id)
}
Then I implemented it in my custom repository. The name is important, it has to begin with the name of the repository interface it is extending. My repo is named MediaRepository so I named the custom implementation MediaRepositoryImpl:
#Component
public class MediaRepositoryImpl implements CustomQuery {
#PersistenceContext
EntityManager manager;
public List<Integer>myCustomQuery(int id){
Query q = manager.createNativeQuery(SQL_QUERY_GOES_HERE);
List<Integer> ids = new ArratList<Integer>();
#SuppressWarnings("unchecked")
List<Integer> result = q.getResultList();
for(Integer o : result){
//process the results and add them to the list
}
return list;
}
}
This way, you can do custom native queries while still keeping the regular Repositories clean. This approach is also easier to test.

Spring CrudRepository findByInventoryIds(List<Long> inventoryIdList) - equivalent to IN clause

In Spring CrudRepository, do we have support for "IN clause" for a field? ie something similar to the following?
findByInventoryIds(List<Long> inventoryIdList)
If such support is not available, what elegant options can be considered? Firing queries for each id may not be optimal.
findByInventoryIdIn(List<Long> inventoryIdList) should do the trick.
The HTTP request parameter format would be like so:
Yes ?id=1,2,3
No ?id=1&id=2&id=3
The complete list of JPA repository keywords can be found in the current documentation listing. It shows that IsIn is equivalent – if you prefer the verb for readability – and that JPA also supports NotIn and IsNotIn.
If inventoryId has the primary key, you can simply use yourRepo.findAll(inventoryIdList).
For any method in a Spring CrudRepository you should be able to specify the #Query yourself. Something like this should work:
#Query( "select o from MyObject o where inventoryId in :ids" )
List<MyObject> findByInventoryIds(#Param("ids") List<Long> inventoryIdList);
Yes, that is supported.
Check the documentation provided here for the supported keywords inside method names.
You can just define the method in the repository interface without using the #Query annotation and writing your custom query. In your case it would be as followed:
List<Inventory> findByIdIn(List<Long> ids);
I assume that you have the Inventory entity and the InventoryRepository interface. The code in your case should look like this:
The Entity
#Entity
public class Inventory implements Serializable {
private static final long serialVersionUID = 1L;
private Long id;
// other fields
// getters/setters
}
The Repository
#Repository
#Transactional
public interface InventoryRepository extends PagingAndSortingRepository<Inventory, Long> {
List<Inventory> findByIdIn(List<Long> ids);
}

Categories