QueryDSL - hierarchical entities - spring

Is there a way to force initialization of the following structure?
class Dataset {
private Dataset parent;
private String name;
}
QDataset.datasetEntity.parent.name.isNotNull(); // parent == null, NullPointerException
I assume complete initialization is not possible as mindlessly initializing parent of a parent of a parent etc will eventually cause StackOverflowException. But is there a way or trick to make it working? In fact I only need the parent to be initialized properly so only 1 level up the hierarchy.
As pointed out in the comments this is possible using #QueryInit. In my case something like below helped.
class Dataset {
#QueryInit({"parent.name"})
private Dataset parent;
private String name;
}

Related

Convert Set of Parent to List of Child using Java 8 streams

So I am trying to convert Set of Parent to List of Child using Java 8 streams.
Here is the parent class:
public class Parent<T extends GenericModel> {
private String href;
private String rel;
private String title;
private ResourceStatus status;
private Parent source;
}
And here is the Child class:
public class Child extends Parent<SomeClass> {
private String category;
private String type;
}
Now I already have Set<Parent<SomeClass>> which I want to convert to a List<Child>. I don't want to go the primitive way, but instead use Java 8 streams to achieve this.
Any help is appreciated.
You can just stream and cast. If you're certain that they're of the right type then you can remove the filter at the risk of class cast exceptions.
Often exceptions are better than silently ignoring problems with your assumptions, but I'll leave it up to you to decide which is better for your case.
final List<Child> children = parents.stream()
.filter(Child.class::isInstance) //optional
.map(Child.class::cast)
.collect(Collectors.toList());

Challenge Persisting Complex Entity using Spring Data JDBC

Considering the complexities involved in JPA we are planning to use Spring Data JDBC for our entities for its simplicity. Below is the sample structure and we have up to 6 child entities. We are able to successfully insert the data into various of these entities with proper foreign key mappings.
Challenge:- We have a workflow process outside of this application that periodically updates the "requestStatus" in the "Request" entity and this is the only field that gets updated after the Request is created. As with spring data JDBC, during the update it deletes all referenced entities and recreates(inserts) it again. This is kind of a heavy operation considering 6 child entities. Are there any workaround or suggestion in terms of how to handle these scenarios
#Table("Request")
public class Request {
private String requestId; // generated in the Before Save Listener .
private String requestStatus;
#Column("requestId")
private ChildEntity1 childEntity1;
public void addChildEntity1(ChildEntity1 childEntityobj) {
this.childEntity1 = childEntityobj;
}
}
#Table("Child_Entity1")
public class ChildEntity1 {
private String entity1Id; // Auto increment on DB
private String name;
private String SSN;
private String requestId;
#MappedCollection(column = "entity1Id", keyColumn = "entity2Id")
private ArrayList<ChildEntity2> childEntity2List = new ArrayList<ChildEntity2>();
#MappedCollection(column = "entity1Id", keyColumn = "entity3Id")
private ArrayList<ChildEntity3> childEntity3List = new ArrayList<ChildEntity3>();
public void addChildEntity2(ChildEntity2 childEntity2obj) {
childEntity2List.add(childEntity2obj);
}
public void addChildEntity3(ChildEntity3 childEntity3obj) {
childEntity3List.add(childEntity3obj);
}
}
#Table("Child_Entity2")
public class ChildEntity2 {
private String entity2Id; // Auto increment on DB
private String partyTypeCode;
private String requestId;
}
#Table(Child_Entity3)
public class ChildEntity3 {
private String entity3Id; // Auto increment on DB
private String PhoneCode;
private String requestId;
}
#Test
public void createandsaveRequest() {
Request newRequest = createRequest(); // using builder to build the object
newRequest.addChildEntity1(createChildEntity1());
newRequest.getChildEntity1().addChildEntity2(createChildEntity2());
newRequest.getChildEntity1().addChildEntity3(createChildEntity3());
requestRepository.save(newRequest);
}
The approach you describe in your comment:
Have a dedicated method performing exactly that update-statement is the right way to do this.
You should be aware though that this does ignore optimistic locking.
So there is a risk that the following might happen
Thread/Session 1: reads an aggregate.
Thread/Session 2: updates a single field as per your question.
Thread/Session 1: writes the aggregate, possibly with other changes, overwriting the change made by Session 2.
To avoid this or similar problems you need to
check that the version of the aggregate root is unchanged from when you loaded it, in order to guarantee that the method doesn't write conflicting changes.
increment the version in order to guarantee that nothing else overwrites the changes made in this method.
This might mean that you need two or more SQL statements which probably means you have to fallback even more to a full custom method where you implement this, probably using an injected JdbcTemplate.

Replacing entire contents of spring-data Page, while maintaining paging info

Using spring-data-jpa and working on getting data out of table where there are about a dozen columns which are used in queries to find particular rows, and then a payload column of clob type which contains the actual data that is marshalled into java objects to be returned.
Entity object very roughly would be something like
#Entity
#Table(name = "Person")
public class Person {
#Column(name="PERSON_ID", length=45) #Id private String personId;
#Column(name="NAME", length=45) private String name;
#Column(name="ADDRESS", length=45) private String address;
#Column(name="PAYLOAD") #Lob private String payload;
//Bunch of other stuff
}
(Whether this approach is sensible or not is a topic for a different discussion)
The clob column causes performance to suffer on large queries ...
In an attempt to improve things a bit, I've created a separate entity object ... sans payload ...
#Entity
#Table(name = "Person")
public class NotQuiteAWholePerson {
#Column(name="PERSON_ID", length=45) #Id private String personId;
#Column(name="NAME", length=45) private String name;
#Column(name="ADDRESS", length=45) private String address;
//Bunch of other stuff
}
This gets me a page of NotQuiteAPerson ... I then query for the page of full person objects via the personIds.
The hope is that in not using the payload in the original query, which could filtering data over a good bit of the backing table, I only concern myself with the payload when I'm retrieving the current page of objects to be viewed ... a much smaller chunk.
So I'm at the point where I want to map the contents of the original returned Page of NotQuiteAWholePerson to my List of Person, while keeping all the Paging info intact, the map method however only takes a Converter which will iterate over the NotQuiteAWholePerson objects ... which doesn't quite fit what I'm trying to do.
Is there a sensible way to achieve this ?
Additional clarification for #itsallas as to why existing map() will not suffice..
PageImpl::map has
#Override
public <S> Page<S> map(Converter<? super T, ? extends S> converter) {
return new PageImpl<S>(getConvertedContent(converter), pageable, total);
}
Chunk::getConvertedContent has
protected <S> List<S> getConvertedContent(Converter<? super T, ? extends S> converter) {
Assert.notNull(converter, "Converter must not be null!");
List<S> result = new ArrayList<S>(content.size());
for (T element : this) {
result.add(converter.convert(element));
}
return result;
}
So the original List of contents is iterated through ... and a supplied convert method applied, to build a new list of contents to be inserted into the existing Pageable.
However I cannot convert a NotQuiteAWholePerson to a Person individually, as I cannot simply construct the payload... well I could, if I called out to the DB for each Person by Id in the convert... but calling out individually is not ideal from a performance perspective ...
After getting my Page of NotQuiteAWholePerson I am querying for the entire List of Person ... by Id ... in one call ... and now I am looking for a way to substitute the entire content list ... not interively, as the existing map() does, but in a simple replacement.
This particular use case would also assist where the payload, which is json, is more appropriately persisted in a NoSql datastore like Mongo ... as opposed to the sql datastore clob ...
Hope that clarifies it a bit better.
You can avoid the problem entirely with Spring Data JPA features.
The most sensible way would be to use Spring Data JPA projections, which have good extensive documentation.
For example, you would first need to ensure lazy fetching for your attribute, which you can achieve with an annotation on the attribute itself.
i.e. :
#Basic(fetch = FetchType.LAZY) #Column(name="PAYLOAD") #Lob private String payload;
or through Fetch/Load Graphs, which are neatly supported at repository-level.
You need to define this one way or another, because, as taken verbatim from the docs :
The query execution engine creates proxy instances of that interface at runtime for each element returned and forwards calls to the exposed methods to the target object.
You can then define a projection like so :
interface NotQuiteAWholePerson {
String getPersonId();
String getName();
String getAddress();
//Bunch of other stuff
}
And add a query method to your repository :
interface PersonRepository extends Repository<Person, String> {
Page<NotQuiteAWholePerson> findAll(Pageable pageable);
// or its dynamic equivalent
<T> Page<T> findAll(Pageable pageable, Class<T>);
}
Given the same pageable, a page of projections would refer back to the same entities in the same session.
If you cannot use projections for whatever reason (namely if you're using JPA < 2.1 or a version of Spring Data JPA before projections), you could define an explicit JPQL query with the columns and relationships you want, or keep the 2-entity setup. You could then map Persons and NotQuiteAWholePersons to a PersonDTO class, either manually or (preferably) using your object mapping framework of choice.
NB. : There are a variety of ways to use and setup lazy/eager relations. This covers more in detail.

Why Entitys uninitialized collection is initialized automatically only for Entities persisted before current transaction?

(Please feel free to edit the title after reading this question)
I have quite simple #ManyToOne bidirectional mapping between entities Parent and Child.
The list of children Collection<Child> children in Parent is never initialized so it should be null.
When using EntityManager.find(...) for previously persisted Parent and then getting the list from that Parent gives ArrayList even there are no children yet with this Parent and it is fine.
However if persisting or merging a new Parent in the same transaction collection of children will be null even if the persisted/merged Parent is fetched again with EntityManager.find(...).
So i wonder this different behavior and if it is happening only in my environment.
I assume it has something to do with the caching of entities: entity is found from cache and it is returned instead of fetching it from db AND the initialization of empty collections will happen only when fetched from db, maybe depending on the JPA implementation.
Is my assumption even near the truth and if not what is the reason ?
Entities and test cases below. My test environment listed in tags.
// using lombok
#Slf4j
#RunWith(Arquillian.class)
public class NoPersistTest {
#PersistenceContext
private EntityManager em;
#Deployment
public static final WebArchive deploy() {
WebArchive wa = ShrinkWrap.create(WebArchive.class, "test.war")
.addAsWebInfResource("test-persistence.xml", "persistence.xml").addClasses(Parent.class, Child.class);
return wa;
}
#Test
#Transactional
public void testWithPreviouslyPersistedParent() {
Parent parent = em.find(Parent.class, 1); // has no children in db
// before
Child child = new Child();
child.setParent(parent);
parent.getChildren().add(child);
log.info("type of Collection<Child> is {}", parent.getChildren().getClass().getName());
// above logs "type of Collection<Child> is
// org.apache.openjpa.util.java$util$ArrayList$proxy"
}
#Test(expected = NullPointerException.class)
#Transactional
public void testPersistingParentInSameTransaction() {
Parent parent = new Parent();
em.persist(parent);
Parent parent2 = em.find(Parent.class, parent.getId());
Child child = new Child();
child.setParent(parent2);
log.info("Collection<Child> is {}", parent2.getChildren());
// above logs Collection<Child> is null
parent2.getChildren().add(child);
}
#Test(expected = NullPointerException.class)
#Transactional
public void testMergingParentInSameTransaction() {
Parent parent = new Parent();
parent = em.merge(parent);
Parent parent2 = em.find(Parent.class, parent.getId());
Child child = new Child();
child.setParent(parent2);
log.info("Collection<Child> is {}", parent2.getChildren());
// logs Collection<Child> is null
parent2.getChildren().add(child);
}
}
#Entity #Getter #Setter
public class Parent {
#Id #GeneratedValue(strategy=GenerationType.IDENTITY)
private Long id;
#OneToMany(mappedBy="parent", cascade=CascadeType.ALL, orphanRemoval=true)
private Collection<Child> children;
private Date created = new Date(); // just to have something to persist
}
#Entity #Getter #Setter
public class Child {
#Id #GeneratedValue(strategy=GenerationType.IDENTITY)
private Long id;
private Date created = new Date(); // just to have something to persist
#ManyToOne(optional=false)
private Parent parent;
}
If you create the Parent the collection is not initialized because you don't do it. And also when persisting the Parent JPA will leave the collection as it is.
But when you read the Parent with Hibernate the collection will contain a proxy because toMany relationships are fetched LAZY and this proxy is used to fetch the children on demand.
My recommendation is to always initialize collection to avoid NullPointerExceptions. That's good programming style.
The answer below is correct, I'd just like to add some more information as I was asked to in a comment elsewhere.
JPA uses caching to avoid database hits where possible, and where a database hit is still required, caching avoids the cost of rebuilding objects and allows maintaining Identity - ensuring you get back the same A instance when traversing A->B->A circular references.
When you persist an entity, you are placing it in the EntityManager cache as a managed entity - calling find on that EntityManager will return you the same exact instance you just passed in.
A initialA = new A();
A managedA = em.persist(initialA);
managedA==initialA
The persist call itself will not change anything within your entity (except possibly the ID if a sequence that allows preallocation to be used), so any null references will still be null.
Eventually the transaction commits and depending on your provider, entities can be cached in a second level cache. I'll assume you aren't using it for the sake of brevity; unless you force the EM to refresh this instance (flush first if its a new one!) or read it in a separate EntityManager, you will always get that same instance back with any null references.
If you refresh it or otherwise cause it to be reloaded, your JPA provider is required to set everything in the object as it is in the database, according to your mappings. Since null isn't a persistable state for a collection mapping, that means it will either eagerly fetch your references, or place proxies in there for lazy relationships, causing you to find an empty collection.

Spring JPA repository how to write a query

I have a User class, that is identified by id, and Skills class, that has its own id field, and also references User.
public class User {
#Id
#GeneratedValue
private int id;
#JsonIgnore
#OneToOne(mappedBy = "user")
private SoftSkills softSkills;
}
the other one has
#Entity
public class SoftSkills {
#Id
#GeneratedValue
private int id;
#OneToOne
#JoinColumn
private User user;
}
Is there a simple way to write a query, implementing the JPARepository, that would search the SoftSkills class by using user.id field as a parameter and return a SoftSkills object as a result?
You can, from the documentation:
Property expressions can refer only to a direct property of the managed entity, as shown in the preceding example. At query creation time you already make sure that the parsed property is a property of the managed domain class. However, you can also define constraints by traversing nested properties. Assume a Person has an Address with a ZipCode. In that case a method name of
List<Person> findByAddressZipCode(ZipCode zipCode);
creates the property traversal x.address.zipCode. The resolution algorithm starts with interpreting the entire part (AddressZipCode) as the property and checks the domain class for a property with that name (uncapitalized). If the algorithm succeeds it uses that property. If not, the algorithm splits up the source at the camel case parts from the right side into a head and a tail and tries to find the corresponding property, in our example, AddressZip and Code. If the algorithm finds a property with that head it takes the tail and continue building the tree down from there, splitting the tail up in the way just described. If the first split does not match, the algorithm move the split point to the left (Address, ZipCode) and continues.
So this will do the trick:
SoftSkills findByUserId(int id);
Reference; Spring Data JPA Documentation

Resources