Spring Data JPA query building via "query creation" method for #embeddedID - java

I'm struggling to find any type of documentation on how to query more complex attributes in my models.
For example I have
public class MyEmbedded{
#EmbeddedID
private MyEmbeddedPK embeddedPK;
}
#Embeddable
public class MyEmbeddedPK{
private Integer age;
private Integer zip;
}
In my repository I am implementing the CrudRepository and would expect to be able to do
public List<MyEmbedded> findByageAndZip(String age, String zip);
But that doesn't seem to work. The documentation doesn't really say anything regarding #EmbeddedId's. The same goes for querying a #OneToMany attribute, I never found anything for that.
Documentation I am referencing. http://docs.spring.io/spring-data/jpa/docs/current/reference/html/#repository-query-keywords
Is there any better documentation on how this query creation works?

I'm not sure if Spring Data Jpa supports this functionality and it seems a bit complex to query based on embedded id properties as it can equally be applicable to state fields of the enclosing entity itself. But this can be achieved easily with JP QL by specifying it with Query and #Param
#Query("SELECT m FROM MyEmbedded m WHERE m.embeddedPK.age = :age AND m.embeddedPK.zip = :zip")
public List<MyEmbedded> findByageAndZip(#Param("age") String age, #Param("zip") String zip);
Also don't forget to specify your repository with the following signature as Spring data runtime needs to know the actual type of the ID class.
#Repository
public interface MyEmbeddedRepository extends CrudRepository<MyEmbedded, MyEmbeddedPK> {..}

I think I found my answer, oddly enough it was in the documentation but I just didn't pick up on it. You just need to combine the properties together via camel case. I could have sworn I tried this but apparently I had my cases messed up.
http://docs.spring.io/spring-data/jpa/docs/current/reference/html/#repositories.query-methods.query-property-expressions
Section 4.4.3. Property expressions
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 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.

Related

Spring Boot Hibernate mapping set of enums to boolean columns

I have a Form enitity in Spring Boot application:
#Data
#Entity
public class Form {
#Id
private String caseId;
private String leadingUnit;
private Set<Addiction> currentAddictions;
}
Addiction is an enum:
public enum Addiction {
Alcohol, Nicotine, Drugs
}
And I would like to map the whole form to one table in database. With the following columns:
caseId, leadingUnit, currentAddictionsAlcohol, currentAddictionsNicotine and currentAddictionsDrugs.
I would like the currentAddictions* columns to bo boolean indicating only if the current addiction is present in the set. I was looking at custom UserTypes but I have no idea how to do it. Is there a simple solution for this?
This doesn't feel right (map a set to boolean columns). But here are a few options. Both options I can think require the the individual booleans to be mapped in the entity class. I want to be super clear: the correct way to map this is to have a table of addictions, and then link the tables. If later you want to add an extra addiction, this just becomes inserting a new row rather than changing the structure of the database.
Change the interface of your class to have a get/set for each boolean value, each set method then adds or remove the value from the Set that is not persisted. The Set can only be modified from the set boolean methods. And you probably need to use a #PostLoad lifecycle method to populate the set after loading an entity.
Basically the opposite: You only expose the Set with a couple of helper methods addAddiction(Addiction) and removeAddiction(Addiction). These methods internally update boolean values that are not exposed in any way. So from the interface of the class, you only have a Set.
Last option, but it's a bit of an advanced approach (so more voodoo), is to use a CompositeUserType. You can find some examples of how to create this class in here too: example 1, example 2.
My recommendation would be to stay away from the CustomUserType as things might be really complicated if, for example, you want to query all the rows with addiction = Alcohol. And please do consider mapping the addictions to their own table. Your future self (or future maintainers) will appreciate it.

Spring boot jpa table hierarchy

I have just one table in the database named Student. Below is the jpa entity against it in my code:
#Entity
#Table(name = "student")
public class Student{
#Id
private Long id;
#Column
private String firstName;
#Column
#Embedded
#JsonUnwrapped
private School school;
}
public class School{
private Integer grade;//eg. 2nd, 3rd
private String type; //eg. primary, secondary
}
Until now, the code was only to fetch all the students and their data or to fetch a particular student. So, the DB schema was such. But now, we have a new functionality wherein we need to search based on a particular grade and fetch all students for that particular grade. OR fetch all students for a particular school type eg. all students in the primary school. So, the requirement has totally reversed such that we need to send the return the below schema to the front end:
class SchoolResponseDTO{
private String schoolType;
private List<Integer> grades;
}
class Grade{
private Integer id;
private List<Integer> studentId;
}
To be a bit verbose, from now on, we need to find all the school types, then, all the grades in that school types, and then, all the students in that school type and grade.
Until now, we were using the Spring JpaRepository for our requirements. I feel this new requirement would require custom queries and I dont think this can be handled in a straight forward manner using the JPARepository. I just need to know what do you think. Can this be done without custom sql queries?
You can use Spring Data and use something like this query property expressions. From the doc:
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. Consider the following method signature:
List<Person> findByAddressZipCode(ZipCode zipCode);
Assume a Person has an Address with a ZipCode. In that case, the method creates the property traversal x.address.zipCode. The resolution algorithm starts by 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 continues building the tree down from there, splitting the tail up in the way just described. If the first split does not match, the algorithm moves the split point to the left (Address, ZipCode) and continues.
Although this should work for most cases, it is possible for the algorithm to select the wrong property. Suppose the Person class has an addressZip property as well. The algorithm would match in the first split round already, choose the wrong property, and fail (as the type of addressZip probably has no code property).
To resolve this ambiguity you can use _ inside your method name to manually define traversal points. So our method name would be as follows:
List<Person> findByAddress_ZipCode(ZipCode zipCode);
Because we treat the underscore character as a reserved character, we strongly advise following standard Java naming conventions (that is, not using underscores in property names but using camel case instead).
Check the link
https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#repositories.query-methods.query-property-expressions
Hope it helps.

Spring Data MongoDB template converts to wrong class

Using Spring 5.0.6 and Spring-Data-Mongo 2.0.7, I have an issue when fetching entities being transformed into the wrong class. See the following simplified scenario:
Entity setup:
public class PersistableObject {
#Id #Field("_id") private String id;
}
#Document(collection = "myapp_user")
public class User extends PersistableObject {...}
public class RealUser extends User {...}
public class VirtualUser extends User {...}
So, there is a common MongoDB collection storing both types of User, discriminated by the automatically added _class property.
Furthermore, there is a Repository into which the MongoTemplate is injected.
#Autowired
private org.springframework.data.mongodb.core.MongoTemplate template;
Everything fine, so far. Now, if I want to fetch all documents that contain a RealUser, I could call this
template.findAll(RealUser.class)
I'd expect the template to find all documents that have the discriminator property _class set to com.myapp.domain.RealUser.
But this doesn't work as expected. I even get all VirtualUsers, as well, put into objects of type RealUser with all VirtualUser-specific properties missing, and all RealUser-specific properties set to null.
Furthermore, when I go and save a User, which is actually a VirtualUser in MongoDB, but has been squeezed into a RealUser class, Spring would change the _class-property to the wrong type, magically converting a VirtualUser into a RealUser.
So both methods here would load the entire collection and squeeze all objects into the specified class, even if it is the wrong one:
template.findAll(VirtualUser.class)
template.findAll(RealUser.class)
This behavior is probably not desired, or if so, then it is extremely misleading and harmful. You can easily shred your whole data with this.
Can anyone shed some light on this?
I've created a ticket at Spring's Jira. Find Olivers comment below:
The method actually works as expected but I agree that we need to
improve the JavaDoc. The method is basically specified as "Load the
documents the given type is configured to persisted in and map all of
them (hence the name) to the given type". The type given to it is not
used as a type mapping criteria at the same time. Every restriction
you want to apply on the documents returned needs to be applied
through a Query instance, which exposes a ….restrict(…) method that
allows to only select documents that carry type information.
The reason that findAll works the way it works is that generally
speaking – i.e. without an inheritance scenario in place – we need to
be able to read all documents, even if they don't carry any type
information. Assume a collection with documents representing people
that have not been written using Spring Data. If a
findAll(Person.class) applied type restrictions, the call would return
no documents even if there were documents present. Unfortunately we
don't know if the collection about to be queried carries type
information. In fact, some documents might carry type information,
some might not. The only way to reasonably control this, is to let the
user decide, which she can by either calling Query.restrict(…) or not.
The former selects documents with type information only, the latter.
As I said, I totally see that the JavaDoc might be misleading here.
I'm gonna use this ticket to improve on that. Would love to hear if
the usage of Query.restict(…) allows you to achieve what you want.

Spring Data Neo4j and queries

I'm trying to write a query that returns a fairly large amount of data (200ish nodes). The nodes are very simple:
public class MyPojo{
private String type;
private String value;
private Long createdDate;
...
}
I originally used the Spring Data Neo4j template interface, but found that it was very slow after around 100 nodes were returned.
public interface MyPojoRepository extends GraphRepository<MyPojo> {
public List<MyPojo> findByType(String type);
}
I turned on debugging to see why it was so slow, and it turned out SDN was making a query for each node's labels. This made sense, as I understand SDN it needs the labels to do its duck-typing. However, Cypher returns all pertinent data in one go so there's no need for this.
So, I tried rewriting it as a Cypher query instead:
public interface MyPojoRepository extends GraphRepository<MyPojo> {
#Query("MATCH(n:MyPojo) WHERE n.type = {0} RETURN n")
public List<MyPojo> findByType(String type);
}
This had the same problem. I dug a little deeper, and while this query returned all node data in one go, it leaves out the labels. There is a way to get them, which works in the Neo4j console so I tried it with SDN:
"MATCH(n:MyPojo) WHERE n.type = {0} RETURN n, labels(n)"
Unfortunately, this caused an exception about having multiple columns. After looking through the source code, this makes sense because Neo4j returns a map of returned columns which in my case looked like: n, labels(n). SDN would have no way of knowing that there was a label column to read.
So, my question is this: is there a way to provide labels as part of the Cypher query to prevent needing to query again for each node? Or, barring that, is there a way to feed SDN a Node containing labels and properties and convert it to a POJO?
Note: I realize that the SDN team is working on using Cypher completely under the hood in a future release. As of now, a lot of the codebase uses the old (and, I believe, deprecated) REST services. If there is any future work going on that would affect this, I would be overjoyed to hear about it.
You're right it would be solvable for the simple use-case and should also solved.
Unfortunately the current APIs don't return the labels as part of the node so we would have to rewrite the inner workings to generate the extra meta-information and return all of that correctly.
One idea is to use RETURN {id:id(n), labels:labels(n), data:n)} as n for the full representation.
The problem is this breaks with user defined queries.
Not sure when and how we can schedule that work. Feel free to raise it as JIRA issue or watch/upvote any related issues.

Extending JPA entity data at runtime

I need to allow client users to extend the data contained by a JPA entity at runtime. In other words I need to add a virtual column to the entity table at runtime. This virtual column will only be applicable to certain data rows and there could possibly be quite a few of these virtual columns. As such I don't want to create an actual additional column in the database, but rather I want to make use of additional entities that represent these virtual columns.
As an example, consider the following situation. I have a Company entity which has a field labelled Owner, which contains a reference to the Owner of the Company. At runtime a client user decides that all Companies that belong to a specific Owner should have the extra field labelled ContactDetails.
My preliminary design uses two additional entities to accomplish this. The first basically represents the virtual column and contains information such as the field name and type of value expected. The other represents the actual data and connects an entity row to a virtual column. For example, the first entity might contain the data "ContactDetails" while the second entity contains say "555-5555."
Is this the right way to go about doing this? Is there a better alternative? Also, what would be the easiest way to automatically load this data when the original entity is loaded? I want my DAO call to return the entity together with its extensions.
EDIT: I changed the example from a field labelled Type which could be a Partner or a Customer to the present version as it was confusing.
Perhaps a simpler alternative could be to add a CLOB column to each Company and store the extensions as an XML. There is a different set of tradeoffs here compared to your solution but as long as the extra data doesn't need to be SQL accessible (no indexes, fkeys and so on) it will probably be simple than what you do now.
It also means that if you have some fancy logic regarding the extra data you would need to implement it differently. For example if you need a list of all possible extension types you would have to maintain it separately. Or if you need searching capabilities (find customer by phone number) you will require lucene or similar solution.
I can elaborate more if you are interested.
EDIT:
To enable searching you would want something like lucene which is a great engine for doing free text search on arbitrary data. There is also hibernate-search which integrates lucene directly with hibernate using annotations and such - I haven't used it but I heard good things about it.
For fetching/writing/accessing data you are basically dealing with XML so any XML technique should apply. The best approach really depends on the actual content and how it is going to be used. I would suggest looking into XPath for data access, and maybe look into defining your own hibernate usertype so that all the access is encapsulated into a class and not just plain String.
I've run into more problems than I hoped I would and as such I decided to dumb down the requirements for my first iteration. I'm currently trying to allow such Extensions only on the entire Company entity, in other words, I'm dropping the whole Owner requirement. So the problem could be rephrased as "How can I add virtual columns (entries in another entity that act like an additional column) to an entity at runtime?"
My current implementation is as follow (irrelevant parts filtered out):
#Entity
class Company {
// The set of Extension definitions, for example "Location"
#Transient
public Set<Extension> getExtensions { .. }
// The actual entry, for example "Atlanta"
#OneToMany(fetch = FetchType.EAGER)
#JoinColumn(name = "companyId")
public Set<ExtensionEntry> getExtensionEntries { .. }
}
#Entity
class Extension {
public String getLabel() { .. }
public ValueType getValueType() { .. } // String, Boolean, Date, etc.
}
#Entity
class ExtensionEntry {
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "extensionId")
public Extension getExtension() { .. }
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "companyId", insertable = false, updatable = false)
public Company getCompany() { .. }
public String getValueAsString() { .. }
}
The implementation as is allows me to load a Company entity and Hibernate will ensure that all its ExtensionEntries are also loaded and that I can access the Extensions corresponding to those ExtensionEntries. In other words, if I wanted to, for example, display this additional information on a web page, I could access all of the required information as follow:
Company company = findCompany();
for (ExtensionEntry extensionEntry : company.getExtensionEntries()) {
String label = extensionEntry.getExtension().getLabel();
String value = extensionEntry.getValueAsString();
}
There are a number of problems with this, however. Firstly, when using FetchType.EAGER with an #OneToMany, Hibernate uses an outer join and as such will return duplicate Companies (one for each ExtensionEntry). This can be solved by using Criteria.DISTINCT_ROOT_ENTITY, but that in turn will cause errors in my pagination and as such is an unacceptable answer. The alternative is to change the FetchType to LAZY, but that means that I will always "manually" have to load ExtensionEntries. As far as I understand, if, for example, I loaded a List of 100 Companies, I'd have to loop over and query each of those, generating a 100 SQL statements which isn't acceptable performance-wise.
The other problem which I have is that ideally I'd like to load all the Extensions whenever a Company is loaded. With that I mean that I'd like that #Transient getter named getExtensions() to return all the Extensions for any Company. The problem here is that there is no foreign key relation between Company and Extension, as Extension isn't applicable to any single Company instance, but rather to all of them. Currently I can get past that with code like I present below, but this will not work when accessing referenced entities (if for example I have an entity Employee which has a reference to Company, the Company which I retrieve through employee.getCompany() won't have the Extensions loaded):
List<Company> companies = findAllCompanies();
List<Extension> extensions = findAllExtensions();
for (Company company : companies) {
// Extensions are the same for all Companies, but I need them client side
company.setExtensions(extensions);
}
So that's were I'm at currently, and I have no idea how to proceed in order to get past these problems. I'm thinking that my entire design might be flawed, but I'm unsure of how else to try and approach it.
Any and all ideas and suggestions are welcome!
The example with Company, Partner, and Customer is actually good application for polymorphism which is supported by means of inheritance with JPA: you will have one the following 3 strategies to choose from: single table, table per class, and joined. Your description sounds more like joined strategy but not necessarily.
You may also consider just one-to-one( or zero) relationship instead. Then you will need to have such relationship for each value of your virtual column since its values represent different entities. Hence, you'll have a relationship with Partner entity and another relationship with Customer entity and either, both or none can be null.
Use pattern decorator and hide your entity inside decoratorClass bye
Using EAV pattern is IMHO bad choice, because of performance problems and problems with reporting (many joins). Digging for solution I've found something else here: http://www.infoq.com/articles/hibernate-custom-fields

Categories