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.
Related
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.
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.
I have a Product entity and table and would like the database design to allow finding a product by different keywords on top of its name, that is, like using a thesaurus e.g. product name "HDR-TD20V" should also be found by keywords "camcorder", "camera", "video camera", etc. Note that this same mechanics can be used to locate the same record from different input languages e.g. looking for "camara de video" (Spanish) or "videokamera" (German) should also find the same record.
Assuming that I am using Hibernate-search i.e. Lucene I have the following two design choices:
De-normalized approach: Product table has a keywords column that contain comma separated keywords for that product. This clearly violates the First Normal Form "... the value of each attribute contains only a single value from that domain.". However, this would integrate nicely with Hibernate-search.
Normalized approach: Define a Keyword entity table i.e. Keyword(id,keyword,languageId) and the many-to-many association ProductKeyword(productId,keywordId) but the integration with Hibernate-Search is not so intuitive anymore ... unless e.g. I create a materialized view i.e. select * from Product p, Keyword k, ProductKeyword pk where p.id=pk.productId and k.id=pk.keywordId and index this materialized view.
I would of course prefer the choice 2 but I am not sure how Hibernate-search would optimally cover this use-case.
Something like this should work:
#Indexed
public class Product {
#Id
private long id;
#ManyToMany
#IndexedEmbedded
Set<Keyword> keywords;
// ...
}
public class Keyword {
#Id
private long id;
// only needed if you want a bidirectional relation
#ManyToMany
#ContainedIn
Set<Product> products;
// ...
}
I am leaving out options for lazy loading etc. How exactly the JPA mapping looks like depends on the user case
I'm working on a desktop application in Java6 using H2 as the db and Hibernate 3.6.
Because of a construct with a third-party library involving JNI and some interesting decisions made a priori, I am unable to pass around long identifiers in their index code, and can only pass int. These indexes are generated quickly and repeatedly(not my choice), and get handed around via callbacks. However, I can split my expected dataset along the lines of a string value, and keep my id size at int without blowing out my id's. To this end, I'm keeping a long value as pk on the core object, and then using that as a one-to-one into another table, where it maps the int id back to the core entity, which when combined with the string, is unique.
So I've considered embedded compound keys and such in hibernate, but what I REALLY want is to just have this "extra" id that is unique within the context of the extra string key, but not necessarily universally unique.
So something like(not adding extraneous code/annotations):
#Entity
public class Foo{
...
#Id
public Long getId(){...}
...
#OneToOne
#PrimaryKeyJoinColumn
public ExtraKey getExtra(){...}
}
#Entity
public class ExtraKey{
...
#Id
public Long getFooId(){...}
...
public Integer getExtraId(){...}
...
public String getMagicString(){...}
}
In that case, I could really even remove the magicString, and just have the fooId -> extraId mapping in the table, and then have the extraId + magicString be in another where magicString is unique. However, I want hibernate to allow the creation of new magicString's at whim(app requirement), ideally one per row in a table, and then have hibernate just update the extraId associated to that magicString via incrementation/other strategy.
Perusing all of the hibernate manuals and trying a few tests on my own in a separate environment has not quite yielded what I want(dynamically created named and sequential id's basically), so I was hoping for SO's input. It's entirely possible I'll have to hand-code all of it myself in the db with sequences or splitting a long and doing logic on the upper and lower, but I'd really rather not, as I might have to maintain this code someday(really likely).
Edit/Addendum
As a sneaky way of getting around this, I'm just adding the extraId to the Foo object(ditching the extraKey class), and generating it from another object singleton, that at load time, does a group by select over the backing Foo table, returning magicKey, and the max(extraId). When I create a new Foo, I ask that object(multithread safe) to hand me the next extraId for the given magicKey and push that into Foo, and store it, thus updating my effective extraId for each magicKey on next app reload without an extra table. It costs me one group by query on the first request for a new extraId, which is suboptimal, but it's fast enough for what I need, simple enough to maintain in the future, and all contained in an external class, so I COULD replace it in one place if I ever come up with something more clever. I do dislike having the extra "special query" in my dao for this purpose, but it's easy enough to remove in the future, and well-documented.
Maybe I still didn't understand your problem properly, but I think you can consider using Hibernate's hilo algorithm. It will generate unique identifier for the whole database, based on a table that Hibernate creates and manages. More details here:
http://docs.jboss.org/hibernate/core/3.5/reference/en/html/mapping.html#mapping-declaration-id
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