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.)
Related
I'm not sure how to phrase the question title to be honest, if someone has a suggestion, please let me know.
My use case is this, I have an entity with an account property like so (this is cleaned up to avoid clutter):
#Entity
#Table(name = "report_line", schema = "public")
public class ReportLine extends BaseReportLine {
#ManyToOne
#JoinColumn(name = "report_id")
private Report report;
#ManyToOne
#JoinColumn(name = "account_id")
private Account account;
}
But a DTO that only has an account id / different properties:
public class ImportLineDto {
public String groupName;
public Integer position;
public Integer parentPosition;
public String accountId;
public String name;
public BigDecimal amount;
public List<ImportLineDto> lines = new ArrayList<>();
}
I need to go through / flatten all lines so I can save it to a JPA repository, but there are 2 issues:
Is there a way to create the table line object using the accountId only, without having to look up the account for each line, as that will add a massive amount of unnecessary db calls.
What should I do with the 'lines' on each table object after flattening? Should I set them to null / empty list?
Is there a better way to do this? For once I can actually make changes to the code
Here is what I have so far:
private void saveReport(ImportedResult result) {
Report report = new Report();
...
report.setLines(getReportLinesFromDtoLines(result.lineItems.lines));
ReportRepository.saveAndFlush(report);
}
private List<ReportLine> getReportLinesFromDtoLines(ImportLineDto lines) {
List<ImportLineDto> flatLines = flatMapRecursive(lines).collect(Collectors.toList());
List<ReportLine> reportLines = new ArrayList<>();
for(ImportLineDto line: flatLines) {
ReportLine reportLine = new ReportLine();
reportLine.setItemText(line.name);
reportLine.setAmount(line.amount);
reportLine.setAccount(???);
// how do I set the 'Account' property using the id only, without looking up each account?
reportLines.add(reportLine);
}
return ReportLines;
}
public Stream<ImportLineDto> flatMapRecursive(ImportLineDto item) {
if (item.lines == null) {
return Stream.empty();
}
return Stream.concat(Stream.of(item), item.lines.stream()
.flatMap(this::flatMapRecursive));
}
Follow up:
Just to throw a wrench in there, what if the DTO accountId was not the actual "id" field in the table, but another custom field, I have another situation like that, would it even be possible? I still need the answer the the 1st question however with a standard id.
you may use entityManager.getReference as explained here
reportLine.setAccount(entityManager.getReference(Account.class, line.accountId));
I want to create a dynamodb which has following features
PK: orderId
RK: date
shipped: Y|N
details: <nested json structure>
Point 4 is the one which i am really confused about. If i keep details field as a string and try to store json as string, AWS escape " characters i.e., {"onlineStore" : "283"} becomes {\"onlineStore\": \"283\"}
This get's retrieved properly from dynamodb with details as string mappings but if i have to convert it to a pojo using jackson, I have to take care of those \.
So as an alternative, I thought that i could create details a POJO i.e.,
public class OrderDetail{
private int onlineStore;
// rest of the JSON properties
#JsonCreator
public OrderDetail (#JsonProperty("onlineStore") int onlineStore, ...){
this.onlineStore = onlineStore;
}
}
With the above implementation, i get error that DynamoDBMappingException: Couldn't convert attribte.
The OrderDetail type is a common type which is being used between my JSON REST Response as well so i want to avoid putting DynamoDB specific annotation here.
Now the question is what should be proper way to implement it.
If you are using DynamoDB Mapper class to perform the CRUD operation, you can use the annotation #DynamoDBTypeConvertedJson to save the order details.
DynamoDBTypeConvertedJson - Check this link for more details
DynamoDBMapper class:-
The AWS SDK for Java provides a DynamoDBMapper class, allowing you to
map your client-side classes to DynamoDB tables. To use
DynamoDBMapper, you define the relationship between items in a
DynamoDB table and their corresponding object instances in your code.
The DynamoDBMapper class enables you to access your tables, perform
various create, read, update and delete (CRUD) operations, and execute
queries.
Sample Implementation:-
#DynamoDBTable(tableName = "Order")
public class Order implements Serializable {
private static final long serialVersionUID = -3534650012619938612L;
private String orderId;
private OrderDetail orderDetail;
#DynamoDBHashKey(attributeName = "orderId")
#DynamoDBAutoGeneratedKey
public String getOrderId() {
return orderId;
}
public void setOrderId(String orderId) {
this.orderId = orderId;
}
#DynamoDBTypeConvertedJson
public OrderDetail getOrderDetail() {
return orderDetail;
}
public void setOrderDetail(OrderDetail orderDetail) {
this.orderDetail = orderDetail;
}
}
public class OrderDetail implements Serializable {
private static final long serialVersionUID = 7312390212027563305L;
private Integer onlineStore;
public Integer getOnlineStore() {
return onlineStore;
}
public void setOnlineStore(Integer onlineStore) {
this.onlineStore = onlineStore;
}
#Override
public String toString() {
return "OrderDetail [onlineStore=" + onlineStore + "]";
}
}
public Order loadOrder(String orderId) {
DynamoDBMapper dynamoDBMapper = new DynamoDBMapper(dynamoDBClient);
Order order = dynamoDBMapper.load(Order.class, orderId,
new DynamoDBMapperConfig(DynamoDBMapperConfig.ConsistentReads.CONSISTENT));
System.out.println("Order : " + order.toString());
System.out.println("Order Id : " + order.getOrderId());
System.out.println("Order Detail : " + order.getOrderDetail());
System.out.println("Online store : " + order.getOrderDetail().getOnlineStore());
return order;
}
Output:-
Order Id : 0beced28-f1de-4c44-8094-6de687d25e97
Order Detail : OrderDetail [onlineStore=1]
Online store : 1
Data in DDB:-
As you mentioned, the order detail will be stored with escape characters. However, when you get the data using DynamoDB mapper, it will be in deserialized form (i.e. as POJO object).
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);
I'm still new to Java but I'm having problems with an application framework when I turn off Solr (To see changes made to database) I get an error because one of the classes is still using using an attribute that no longer exists.
Part 1: I have no idea why this was changed, and doesn't use a ManyToMany anymore, most likely a workaround for some limitation I guess.
public class CategoryImpl implements Category, Status, AdminMainEntity {
#OneToMany(targetEntity = CategoryProductXrefImpl.class, mappedBy = "categoryProductXref.category")
protected List<CategoryProductXref> allProductXrefs = new ArrayList<CategoryProductXref>(10);
Part 2:
public class CategoryProductXrefImpl implements CategoryProductXref {
#EmbeddedId
CategoryProductXrefPK categoryProductXref = new CategoryProductXrefPK();
public CategoryProductXrefPK getCategoryProductXref() {
return categoryProductXref;
}
public void setCategoryProductXref(CategoryProductXrefPK categoryProductXref) {
this.categoryProductXref = categoryProductXref;
}
public void setCategoryProductXref(CategoryProductXrefPK categoryProductXref) {
this.categoryProductXref = categoryProductXref;
}
public Category getCategory() {
return categoryProductXref.getCategory();
}
public void setCategory(Category category) {
categoryProductXref.setCategory(category);
}
public Product getProduct() {
return categoryProductXref.getProduct();
}
public void setProduct(Product product) {
categoryProductXref.setProduct(product);
}
Part 3:
#Embeddable
public class CategoryProductXrefPK implements Serializable {
#ManyToOne(targetEntity = CategoryImpl.class, optional=false)
#JoinColumn(name = "CATEGORY_ID")
protected Category category = new CategoryImpl();
#ManyToOne(targetEntity = ProductImpl.class, optional=false)
#JoinColumn(name = "PRODUCT_ID")
protected Product product = new ProductImpl();
Problem was in ProductDao
public List<Product> readFilteredActiveProductsByCategory(Long categoryId, Date currentDate,
ProductSearchCriteria searchCriteria) {
// Set up the criteria query that specifies we want to return Products
CriteriaBuilder builder = em.getCriteriaBuilder();
CriteriaQuery<Product> criteria = builder.createQuery(Product.class);
// The root of our search is Category since we are browsing
Root<CategoryImpl> category = criteria.from(CategoryImpl.class);
// We want to filter on attributes from product and sku
Join<Category, Product> product = category.join("allProducts"); <--Problem Occurs Here
Join<Product, Sku> sku = product.join("defaultSku");
// Product objects are what we want back
criteria.select(product);
I've tried this:
Join<CategoryImpl, CategoryProductXrefImpl> catXref = category.join("allProductXrefs");
Join<CategoryProductXrefImpl, Product> product = catXref.join("product");
Join<Product, Sku> sku = product.join("defaultSku");
I've tried it by Implementation, tried with a third join like:
Join<CategoryProductXrefImpl, CategoryProductXrefPK>,
tried using CategoryProductXrefPK instead of XrefImpl, Tried using CategoryProductXref as a second Root. I've spent a few days on this searching and trying different things. I ran out of ideas.
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();
}