SpringData JPA: Query with collection of entity as parameter - spring-boot

I have a list of entities on which I want to perform an update, I know I could update the table with list of String/Integer.. etc as the parameter with something like
#Query("update tableName i set i.isUpdated = true where i.id in :ids")
void markAsUpdated(#Param("ids") List<Integer> itemIds);
I'm trying to avoid repeated conversion of list of entities to list of Ids for making the query in DB. I know there are deleteAll and deleteInBatch commands which accept parameter as list of entities.
How do I do this in JPA Query, I tried the following but it didn't work yet.
#Modifying(flushAutomatically = true, clearAutomatically = true)
#Query("update tableName i set i.updated = true where i in :items")
void markAsUpdated(#Param("items") List<Item> items)

The query needs ids, it doesn't know how to deal with entities.
You have multiple options:
Just pass ids to the method, the client is responsible for extracting ids.
Pass entities and use SpEL for extracting ids
As suggested in the comments use a default method to offer both APIs and to delegate from one to the other.
As for the question that came up in the comments: You can move the method for extracting the id into a single method by either have relevant entities implement an interface similar to this one:
interface WithId {
Long getId();
}
Or by passing a lambda to the method, doing the conversion for a single entity:
List<ID> extractIds(List<E> entities, Function<E, ID> extractor) {
// ...
}

Related

How to fetch multiple entities by a list of natural ids with Hiberate or JPA repository?

If I have a list of natural Ids, how can I fetch all the records in the DB associated with these natural Ids at once?
All I've seen are methods that let you find an entity by a natural Id, an example of one is shown below.
#Override
public Optional<T> findBySimpleNaturalId(ID naturalId) {
Optional<T> entity = entityManager.unwrap(Session.class)
.bySimpleNaturalId(this.getDomainClass())
.loadOptional(naturalId);
return entity;
}
I am looking for a method that can take a list natural Ids and fetch all the entities with these natural Ids. My current Entity has a autogenerated UUID and a naturalId and I'd like to keep it this way.
Is there something like this below
List<Song> songs = entityManager
.unwrap(Session.class)
.byMultipleSimpleNaturalIds(Song.class)
.multiLoad(songGroup.getSongIds());
// or using the repository
customRepository.findAllByNaturalId(...)
The examples you've seen are showing you how to build them yourself, as spring does not provide individual methods for you; it knows nothing about properties in your entity other than it must have an id. If you want a findAllByNaturalId method, you have to define it in the interface.
Specifying this in your customRepository:
public List<Song> findByNaturalIdIn(List<Int> naturalIds);
Spring should generate an implementation that creates a query similar to "Select s from Song s where s.naturalId In :naturalIds".
If it doesn't, just add that JPQL query string as an annotation and it will execute it for you:
#Query(value = "Select s from Song s where s.naturalId In :naturalIds")
public List<Song> findByNaturalIdIn(List<Int> naturalIds);
Or you can write your own implementation method to execute your loadOptional calls, or any query you wish, but you still must define the method in your repository.

SpringBoot named query: IN-clause not working for multiple values

"In clause" not working on passing multiple values
Repository code
#Query( "select myObject from MyObject myObject where myObject.anyColumn in :values" )
public List<MyObject> findPriDestByCntryAbbr(#Param("values") List<String> values);
Calling from service
List<String> values = Arrays.asList(valuesString.split(","));
List<MyObject> result = myObjectRepository.findByAnyColumns(values);
When i am passing single value it is retrieving correct information from table, But on passing multiple values in List "values" giving empty result
Ideally it should work. You should check the values variable.
The other way you can try is- without writing query.
As per the doc we can create MyObjectRepository interface by extending Repository interface and then have a method to fetch all MyObjects. It goes like:
public interface MyObjectRepository extends Repository<MyObject,**Long**> {
List<MyObject> findBy**AnyColumn**In(Collection<String> values);
}
"AnyColumn" is your column name and second parameter for Repository interface is as per your requirement.

How to Return all instances of the type with the given ID in JPA SpringBoot?

I'm trying to return (or fetch) all the records from the database based on an ID provided by me. I'm using JPA and i'm aware of findAll() method but it returns all the records without any criteria, I created a custom query and it is only returning a unique value from the table but i want to return all records based on a criteria.
For example, findAllByUserID(String UserID) method should return all the records based on that UserID not just one.
I'd appreciate any help or suggestion.
Thanks
Have a look at the doc. There you will find the keywords you can use to declare methods in repository interfaces that will generate the according queries:
https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#jpa.query-methods
In your case: If userID is an attribute of your entity you can add a method
List<YourEntity> findByfindAllByUserID(String userId)
to your repository interface.
First, make sure that you're not using any aggregate function in your select query such as DISTINCT()
Then make sure that the the method which is implementing that query is returning a List of you're desired result.
here's how it should look :
#Query("select t from table t where t.code = ?1")
List<Result> findAllByUserID(String UserID);

Run two #NamedNativeQuery query on same entity Class

I want to define two #NamedNativequery on entity class . When tying to define eclipse gives a error.
Duplicate annotation of non-repeatable type #NamedNativeQuery. Only
annotation types marked #Repeatable can be used multiple times at one
target.
From that error , I know we cannot define two define two #NamedNativeQuery of the entity class like
#Entity
#Table(name = "abc")
#NamedNativeQuery(name = "ABC.getSomeMethod1" query = "some_query",resultSetMapping ="abcDTO")//1st name query
// #NamedNativeQuery(name = "some_name" query = "some_query",resultSetMapping ="some_dto")//try to define second query , but gives error
public class ABC {
}
I am using spring repository at dao layer to called the method which bind with this query
public interface SomeInterface extends JpaRepository<ABC, Long> {
#Query(nativeQuery =true)
List<ABCDTO> getSomeMethod1(#Param("someParam1") long someParam1, #Param("someParam2") String someParam2);
}
The senario is that I want to run the 1st native sql (which run fine) query and then run the 2nd native sql query(want to run this also from same). How to solve this or What is the possible solution.
If this way I cannot run the two native sql query then is there any other to achive this.
You can define multiple named queries like this
#NamedNativeQueries({
#NamedNativeQuery(name = "ABC.getSomeMethod1"
query = "some_query",resultSetMapping ="abcDTO"
),
#NamedNativeQuery(name = "some_name"
query = "some_query",resultSetMapping ="some_dto"
)
})
Then in the business layer under the transaction you can call these two queries one after another,
If its a simple join between two entities and select and display better go with join's. Always remember to have those columns index in the Table ;)

Incorrect derived query for byId in Spring Data Neo4j

I have two entities: User and Connection, along with two appropriate repositories. Both entities has #GraphId id field. Connection entity has User user field.
In ConnectionRepository interface I added following method:
List<Connection> findByUserId(long userId)
But it doesn't work. It generates incorrect cypher query. I think it incorrect, because it contains clause like this:
WHERE user.id = 15
which is not working, because id is not a property. It must be:
WHERE id(user) = 15
Is this a bug? In any case, how can I get it to work?
The derived query translates to the property id of the user defined on the Connection. It is quite possible that node entities contain a user managed id property as well and it would be incorrect to assume that id is always the node id.
In this case, you might want to use a #Query instead.
#Query("MATCH (user:label) WHERE ID(user)={0} return user")
List<Connection> findByUserId(long userId)

Resources