How To Apply Example Matcher in JPA custom findAll query - java

hi I am trying to make a JPA custom query call using Examplematcher . Below is the code i am trying . If i get the stateId i need to use it in query condition otherwise i need pull all counties irrespective of state . I dont want to make two repo calls like to achieve it in a single repo call .
StateCountyId stateCountyId = new StateCountyId();
StateCounty stateCounty = new StateCounty();
if(null != stateId){
stateCountyId.setStateId(stateId);
}
stateCounty.setStateCountyId(stateCountyId);
return countiesRepository.findAllByStateCountyIdStateIdOrderByStateCountyIdCountyNameAsc
(Example.of(stateCounty, ExampleMatcher.matchingAll().withIgnoreCase()))
.stream().map(this::countyAsDropDown).collect(Collectors.toList());
#Repository
public interface CountiesRepository extends JpaRepository<StateCounty, StateCountyId> {
List<StateCounty> findAllByStateCountyIdStateIdOrderByStateCountyIdCountyNameAsc(Example
stateId);
}
#Entity
#Builder
public class StateCounty implements Serializable {
#EmbeddedId
StateCountyId stateCountyId;
#Column(name = "CODE_NBR")
private String codeNbr;
}
#Embeddable
#EqualsAndHashCode
public class StateCountyId implements Serializable {
#Column(name = "STATE_ID")
private String stateId;
#Column(name = "COUNTY_NAME")
private String countyName;
}
If i make the repo call with just string stateId like below its working , but if stateId is empty it will bring back empty result , i need all need to comeback .
countiesRepository.findAllByStateCountyIdStateIdOrderByStateCountyIdCountyNameAsc
(stateId)
.stream().map(this::countyAsDropDown).collect(Collectors.toList());

Use a Specification like this:
#Repository
public interface CountiesRepository extends JpaRepository<StateCounty, StateCountyId> {
List<StateCounty> findAllByStateCountyIdStateIdOrderByStateCountyIdCountyNameAsc(Specification<StateCounty> specification);
}
countiesRepository.findAllByStateCountyIdStateIdOrderByStateCountyIdCountyNameAsc(
(root, query, cb) -> {
if (stateId == null) return null;
return cb.equal(root.get("stateCountyId").get("stateId"), stateId);
}
)
.stream().map(this::countyAsDropDown).collect(Collectors.toList());

Related

ObjectBox: Get objects with specific relations

Expect the two entities Movie and Genre:
#Entity
public class Movie {
#Id
private long id;
private String name;
private ToMany<Genre> genres;
[...]
}
#Entity
public class Genre {
#Id
private long id;
private String name;
[...]
}
We all know how to create a relation and save it:
Movie movie = new Movie();
movie.setTitle("Star Wars");
movie.getGenres().add(new Genre("Sci-Fi");
box.put(movie);
but is there a possibility to query all Movie-objects with a specific Genre? Like
Box<Movie> box = boxStore.boxFor(Movie.class);
Query query = box.query()
.equal(Genre_.name, "Sci-Fi") // note that I want to query the Movie-Box with Genre-properties
.build();
List movies = query.find();
My goal is to find all movies with a specific genre in a simple way. Does anyone know how to do it or do I have to query all movies and filter the result on my own? Or do I have to adapt my entities in another way?
Update:
I prepared the correct marked answer below to a working example:
final Genre genreSciFi = genreBox.query().equal(Genre_.name, "Sci-Fi").build().findFirst();
List<Movie> filteredMovies = movieBox.query().filter(new QueryFilter<Movie>() {
#Override
public boolean keep(#NonNull Movie entity) {
return entity.getGenres().contains(genreSciFi);
}
}).build().find();
To make the contains-Method work correctly, override equals-Method in your Genre-Entity:
#Override
public boolean equals(Object obj) {
return obj instanceof Genre && ((Genre) obj).getId() == id && ((Genre) obj).getName().equals(name);
}
Unfortunately, this part of the API is not exposed in Java yet. We want to refactor the Query API very soon.
Until this is ready, you can workaround using query filtering. Example using Java/Kotlin-ish code for brevity:
Query query = movieBox.query().filter(movie -> {
return genres.contains(genre -> {
return "Sci-Fi".equals(genre.getName())
}
}).build()
(Will make it similar in Java with the next update.)

Query map value with SpringData and QueryDSL-JPA

I got the following data structure
#Entity
public class Publication {
private Map<Integer, Author> authors;
// more stuff
}
#Entity
public class Author {
private String name;
// more stuff
}
I'm looking for a query dsl predicate which gives my all publication where any Author.name contains a certain String e.g. "Hans"
I tried:
QPublication publication = QPublication.publication;
QAuthor author = QAuthor.author;
publication.authors.containsValue(JPAExpressions.selectFrom(author).where(author.lastName.containsIgnoreCase("Hans")));
But this complains if there is more than one author containing "Hans" in the name. Is there somenting like publication.authors.anyValue().name.equalsIgrnoreCase("Hans") like there is for a Set?
I found a solution with a join and a custom repository implementation:
public class PublicationRepositoryImpl extends QueryDslRepositorySupport
implements PublicationRepositoryCustom {
#Override
public Page<Publication> findByAnyAuthorName(String name, PageRequest pageRequest) {
QPublication publication = QPublication.publication;
JPQLQuery<Publication> query = from(publication);
QAuthor author = QAuthor.author;
query.join(publication.authors, author);
query.where(author.name.containsIgnoreCase(name);
JPQLQuery<Publication> pagedQuery = getQuerydsl()
.applyPagination(pageRequest, query);
return PageableExecutionUtils.getPage(pagedQuery.fetch(), pageRequest,
() -> query.fetchCount());
}
I think you have missed the
BooleanBuilder
in your Repository - try this
QPublication publication = QPublication.publication;
QAuthor author = QAuthor.author;
BooleanBuilder where= new BooleanBuilder();
where.and(publication.author.name.containsIgnoreCase(textToSearch));
//pass the where condition to the desired Repo method
repo.findAll(where);

Spring-Boot Data MongoDB - How to getting a specific nested object for a super specific object

I have the following data model, and I want to get a specific object in the sub list objects, I know it's possible to get the entire list and go through each object and compare with what the search id, but I wonder if it is possible use MongoRepository to do this.
#Document
public class Host {
#Id
private String id;
#NotNull
private String name;
#DBRef
private List<Vouchers> listVoucher;
public Host() {
}
//Getters and Setters
}
And..
#Document
public class Vouchers {
#Id
private String id;
#NotNull
private int codeId;
public Vouchers() {
}
//Getters and Setters
}
The Repository Class:
public interface HostRepository extends MongoRepository<Host, String> {
List<Host> findAll();
Host findById(String id);
Host findByName(String name);
//How to build the correct query ??????????
List<Vouchers> findVouchersAll();
Vouchers findByVouchersById(String hostId, String voucherId);
}
The Controller Class:
#RestController
#RequestMapping(value = "api/v1/host")
public class VoucherController {
#Inject
HostRepository hostRepository;
#RequestMapping(value = "/{hostId}/voucher",method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
#ResponseBody
public List<Vouchers> list() {
return hostRepository.findVouchersAll();
}
#RequestMapping(value = "/{hostId}/voucher/{voucherId}", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
#ResponseBody
public Vouchers getOneVoucher(#PathVariable String hostId, #PathVariable String voucherId) {
Vouchers voucher = hostRepository.findByVouchersById(hostId, voucherId);
if (voucher != null) {
return voucher;
} else {
throw new VoucherNotFoundException(String.format("There is no voucher with id=%s", voucherId));
}
}
}
Thanks in Advance!
I think there is a way to do this although I have not tried this myself but maybe I can shed some light in how I would do it.
Firstly, I would rather use the more flexible way of querying mongodb by using MongoTemplate. MongoTemplate is already included in the Spring Boot Mongodb data library and it looks like you are already using the library so it is not an additional library that you will have to use. In Spring there is a way to #Autowired your MongoTemplate up so it is quick and easy to get the object for completing this task.
With mongoTemplate, you would do something like this:
Query query = new Query();
query.addCriteria(Criteria.where("listVouchers.id").is("1234"));
List<Host> host = mongoTemplate.find(query, Host.class);
Please see docs here: https://docs.mongodb.org/manual/tutorial/query-documents/

How to build a Morphia query on a subset of the properties of a Java Collection's elements?

I'm not being able to query a MongoDB document according to field values of an embedded Java Collection.
I have the following entity:
#Entity
public class StationHistoryEntry // extends ...
{
#Embedded
private Set<SongFeedback> songFeedback = new HashSet<SongFeedback>();
// ...
}
And the following embedded class:
#Embedded
public class SongFeedback // extends ...
{
#Embedded
private FeedbackType feedbackType;
private ObjectId userId;
public enum FeedbackType {
THUMBS_UP, THUMBS_DOWN, STAR;
}
// other properties
}
What I need to do is to find StationHistoryEntries that have SongFeedback with a given userId and feedbackType=STAR.
I've tried the following but didn't succeed when the other SongFeedback properties (the ones not shown on the code snippet because I don't have control over their values) were not null, which happens in production:
public List<StationHistoryEntry> findStarredByUserId(ObjectId userId) {
SongFeedback songFeedback = new SongFeedback(FeedbackType.STAR, userId);
return ds.find(StationHistoryEntry.class)
.filter("songFeedback elem", songFeedback).asList();
}
And I've also tried the following, but it always returns an empty list:
public List<StationHistoryEntry> findStarredByUserId(ObjectId userId) {
Query<StationHistoryEntry> query = ds.createQuery(StationHistoryEntry.class);
query.and(
query.criteria("songFeedback.userId").equal(userId),
query.criteria("songFeedback.feedbackType").equal(FeedbackType.STAR));
return query.asList();
}
If it helps at all, I've created a Github repository with the stripped down code and a unit test: https://github.com/gabrielcs/MorphiaQueryStackOverflow
Any ideas? Thanks!
try this
public List<StationHistoryEntry> findStarredByUserId(ObjectId userId) {
Query<StationHistoryEntry> query = ds.createQuery(StationHistoryEntry.class);
query.and(
query.criteria("songFeedback.userId").equal(userId),
query.criteria("songFeedback.feedbackType").in(FeedbackType.STAR));
return query.asList();
}

ModelMapper integration with Jooq Record

===== POJO =====
// Employee POJO
#JsonInclude(JsonInclude.Include.NON_NULL)
#JsonNaming(PropertyNamingStrategy.LowerCaseWithUnderscoresStrategy.class)
public class Employee implements Serializable {
private Integer id;
private String name;
private Integer companyId;
// assume getters ,setters and serializable implementations.
}
// Company POJO
#JsonInclude(JsonInclude.Include.NON_NULL)
#JsonNaming(PropertyNamingStrategy.LowerCaseWithUnderscoresStrategy.class)
public class Company implements Serializable {
private Integer id;
private String name;
// assume getters ,setters and serializable implementations.
}
// EmployeeVO POJO
#JsonInclude(JsonInclude.Include.NON_NULL)
#JsonNaming(PropertyNamingStrategy.LowerCaseWithUnderscoresStrategy.class)
public class EmployeeVO implements Serializable {
private Employee employee;
private Company company;
// assume getters ,setters and serializable implementations.
}
===== My DAO layer class =====
public List<EmployeeVO> getEmployees(){
// configuring model mapper.
ModelMapper modelMapper = new ModelMapper();
modelMapper.getConfiguration()
.addValueReader(new RecordValueReader())
.setSourceNameTokenizer(NameTokenizers.UNDERSCORE);
//property map configuration.
PropertyMap<Record, EmployeeVO> employeeVOMap = new PropertyMap<Record, EmployeeVO>() {
protected void configure() {
map().getEmployee().setName(this.<String>source("name"));
map().getEmployee()..setId(this.<Integer>source("id"));
map().getCompany().setName(this.<String>source("comp_name"));
map().getCompany().setId(this.<String>source("comp_id"));
}
};
// TypeMap config
modelMapper.createTypeMap(Record.class, EmployeeVO.class);
// adding employeeVOMap .
modelMapper.addMappings(employeeVOMap);
// JOOQ query
List<Field<?>> fields = Lists.newArrayList();
// fields includes, id, name, comp_name, comp_id
SelectJoinStep query = select(dslContext, fields).from(EMPLOYEE)
.join(COMPANY)
.on(COMPANY.ID.equal(EMPLOYEE.COMPANY_ID));
Result<Record> records = query.fetch();
Record record = null;
Iterator<Record> it = records.iterator();
List<EmployeeVO> employeeList= Lists.newArrayList();
while (it.hasNext()) {
record = it.next();
EmployeeVO employeeVOObj =
modelMapper.map(record, EmployeeVO.class);
employeeList.add(employeeVOObj);
}
return employeeList;
}
===== Error log =====
1) Error mapping org.jooq.impl.RecordImpl to com.myportal.bingo.db.model.EmployeeVO
1 error] with root cause
java.lang.ArrayIndexOutOfBoundsException: -1
Note:
ModelMapper throws the above exception when it reaches below method.
private void matchSource(TypeInfo<?> sourceTypeInfo, Mutator destinationMutator)
in ImplicitMappingBuilder.java
sourceTypeInfo.getAccessors() is null.
Any help?
Had the same problem, or at least which looked the same. (You can move directly to my solution in the last paragraph.) Lots of debugging have shown the following:
if accessors on that line (mentioned in your question) are null, then accessors = PropertyInfoSetResolver.resolveAccessors(source, type, configuration) line in TypeInfoImpl class is executed, and the reason of exception in my case was this call:
valueReader.get(source, memberName) at the following piece of code at 'resolveAccessors' method in the PropertyInfoSetResolver class:
if (valueReader == null)
resolveProperties(type, true, configuration, accessors);
else {
NameTransformer nameTransformer = configuration.getSourceNameTransformer();
for (String memberName : valueReader.memberNames(source))
accessors.put(nameTransformer.transform(memberName, NameableType.GENERIC),
new ValueReaderPropertyInfo(valueReader, valueReader.get(source, memberName),
memberName));
which ends up in source.getValue(memberName.toUpperCase()), where source is JOOQ's Record; InvoiceRecord in my case. And - tada - for some reason invoice.getValue("INVOICE_ID") ends up in the exception (no such field and therefore indexOf returns -1 which causes the ArrayIndexOutOfBoundsException), while invoice.getValue("invoice_id") is totally fine.
So else condition (the same piece of code above) wasn't the right way to execute the code, and if case turned out to be ok.
So that's what helped me in my particular case: removing of the row modelMapper.getConfiguration().addValueReader(new RecordValueReader()). Hope this will help you too.

Categories