Wisdom query using Spring JpaRepository - spring-data-jpa

Suggest, I have next structure of objects:
class MovieWrapper {
private Movie movie;
}
class Movie {
private User user;
#Enumerated(EnumType.STRING)
private Status status;
}
and I want to query from repository only first occurrence of MovieWrapper by User Id and where status is Active or Pending:
#Repository
public interface MovieWrapperRepository extends JpaRepository<MovieWrapper, Long> {
MovieWrapper findFirstByMovieUserIdAndMovieStatusActiveOrMovieStatusPending(Long userId);
}
How to do it in correct way?

To get 'custom' object from the repo method you should use projections (interface-base projection is preferable IMO).
To implement complex method you can build its query yourself, for example:
public interface MovieRepo extends JpaRepository<Movie, Long> {
Query("select m as movie from Movie m where m.user.id = ?1 and (m.status = 'ACTIVE' or m.status = 'PENDING')")
List<MovieProjection> findActiveOrPending(Long userId, Status status);
}
Where MovieProjection is a simple interface:
public interface MovieProjection {
Movie getMovie();
}
Note to the alias m as movie in the query, it's recommended to use with projections to avoid some errors.
UPDATED
If you need only one records you can use a trick with Pageable:
public interface MovieRepo extends JpaRepository<Movie, Long> {
Query("select m as movie from Movie m where m.user.id = ?1 and (m.status = 'ACTIVE' or m.status = 'PENDING')")
List<MovieProjection> findAllActiveOrPending(Long userId, Status status, Pageable pageable);
default Optional<MovieProjection> getFirstOne(Long userId, Status status) {
return findAllActiveOrPending(userId, status, PageRequest.of(0, 1)).stream().findAny();
}
}
Here we take first page with one record (PageRequest.of(0, 1) - see here)

Related

SpringBoot JPA ManyToMany leftOuter join query and mapping

I have manyToMany relationship between Newsfeed and User in order to store favorite Newsfeeds of Uesrs.
Newsfeed
id
title
description
1
Test Title
Here is description
2
Title 2
Here is another description
Favorite_Newsfeed (ManyToMany Relationship)
newsfeed_id
user_id
1
1
2
2
1
2
Now I need to retrieve all the newsfeeds along with favorites filtered by user.
NewsfeedWithFavorite (Goal)
id
title
description
isFavorite
1
Test Title
Here is description
1
2
Title 2
Here is another description
I can do this by using case when in raw query but What is the best approach to retrieve data in this format using JPA? (I am new in JPA)
You have to use join in JPQL and then map favorites.
Repository :
public interface NewsFeedJpaRepository extends JpaRepository<NewsFeed, Long> {
#Query("SELECT nf , fnf.user.id FROM NewsFeed nf LEFT JOIN FavoriteNewsFeed fnf ON fnf.newsfeed = nf and fnf.user.id = :userId ")
Page<Object[]> findAllWithFavorite(Long userId, Pageable pageable);
}
create a dto like following:
public class NewsFeedWithFavorite extends NewsFeed {
private Boolean isFavorite;
public NewsFeedWithFavorite(){};
public NewsFeedWithFavorite(NewsFeed newsFeed, Long userID){
setId(newsFeed.getId());
setTitle(newsFeed.getTitle());
setDescription(newsFeed.getDescription());
if(userID !=null)
setFavorite( Boolean.TRUE);
else
setFavorite(Boolean.FALSE);
};
public Boolean getFavorite() {
return isFavorite;
}
public void setFavorite(Boolean favorite) {
isFavorite = favorite;
}
}
And finally in the Service :
public Page<NewsFeedWithFavorite> findAllWithFavorite(Long userId, Pageable pageable) {
Page<Object[]> result = newsFeedJpaRepository.findAllWithFavorite(userId,pageable);
return result.map(objArr -> new NewsFeedWithFavorite((NewsFeed) objArr[0], (Long) objArr[1]));
}
Do not use #ManyToMany annotation.
It will be extremely hard for you to add multiple columns and in general work with the auto generated Favorite_Newsfeed table.
The best way to handle this scenarior is to use a different table for User's news feed.
Check out the following structure.
public class User {
#OneToMany
private List<UserNewsFeed> newsFeeds;
}
public class NewsFeed {
public String description;
public String title;
}
public class UserNewsFeed {
#ManyToOne
private User user;
#ManyToOne
private NewsFeed newsFeed;
private boolean isFavorite;
}

Use limit and skip in MongoRepository<Customer,String>

We are working on a project to get data from mongoDB. We have created repository class as below
#Repository
public interface CustomerRepository extends MongoRepository<Customer,String>{
List<Customer> customers = findByCustomerId(final String customerId);
}
We are looking to add skip/offset and limit parameters to be used as part of findByCustomerId method. where limit is used to define number of records returned and skip/offset defines the number of records after which we need to get the records.
Please help how we can get this implemented in best way using MongoRepository.
There are two ways to do this.
Use of #Aggregation annotation as mentioned in this answer.
https://stackoverflow.com/a/71292598/8470055
For example:
#Repository
public interface CustomerRepository extends MongoRepository<Customer,String>{
#Aggregation(pipeline = {
"{ '$match': { 'customerId' : ?0 } }",
"{ '$sort' : { 'customerId' : 1 } }",
"{ '$skip' : ?1 }",
"{ '$limit' : ?2 }"
})
List<Customer> findByCustomerId(final String customerId, int skip, int limit);
#Aggregation(pipeline = {
"{ '$match': { 'customerId' : ?0 } }",
"{ '$sort' : { 'customerId' : 1 } }",
"{ '$skip' : ?1 }"
})
Page<Customer> findCustomers(final String customerId, int skip, Pageable pageable);
}
The $match operator's query might need to be modified so that it better reflects the condition that needs to be satisfied by the matching documents.
Use Pageable argument in the query method and supply the PageRequest from the layer that calls the Repository method as shown in this answer.
https://stackoverflow.com/a/10077534/8470055
For the code snippet in the question this then becomes.
#Repository
public interface CustomerRepository extends MongoRepository<Customer,String> {
Page<Customer> findByCustomerId(final String customerId, Pageable pageable);
}
// -------------------------------------------------------
// Call the repository method from a service
#Service
public class CustomerService {
private final CustomerRepository customerRepository;
public CustomerService(CustomerRepository customerRepository) {
this.customerRepository = customerRepository;
}
public List<Customer> getCustomers(String customerId, int skip, int limit) {
// application-specific handling of skip and limit arguments
int page = 1; // calculated based on skip and limit values
int size = 5; // calculated based on skip and limit values
Page<Customer> page = customerRepository.findByCustomerId(customerId,
PageRequest.of(page, size, Sort.Direction.ASC, "customerId"));
List<Customer> customers = page.getContent();
/*
Here, the query method will retrieve 5 documents from the second
page.
It skips the first 5 documents in the first page with page index 0.
This approach requires calculating the page to retrieve based on
the application's definition of limit/skip.
*/
return Collections.unmodifiableList(customers);
}
}
The aggregation approach is more useful.
If the result is limited to a few documents then the query method can return List<Customer>.
If there a lot of documents then the query method can be modified to use Pageable argument that returns Page<Customer> to page over the documents.
Refer to both Spring Data and MongoDB documentation.
https://docs.spring.io/spring-data/mongodb/docs/3.2.10/reference/html/#mongo.repositories
https://docs.spring.io/spring-data/mongodb/docs/3.2.10/reference/html/#mongodb.repositories.queries.aggregation
https://docs.spring.io/spring-data/mongodb/docs/3.2.10/api/org/springframework/data/mongodb/repository/Aggregation.html
https://docs.spring.io/spring-data/commons/docs/current/api/org/springframework/data/domain/Pageable.html
https://docs.spring.io/spring-data/commons/docs/current/api/org/springframework/data/domain/PageRequest.html
MongoDB Aggregation - https://www.mongodb.com/docs/manual/meta/aggregation-quick-reference/
Dynamic Queries
Custom Spring Data repository implementation along with use of MongoTemplate should help in implementing dynamic queries.
Custom Repositories - https://docs.spring.io/spring-data/mongodb/docs/3.2.10/reference/html/#repositories.custom-implementations
MongoTemplate - https://docs.spring.io/spring-data/mongodb/docs/3.2.10/api/org/springframework/data/mongodb/core/MongoTemplate.html
A simple use case is to use a custom repository with the Query and SimpleMongoRepository classes.
CustomerRepository.java
#Repository
public interface CustomerRepository extends ResourceRepository<Customer, String> {
}
ResourceRepository.java
#NoRepositoryBean
public interface ResourceRepository<T, I> extends MongoRepository<T, I> {
Page<T> findAll(Query query, Pageable pageable);
}
ResourceRepositoryImpl.java
#SuppressWarnings("rawtypes")
public class ResourceRepositoryImpl<T, I> extends SimpleMongoRepository<T, I> implements ResourceRepository<T, I> {
private MongoOperations mongoOperations;
private MongoEntityInformation entityInformation;
public ResourceRepositoryImpl(final MongoEntityInformation entityInformation, final MongoOperations mongoOperations) {
super(entityInformation, mongoOperations);
this.entityInformation = entityInformation;
this.mongoOperations = mongoOperations;
}
#Override
public Page<T> findAll(final Query query, final Pageable pageable) {
Assert.notNull(query, "Query must not be null!");
long total = mongoOperations.count(query, entityInformation.getJavaType(), entityInformation.getCollectionName());
List<T> content = mongoOperations.find(query.with(pageable), entityInformation.getJavaType(), entityInformation.getCollectionName());
return new PageImpl<T>(content,pageable,total);
}
}
CustomerService.java
#RequiredArgsConstructor
#Service
public class CustomerService {
private final CustomerRepository repository;
/**
* #param customerId
* #param limit the size of the page to be returned, must be greater than 0.
* #param page zero-based page index, must not be negative.
* #return Page of {#link Customer}
*/
public Page<Customer> getCustomers(String customerId, int limit, int page) {
Query query = new Query();
query.addCriteria(Criteria.where("customerId").is(customerId));
return repository.findAll(query, PageRequest.of(page, limit, Sort.by(Sort.Direction.ASC, "customerId")));
}
public List<Customer> getCustomersList(String customerId, int limit, int page) {
Page<Customer> customerPage = getCustomers(customerId, limit, page);
return customerPage.getContent();
}
}
A reference with specific criteria:
https://dzone.com/articles/advanced-search-amp-filtering-api-using-spring-dat
I have used the Aggregation query with $skip and $limit, it works fine and is quite useful when you need to Paginate a complex piece of a query result. For simpler queries, I use the spring mongo template which takes a Query object. The query object takes a Pageable object where you define a page number and page size with a sorting option.
Criteria criterion = Criteria.where("field").is("value");//build your criteria here.
Query query = new Query(criterion);
Sort fieldSorting = Sort.by(Sort.Direction.DESC, "sortField"); // sort field
int pageNo = 1; //which page you want to fetch. NoOfPages = TotalRecords/PageZie
int pagesize = 10; // no of records per page
Pageable pageable = PageRequest.of(pageNo, pagesize, fieldSorting); // define your page
mongoTemplate.find(query.with(pageable), Object.class); // provide appropriate DTO class to map.
For mongo DB aggregation options - https://www.mongodb.com/docs/manual/reference/operator/aggregation/limit/
https://www.mongodb.com/docs/manual/reference/operator/aggregation/skip/
Another (maybe simpler) approach for limiting the results of the query is adding the filters in the method declaration when using MongoRepository. Both the keywords top and first can be used to reach this goal, specifying also the amount of results desired (or by omitting it, obtaining thus just one result).
The code below is an example, available in the docs.spring.io documentation for MongoRepositories (link below).
User findFirstByOrderByLastnameAsc();
User findTopByOrderByAgeDesc();
Page<User> queryFirst10ByLastname(String lastname, Pageable pageable);
Slice<User> findTop3ByLastname(String lastname, Pageable pageable);
List<User> findFirst10ByLastname(String lastname, Sort sort);
List<User> findTop10ByLastname(String lastname, Pageable pageable);
You can also apply pagination to your query (more details in the
documentation).
SOME EXTRA INFORMATION ABOUT SORTING:
As the other answers also gave some insight about the sorting, I would like to bring other options in this regard.
If your method will always sort the results in the same way, the sorting can be made by using the OrderBy keyword in your method declaration, followed by Asc or Desc depending on your use-case.
List<User> findFirst10ByLastnameOrderByAgeAsc(String lastname);
List<User> findFirst10ByLastnameOrderByAgeDesc(String lastname);
If you would like to sort your results dynamically, you can use the Sort argument on your method and provide.
List<User> findFirst10ByLastname(String lastname, Sort sort);
As an example, providing Sort.by(DESC, "age") in the method call will define { age : -1 } as the sort for the query.
References:
https://docs.spring.io/spring-data/mongodb/docs/3.2.10/reference/html/#repositories.query-methods

Spring Data JPA And NamedEntityGraphs

currently I am wrestling with being able to fetch only the data I need. The findAll() method needs to fetch data dependant on where its getting called.
I do not want to end up writing different methods for each entity graph.
Also, I would avoid calling entitymanagers and forming the (repetitive) queries myself.
Basicly I want to use the build in findAll method, but with the entity graph of my liking. Any chance?
#Entity
#Table(name="complaints")
#NamedEntityGraphs({
#NamedEntityGraph(name="allJoinsButMessages", attributeNodes = {
#NamedAttributeNode("customer"),
#NamedAttributeNode("handling_employee"),
#NamedAttributeNode("genre")
}),
#NamedEntityGraph(name="allJoins", attributeNodes = {
#NamedAttributeNode("customer"),
#NamedAttributeNode("handling_employee"),
#NamedAttributeNode("genre"),
#NamedAttributeNode("complaintMessages")
}),
#NamedEntityGraph(name="noJoins", attributeNodes = {
})
})
public class Complaint implements Serializable{
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue
private long id;
private Timestamp date;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "customer")
private User customer;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "handling_employee")
private User handling_employee;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name="genre")
private Genre genre;
private boolean closed;
#OneToMany(mappedBy = "complaint", fetch = FetchType.LAZY, cascade = CascadeType.ALL)
private List<ComplaintMessage> complaintMessages = new ArrayList<ComplaintMessage>();
//getters and setters
}
And my JPARepository
#Repository
public interface ComplaintRepository extends JpaRepository<Complaint, Long>{
List<Complaint> findByClosed(boolean closed);
#EntityGraph(value = "allJoinsButMessages" , type=EntityGraphType.FETCH)
#Override
List<Complaint> findAll(Sort sort);
}
We ran into a similar problem and devised several prospective solutions but there doesn't seem to be an elegant solution for what seems to be a common problem.
1) Prefixes. Data jpa affords several prefixes (find, get, ...) for a method name. One possibility is to use different prefixes with different named graphs. This is the least work but hides the meaning of the method from the developer and has a great deal of potential to cause some non-obvious problems with the wrong entities loading.
#Repository
#Transactional
public interface UserRepository extends CrudRepository<User, Integer>, UserRepositoryCustom {
#EntityGraph(value = "User.membershipYearsAndPreferences", type = EntityGraphType.LOAD)
User findByUserID(int id);
#EntityGraph(value = "User.membershipYears", type = EntityGraphType.LOAD)
User readByUserId(int id);
}
2) CustomRepository. Another possible solutions is to create custom query methods and inject the EntityManager. This solution gives you the cleanest interface to your repository because you can name your methods something meaningful, but it is a significant amount of complexity to add to your code to provide the solution AND you are manually grabbing the entity manager instead of using Spring magic.
interface UserRepositoryCustom {
public User findUserWithMembershipYearsById(int id);
}
class UserRepositoryImpl implements UserRepositoryCustom {
#PersistenceContext
private EntityManager em;
#Override
public User findUserWithMembershipYearsById(int id) {
User result = null;
List<User> users = em.createQuery("SELECT u FROM users AS u WHERE u.id = :id", User.class)
.setParameter("id", id)
.setHint("javax.persistence.fetchgraph", em.getEntityGraph("User.membershipYears"))
.getResultList();
if(users.size() >= 0) {
result = users.get(0);
}
return result;
}
}
#Repository
#Transactional
public interface UserRepository extends CrudRepository<User, Integer>, UserRepositoryCustom {
#EntityGraph(value = "User.membershipYearsAndPreferences", type = EntityGraphType.LOAD)
User findByUserID(int id);
}
3) JPQL. Essentially this is just giving up on named entity graphs and using JPQL to handle your joins for you. Non-ideal in my opinion.
#Repository
#Transactional
public interface UserRepository extends CrudRepository<User, Integer>, UserRepositoryCustom {
#EntityGraph(value = "User.membershipYearsAndPreferences", type = EntityGraphType.LOAD)
User findByUserID(int id);
#Query("SELECT u FROM users WHERE u.id=:id JOIN??????????????????????????")
User findUserWithTags(#Param("id") final int id);
}
We went with option 1 because it is the simplest in implementation but this does mean when we use our repositories we have have to look at the fetch methods to make sure we are using the one with the correct entity graph. Good luck.
Sources:
JPA EntityGraph with different views using Spring
https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#repositories.query-methods
I don't have enough reputation to post all of my sources. Sorry :(
We had the same issue and built a Spring Data JPA extension to solve it :
https://github.com/Cosium/spring-data-jpa-entity-graph
This extension allows to pass named or dynamically built EntityGraph as an argument of any repository method.
With this extension, you would have this method immediatly available:
List<Complaint> findAll(Sort sort, EntityGraph entityGraph);
And be able to call it with an EntityGraph selected at runtime.
Use #EntityGraph together with #Query
#Repository
public interface ComplaintRepository extends JpaRepository<Complaint, Long>{
#EntityGraph(value = "allJoinsButMessages" , type=EntityGraphType.FETCH)
#Query("SELECT c FROM Complaint ORDER BY ..")
#Override
List<Complaint> findAllJoinsButMessages();
#EntityGraph(value = "allJoins" , type=EntityGraphType.FETCH)
#Query("SELECT c FROM Complaint ORDER BY ..")
#Override
List<Complaint> findAllJoin();
...
}
Using the #EntityGraph annotation on a derived query is possible, as I found out from This article. The article has the example:
#Repository
public interface ArticleRepository extends JpaRepository<Article,Long> {
#EntityGraph(attributePaths = "topics")
Article findOneWithTopicsById(Long id);
}
But I don't think there's anything special about "with" and you can actually have anything between find and By. I tried these and they work (this code is Kotlin, but the idea is the same):
interface UserRepository : PagingAndSortingRepository<UserModel, Long> {
#EntityGraph(attributePaths = arrayOf("address"))
fun findAnythingGoesHereById(id: Long): Optional<UserModel>
#EntityGraph(attributePaths = arrayOf("address"))
fun findAllAnythingGoesHereBy(pageable: Pageable): Page<UserModel>
}
The article had mentioned the caveat that you can't create a method similar to findAll which will query all records without having a By condition and uses findAllWithTopicsByIdNotNull() as an example. I found that just including By by itself at the end of the name was sufficient: findAllWithTopicsBy(). A little more terse but maybe a little more confusing to read. Using method names which end with just By without any condition may be in danger of breaking in future versions in Spring since it doesn't seem like an intended use of derived queries name.
It looks like the code for parsing derived query names in Spring is here on github. You can look there in case you're curious about what's possible for derived queries repository method names.
These are the spring docs for derived queries.
This was tested with spring-data-commons-2.2.3.RELEASE
EDIT: this doesn't actually work. Ended up having to go with https://github.com/Cosium/spring-data-jpa-entity-graph. The default method LOOKS correct, but doesn't successfully override the annotations.
Using JPA, what I found works is to use a default method, with a different EntityGraph annotation:
#Repository
public interface ComplaintRepository extends JpaRepository<Complaint, Long>{
List<Complaint> findByClosed(boolean closed);
#EntityGraph(attributePaths = {"customer", "genre", "handling_employee" }, type=EntityGraphType.FETCH)
#Override
List<Complaint> findAll(Sort sort);
#EntityGraph(attributePaths = {"customer", "genre", "handling_employee", "messages" }, type=EntityGraphType.FETCH)
default List<Complaint> queryAll(Sort sort){
return findAll(sort);
}
}
You don't have to do any of the re-implementation, and can customize the entity graph using the existing interface.
Can you try create EntiyGraph name with child that you will request and give same name to the find all method.
Ex:
#EntityGraph(value = "fetch.Profile.Address.record", type = EntityGraphType.LOAD)
Employee getProfileAddressRecordById(long id);
For your case:
#NamedEntityGraph(name="all.Customer.handling_employee.genre", attributeNodes = {
#NamedAttributeNode("customer"),
#NamedAttributeNode("handling_employee"),
#NamedAttributeNode("genre")
})
method name in repository
#EntityGraph(value = "all.Customer.handling_employee.genre" , type=EntityGraphType.FETCH)
findAllCustomerHandlingEmployeeGenre
This way you can keep track of different findAll methods.

Spring Data REST custom finder for JpaRepository

I am looking to build a REST interface with a generic finder. The idea is to provide a search form where users can get all records by not providing any parameter or refine their search results by typing any combination of the fields.
The simple example I have annotates the JpaRepository with #RestResource which provides a nice out of the box way to add finders either by using #Query or by method name conventions
#RestResource(path = "users", rel = "users")
public interface UserRepository extends JpaRepository<User, Long>{
public Page<User> findByFirstNameStartingWithIgnoreCase(#Param("first") String fName, Pageable page);
}
I am looking to add a custom finder that would map my parameters and would leverage the paging, sorting and REST support where the actual implementation query will be composed dynamically (probably using QueryDSL) the method will have n parameters (p 1 ... p n) and will look like:
public Page<User> findCustom(#Param("p1") String p1, #Param("p2") String p2, ... #Param("pn") String pn, Pageable page);
I have tried the approach described in:
http://docs.spring.io/spring-data/data-jpa/docs/current/reference/html/repositories.html#repositories.custom-implementations
but my custom method is not available from the repository's REST interface (/users/search)
I hope someone already figured this out and would be kind to give me some direction.
Try something like this but of course adopted to your scenario:
public interface LocationRepository extends CrudRepository,
PagingAndSortingRepository,
LocationRepositoryExt {
}
public interface LocationRepositoryExt {
#Query
public List findByStateCodeAndLocationNumber(#Param("stateCode") StateCode stateCode, #Param("locationNumber") String locationNumber);
}
class LocationRepositoryImpl extends QueryDslRepositorySupport implements LocationRepositoryExt {
private static final QLocation location = QLocation.location;
public LocationRepositoryImpl() {
super(Location.class);
}
#Override
public Page findByStateAndLocationNumber(#Param("state") State state, #Param("locationNumber") String locationNumber, Pageable pageable) {
List locations = from(location)
.where(location.state.eq(state)
.and(location.locationNumber.eq(locationNumber)))
.list(location);
return new PageImpl(locations, pageable, locations.size());
}
}

Using the $in operator through Morphia - doing it wrong?

I have the following Play Framework entity (using Morphia for persistence) as part of a generic blogging app:
#Entity
public class Comment extends Model {
...
#Reference
#Indexed
public SiteUser commenter;
public static List<Comment> getLastCommentsByUsers(final List<SiteUser> users) {
final Query<Comment> query ds().createQuery(Comment.class);
query.field(commenter).hasAnyOf(users);
return query.asList();
}
}
SiteUser:
#Entity(noClassnameStored=true)
public class SiteUser extends AbstractUser {
public String realName;
}
AbstractUser:
public class AbstractUser extends Model {
#Indexed(value= IndexDirection.DESC, unique = true)
public String emailAddress;
#Required
public String password;
}
The method getLastCommentsByUsers() is supposed to return all comments by the users in the users parameter, but I always get an empty List back. The reason that Commment is a separate collection is to be able to retrieve last X Comments by certain users across their associated Posts, which isn't possible if the Comment is embedded in the Post collection.
Is there something wrong with my query (should I be using something other than hasAnyOf), or is it a problem with the relationship mapping - should I be using ObjectId instead?
I use the in() method with a list or set and its working perfectly. Here's a snippet:
List<String> keywordList;
List<Product> products = Product.find().field("keywords").in(keywordList).asList();
This should work for collection of embedded or references too.
You should use List<Key<SiteUser>> to query:
public static List<Comment> getLastCommentsByUsers(final List<SiteUser> users) {
final Query<Comment> query ds().createQuery(Comment.class);
query.field(commenter).hasAnyOf(toKeys(users)); // convert to keys
return query.asList();
}
public static List<Key<SiteUser>> toKeys(List<SiteUser> users) {
List<Key<SiteUser>> keys = new ArrayList<Key<SiteUser>>();
for(SiteUser user: users) {
keys.add(ds().getMapper().getKey(user));
}
return keys;
}
Or you can just get the keys by:
List<Key<SiteUser>> keys = ds().createQuery(SiteUser.class).query().filter(...).asKeyList();