Why JPQL expressions cannot navigate beyond relationship fields that are collections? - java

Excerpt from Java EE 7 tutorial:
SELECT DISTINCT p FROM Player p, IN (p.teams) AS t WHERE t.city = :city
Expressions cannot navigate beyond (or further qualify) relationship
fields that are collections. In the syntax of an expression, a
collection-valued field is a terminal symbol. Because the teams field
is a collection, the WHERE clause cannot specify p.teams.city (an
illegal expression).
Why expressions cannot navigate beyond relationship fields that are collections?

I didn't design the language, and I could of course miss more important reasons, but IMHO, it wouldn't make much sense: p.teams.city reads as "the city property of the teams property of p". But teams is a collection. And collections don't have any city property.
That's at least a good way to remember that this is not vald JPQL.

Related

Uni/Bi direction X One/Many X Many/One Association relationships

Following question also refers to discussion in following questions as well
https://stackoverflow.com/search?page=2&tab=Relevance&q=one%20to%20many%20unidirectional%20java
Best practise for adding a bidirectional relation in OO model
I tried to implementing 8 association combinations formed by [Unidirectional/Bidirectional] X [(One/Many) to (One/Many)] in Java. I found two cases can not be implemented namely Unidirectional One to One and Unidirectional One to Many (e.g. Person->*Vehicle). Other 6 combinations and Composition are possible programatically.
I Feel its not only the case with Java, these 2 cases do not exist. e.g. Use case - allocate one Aadhar/SSN number to only one person is possible if we know that number is not allocated to anybody else (reverse navigation is must). Does this mean we need to take care while making our design model not to arrive at these specific associations (though they might be present in analysis model)? I am confused on this.
Basic (No Aggregation)
If you are looking at basic unidirectional association, then that's the simplest of them all.
Unidirectional One to One
class Person {
String name;
}
Unidirectional One to Many
class Person {
List vehicles;
}
Composite Aggregation
If I assume that you are asking about composite relationshions (where one SSN can be assigned to at most one person), then you can still implement it.
How exactly you decide to implement it is however subject to your specific domain or e.g. how you store your data, because
reverse navigation is must
is not actually true, because you can just check all Person instances; or you can store all the SSNs in a smart data structure that allows you to quickly check if a new one is unique, and then you would assign it to the Person without additional checks, because you already know that it is unique).
Or you can implement also the opposite lookup, which is not prohibited even if the association is "uni-directional"
To quote the UML Specs (11.5.3.1 Associations) [emphasis mine]:
Navigability means that instances participating in links at runtime (instances of an Association) can be
accessed efficiently from instances at the other ends of the Association. The precise mechanism by which such efficient
access is achieved is implementation specific. If an end is not navigable, access from the other ends may or may not be
possible, and if it is, it might not be efficient.
Update from comments
Noone claims that upholding the relationship constraints has to be done in the accessors. In fact pretty much always you will have temporarily invalid relationships, imagine:
person = new Person();
// right now person is invalid state because it doesn't have an SSN
ssn = ssnGenerator.createNew();
// now ssn is also in invalid state because it has no person
person.setSSN(ssn);
// only now is person and ssn valid
(creating a constructor wouldn't help, because constructor is called after the object has already been created (so another part of the constructor could need the ssn already set).
So it is the responsibility of the programmer to ensure that the system upholds all constraints in whatever way it makes most sense. Using constructors/accessors is the easiest way in some circumstances, but you could e.g. wrap the code above in an atomic transaction. After all, if you kept your validation in the setSSN(), then what would happen if the programmer were to forget to call the method at all?
(person 1->* vehicle)
p1.add(v1) and p2.add(v1) are possible violations
You asked about "person ->* vehicle", now you've changed it to "person 1 -> * vehicle" so obviously the answer differs. But the same principle as above applies -- it is the responsibility of the system to uphold all constraints, and wherever that's done in accessors, validation methods, or the way the system constructed is an implementational detail -- there's no single best way, and there will be always trade-offs.

HQL - Select nested objects

Consider the following scenario: two database tables (producers and produce), with a one to many relation (one produce is created by one producer, one producer has many products), each table with multiple fields (including name and id).
Also, consider I have two managed entities (Producer and Produce) and two smaller versions of the these objects (unmanaged) used as DTOs (ProducerDTO and ProduceDTO) with only name and ID (and the relation).
I want to select using an HQL query a specific product (as a DTO) with it's producer set as well. More clearly:
SELECT new ProduceDTO(produce.id, new ProducerDTO(producer.id, producer.name), produce.name) FROM Produce produce JOIN produce.producer producer WHERE ...
But I get a QuerySyntaxException: unexpected token: , after produce.id in the query. So my question would be: is it possible to select nested custom objects in HQL and if so, what are the rules / limitations for this? For a single custom object, it works just fine, but when I try to nest 2 or more, I'm having issues.
Note: the question is to better understand the phenomenon, not necessarily for this specific case (I already have a workaround for my case with my actual classes).
Thanks!
The full JPA query language syntax specification can be found in official Java EE 7 documentation here: http://docs.oracle.com/javaee/7/tutorial/persistence-querylanguage005.htm#BNBUF
Specifically, the parts that answer your question are:
constructor_expression ::= NEW constructor_name(constructor_item {, constructor_item}*)
constructor_item ::= single_valued_path_expression
| aggregate_expression
So this basically means
You can't use a null constructor.
Each parameter can be either single_valued_path_expression or aggregate_expression.
Further, constructor_expression is present only in select_expression that can be used only as a top-level query construct, so per standard JPQL you can't do tricks with nested select ... statements. I couldn't find a complete specification of HQL online, so not sure about that one.

Relationship between Car Class and Road class - UML and Java code

I am trying to understand the relationship between classes in Object oriented world, and came across various terms like:
Association , Aggregation, Composition, Dependency, Generalization, Realization, Using (and may be there are more to the list, which I would encounter soon).
I came across the following UML diagram:
Here, we have two different Classes (and so objects), Car and Road, and the connector symbol connecting them (and I believe it is directed association symbol, as per MS Visio).
So this means that Car and Road classes are having some relationship (association). I have some doubts on this to understand this relation:
1) How would this relationship be translated to Java classes? I am having difficulty in understanding how Car and Road would have "some code" connecting them?
2) what does * and 0..1 mean in this diagram? Usually I have seen these in an Entity-Relationship diagrams (in DB).
Any pointer to understand this would be of great help.
A Car object would have a reference to a Road object (in other words, an instance variable of type Road). A Road object would also have a list (or List) of Car objects. The first sentence represents the 0..1 relationship between the classes; note that the reference could be null (car is on 0 roads) or not (car is on one road). The list in the Road object represents the * relationship -- 0 or more cars are on the road.
1) is too broad to answer. UML and Java are both formal languages with well-defined structures, but there is no standardized way of expressing any particular UML concept in Java or vice versa. Thus, any answer would be opinion-based.
Furthermore, an association is a loosely-defined relationship. UML has many others more strictly defined (you've listed a few), and those are easier to translate to source code.
Because there are more strictly defined relationships, the correct reading of an association relationship is along the lines "these two things are related somehow, but not so tightly that the one contains the other, or that the one uses the other, or is dependent on the other." Those concepts all have their own connectors, and the modeller has made a conscious decision not to use them.
2) * means "any number" and 0..1 means "zero or one", which is usually read as "an optional". So the drivesOn relationship associates any number of Cars with an optional Road.
Presumably this should be taken to mean that a car may drive on a road, but never on more than one, and a road may have any number of cars driving on it.
In terms of understanding UML, this is a very poor example so don't try to read too much from it.
Your questions do have simple answers:
Your uni-directional many-to-one association drivesOn is expressed in (or translated to) Java in the form of a single-valued reference property in the following way:
class Car {
int passengers;
Road drivesOn;
}
The symbols * and 0..1 represent multiplicities: * means many (or unbounded) and 0..1 means at most one, so your model makes two multiplicity statements: (1) a Car movesOn at most one Road, and (2) a Road has many Cars moving on it.
A pointer for reading more about the meaning of associations and multiplicities and how they are expressed in Java is my book chapter Reference Properties and Unidirectional Associations.
It is like many cars can be associated to no road or at max one road. In other words many cars can be driven on 1 road or not at all driven on any road

How to get over limitations of the Hibernate Criteria and Example APIs?

I'm in a position where our company has a database search service that is highly configurable, for which it's very useful to configure queries in a programmatic fashion. The Criteria API is powerful but when one of our developers refactors one of the data objects, the criteria restrictions won't signal that they're broken until we run our unit tests, or worse, are live and on our production environment. Recently, we had a refactoring project essentially double in working time unexpectedly due to this problem, a gap in project planning that, had we known how long it would really take, we probably would have taken an alternative approach.
I'd like to use the Example API to solve this problem. The Java compiler can loudly indicate that our queries are borked if we are specifying 'where' conditions on real POJO properties. However, there's only so much functionality in the Example API and it's limiting in many ways. Take the following example
Product product = new Product();
product.setName("P%");
Example prdExample = Example.create(product);
prdExample.excludeProperty("price");
prdExample.enableLike();
prdExample.ignoreCase();
Here, the property "name" is being queried against (where name like 'P%'), and if I were to remove or rename the field "name", we would know instantly. But what about the property "price"? It's being excluded because the Product object has some default value for it, so we're passing the "price" property name to an exclusion filter. Now if "price" got removed, this query would be syntactically invalid and you wouldn't know until runtime. LAME.
Another problem - what if we added a second where clause:
product.setPromo("Discounts up to 10%");
Because of the call to enableLike(), this example will match on the promo text "Discounts up to 10%", but also "Discounts up to 10,000,000 dollars" or anything else that matches. In general, the Example object's query-wide modifications, such as enableLike() or ignoreCase() aren't always going to be applicable to every property being checked against.
Here's a third, and major, issue - what about other special criteria? There's no way to get every product with a price greater than $10 using the standard example framework. There's no way to order results by promo, descending. If the Product object joined on some Manufacturer, there's no way to add a criterion on the related Manufacturer object either. There's no way to safely specify the FetchMode on the criteria for the Manufacturer either (although this is a problem with the Criteria API in general - invalid fetched relationships fail silently, even more of a time bomb)
For all of the above examples, you would need to go back to the Criteria API and use string representations of properties to make the query - again, eliminating the biggest benefit of Example queries.
What alternatives exist to the Example API that can get the kind of compile-time advice we need?
My company gives developers days when we can experiment and work on pet projects (a la Google) and I spent some time working on a framework to use Example queries while geting around the limitations described above. I've come up with something that could be useful to other people interested in Example queries too. Here is a sample of the framework using the Product example.
Criteria criteriaQuery = session.createCriteria(Product.class);
Restrictions<Product> restrictions = Restrictions.create(Product.class);
Product example = restrictions.getQueryObject();
example.setName(restrictions.like("N%"));
example.setPromo("Discounts up to 10%");
restrictions.addRestrictions(criteriaQuery);
Here's an attempt to fix the issues in the code example from the question - the problem of the default value for the "price" field no longer exists, because this framework requires that criteria be explicitly set. The second problem of having a query-wide enableLike() is gone - the matcher is only on the "name" field.
The other problems mentioned in the question are also gone in this framework. Here are example implementations.
product.setPrice(restrictions.gt(10)); // price > 10
product.setPromo(restrictions.order(false)); // order by promo desc
Restrictions<Manufacturer> manufacturerRestrictions
= Restrictions.create(Manufacturer.class);
//configure manuf restrictions in the same manner...
product.setManufacturer(restrictions.join(manufacturerRestrictions));
/* there are also joinSet() and joinList() methods
for one-to-many relationships as well */
Even more sophisticated restrictions are available.
product.setPrice(restrictions.between(45,55));
product.setManufacturer(restrictions.fetch(FetchMode.JOIN));
product.setName(restrictions.or("Foo", "Bar"));
After showing the framework to a coworker, he mentioned that many data mapped objects have private setters, making this kind of criteria setting difficult as well (a different problem with the Example API!). So, I've accounted for that too. Instead of using setters, getters are also queryable.
restrictions.is(product.getName()).eq("Foo");
restrictions.is(product.getPrice()).gt(10);
restrictions.is(product.getPromo()).order(false);
I've also added some extra checking on the objects to ensure better type safety - for example, the relative criteria (gt, ge, le, lt) all require a value ? extends Comparable for the parameter. Also, if you use a getter in the style specified above, and there's a #Transient annotation present on the getter, it will throw a runtime error.
But wait, there's more!
If you like that Hibernate's built-in Restrictions utility can be statically imported, so that you can do things like criteria.addRestriction(eq("name", "foo")) without making your code really verbose, there's an option for that too.
Restrictions<Product> restrictions = new Restrictions<Product>(){
public void query(Product queryObject){
queryObject.setPrice(gt(10));
queryObject.setPromo(order(false));
//gt() and order() inherited from Restrictions
}
}
That's it for now - thank you very much in advance for any feedback! We've posted the code on Sourceforge for those that are interested. http://sourceforge.net/projects/hqbe2/
The API looks great!
Restrictions.order(boolean) smells like control coupling. It's a little unclear what the values of the boolean argument represent.
I suggest replacing or supplementing with orderAscending() and orderDescending().
Have a look at Querydsl. Their JPA/Hibernate module requires code generation. Their Java collections module uses proxies but cannot be used with JPA/Hibernate at the moment.

Map database column1, column2, columnN to a collection of elements

In legacy database tables we have numbered columns like C1, C2, C3, C100 or M1, M2, M3, M100.
This columns represent BLOB data.
It is not possible to change anything it this database.
By using JPA Embeddable we map all of the columns to single fields. And then during embedding we override names by using 100 override annotations.
Recently we have switched to Hibernate and I've found things like UserCollectionType and CompositeUserType. But I hadn't found any use cases that are close to mine.
Is it possible to implement some user type by using Hibernate to be able to map a bundle of columns to a collection without additional querying?
Edit:
As you probably noticed the names of columns can differ from table to table. I want to create one type like "LegacyArray" with no need to specify all of the #Columns each time I use this type.
But instead I'd use
#Type(type = "LegacyArrayUserType",
parameters =
{
#Parameter(name = "prefix", value = "A"),
#Parameter(name = "size", value = "128")
})
List<Integer> legacyA;
#Type(type = "LegacyArrayUserType",
parameters =
{
#Parameter(name = "prefix", value = "B"),
#Parameter(name = "size", value = "64")
})
List<Integer> legacyB;
I can think of a couple of ways that I would do this.
1. Create views for the collection information that simulates a normalized table structure, and map it to Hibernate as a collection:
Assuming your existing table is called primaryentity, I would create a view that's similar to the following:
-- untested SQL...
create view childentity as
(select primaryentity_id, c1 from primaryentity union
select primaryentity_id, c2 from primaryentity union
select primaryentity_id, c3 from primaryentity union
--...
select primaryentity_id, c100 from primaryentity)
Now from Hibernate's perspective, childentity is just a normalized table that has a foreign key to primarykey. Mapping this should be pretty straight forward, and is covered here:
http://docs.jboss.org/hibernate/stable/core/reference/en/html/collections.html
The benefits of this approach:
From Hibernate's point of view, the tables are normalized, it's a fairly simple mapping
No updates to your existing tables
The drawbacks:
Data is read-only, I don't think your view can be defined in an updatable manner (I could be wrong)
Requires change to the database, you may need to create lots of views
Alternately, if your DBA won't even let you add a view to the database, or if you need to perform updates:
2. Use Hibernate's dynamic model mapping facility to map your C1, C2, C3 properties to a Map, and have some code you your DAO layer do the appropriate conversation between the Map and the Collection property:
I have never done this myself, but I believe Hibernate does allow you to map tables to HashMaps. I'm not sure how dynamically Hibernate allows you to do this (i.e., Can you get away with simply specifying the table name, and having Hibernate automatically map all the columns?), but it's another way I can think of doing this.
If going with this approach though, be sure to use the data access object pattern, and ensure that the internal implementation (use of HashMaps) is hidden from the client code. Also be sure to check before writing to the database that the size of your collection does not exceed the number of available columns.
The benefits of this approach:
No change to the database at all
Data is updatable
O/R Mapping is relatively simple
The drawbacks:
Lots of plumbing in the DAO layer to map the appropriate types
Uses experimental Hibernate features that may change in the future
Personally, I think that design sounds like it breaks first normal form for relational databases. What happens if you need C101 or M101? Change your schema again? I think it's very intrusive.
If you add Hibernate to the mix it's even worse. Adding C101 or M101 means having to alter your Java objects, your Hibernate mappings, everything.
If you have 1:m relationships with C and M tables, you'd be able handle the cases I just cited by adding additional rows. Your Java objects contain Collection<C> or Collection<M>. Your Hibernate mappings are one-to-many that don't change.
Maybe the reason that you don't see any Hibernate examples to match your case because it's a design that's not recommended.
If you must, maybe you should look at Hibernate Component Mapping.
UPDATE: The fact that this is legacy is duly noted. My point in bringing up first normal form is as much for others who might find this question in the future as it is for the person who posted the question. I would not want to answer the question in such a way that it silently asserted this design as "good".
Pointing out Hibernate component mapping is pertinent because knowing the name of what you're looking for can be the key when you're searching. Hibernate allows an object model to be finer grained than the relational model it maps. You are free to model a denormalized schema (e.g., Name and Address objects as part of a larger Person object). That's just the name they give such a technique. It might help find other examples as well.
Sorry if I'm misunderstanding your problem here, I don't know much about Hibernate. But couldn't you just concatenate during selection from database to get something like what you want?
Like:
SELECT whatever
, C1||C2||C3||C4||...||C100 AS CDATA
, M1||M2||M3||M4||...||M100 AS MDATA
FROM ...
WHERE ...
(Of course, the concatenation operator differs between RDBMSs.)
[EDIT] I suggest to use a CompositeUserType. Here is an example. There is also a good example on page 228f in the book "Java Persistence With Hibernate".
That allows you to handle the many columns as a single object in Java.
The mapping looks like this:
#org.hibernate.annotations.Columns(columns = {
#Column(name="C1"),
#Column(name="C2"),
#Column(name="C3"),
...
})
private List<Integer> c;
Hibernate will load all columns at once during the normal query.
In your case, you must copy the int values from the list into a fixed number of columns in nullSafeSet. Pseudocode:
for (int i=1; i<numColumns; i++)
if (i < list.size())
resultSet.setInt(index+i, list.get(i));
else
resultSet.setNull(index+i, Hibernate.INTEGER.sqlType());
In nullSafeGet you must create a list and stop adding elements when a column is NULL. For additional safety, I suggest to create your own list implementation which doesn't allow to grow beyond the number of columns (inherit from ArrayList and override ensureCapacity()).
[EDIT2] If you don't want to type all the #Column annotations, use a code generator for them. That can be as simple as script which you give a name and a number and it prints #Column(...) to System.out. After the script ran, just cut&paste the data into the source.
The only other solution would be to access the internal Hibernate API to build that information at runtime but that API is internal, so a lot of stuff is private. You can use Java reflection and setAccessible(true) but that code probably won't survive the next update of Hibernate.
You can use UserTypes to map a given number of columns to any type you wish. This could be a collection if (for example) for collections are always bounded in size by a known number of items.
It's been a while (> 3 years) since I used Hibernate so I'm pretty rusty but I recall it being very easy to do; your BespokeUserType class gets passed the ResultSet to hydrate your object from it.
I too have never used Hibernate.
I suggest writing a small program in an interpreted language (such as Python) in which you can execute a string as if it were a command. You could construct a statement which takes the tedious work out of doing what you want to do manually.

Categories

Resources