Hibernate and a Complex many-to-one on Two Columns - java

I have a BUSINESS table that looks like this:
BUSINESS_ID | BRN | CODE | PARENT_ID
A Business can have a parentBusiness, which is joined from child.parent_id->parent.brn and child.code->parent.code and can be represented by the query:
select * from business childbus left join business parentbus on childbus.parent_id=parentbus.brn and childbus.yppa_code=parentbus.yppa_code where childbus.business_id=?
How can I create that many-to-one relationship for business.parentBusiness? The following many-to-one mapping creates the link from PARENT_ID to BRN but how do I get the constraint from the CODE column in there?
<many-to-one name="parentBusiness" class="Business" column="PARENT_ID" property-ref="brn"/>
EDIT
It was suggested in an answer below that I try to use a formula. According to the docs the formula needs to return just the PK for the object and it will figure out how to populate itself. Here is where I'm at:
<many-to-one name="parentBusiness">
<formula>(select parentBusiness.business_id from business parentBusiness where parentBusiness.brn=parent_id and parentBusiness.code=code)</formula>
</many-to-one>
But this creates an sql error
...
from Business business0_
left outer join Business business1_ on
(select parentBusiness.business_id from business parentBusiness where parentBusiness.brn=business0_.parent_id and parentBusiness.code=business0_.code)=business1_.BUSINESS_ID
because, of course
ORA-01799: a column may not be outer-joined to a subquery
How should my formula be set up?
Many thanks in advance.

you could try to use the formaula attribute.
here is some documentation about how to use it...

I was never able to get this complex relationship working as stated in the original question. I was, however, able to convince management that this was a poor data model.

Related

JPA: Modelling m:n relationship & join-table values

having a m:n real-world relationship between some entities, e.g. user <---> group
Now I want to model this relationship and store additional information based on it, e.g. a field "quality".
I heard that I would have to create a new join table user_group as follows:
id | user_ref | group_ref | quality
----------------------------------
1 1 1 0.5
2 1 2 1.3
... ... ... ...
The corresponding entity has two related entities (private members) user and group, annotated with the #ManyToOne-annotation.
On the other hand, both, my user and my group have a set of related user_group-entities, both private members and declared with the #OneToMany-annotation.
I have three questions:
Is this the right way to model the problem of additional fields in JPA 2.0?
I not allowed to use both user and group in user_group as primary key
since they are not valid primary key types. Is it really necessary to declare a new primary.
Is this a common workflow with these join-tables/entities?
...
EntityManager em = ...
...
em.getTransaction().begin();
User u = new User("Pete");
Group g = new Group("Anonymous workaholics")
UserGroup ug = new UserGroup();
ug.addUser(u);
ug.addGroup(g);
em.persist(u); em.persist(g); em.persist(ug);
em.getTransaction().commit();
em.close()
Thanks a lot!
Yes, it's the good way to do it.
It's possible to have a composite primary key consisting of two ManyToOne associations, but it's much more complex, both in the mapping and in the rest of the application, to handle that, and it's also less efficient. You have an entity, so just do like for all the other entities and use an auto-generated, single-column primary key. The fact that this entity used to be a join table of a many-to-many association is irrelevant.
Yes, that seems OK, except the addUser() and addGroup() methods should be named setUser() and setGroup(): there's only one user and one group for a given UserGroup. I would also use another name for the entity itself. Something like "Membership" for example.

Deadlock in Hibernate Many-to-Many with join table

I am working on an enterprise application where we use Hibernate and a many-to-many relationship with a join table. We are seeing very sporadic database deadlocks in production (with high volume) that we cannot recreate.
Category.java
public class Category {
....
private Set<Product> products = new HashSet<Product>();
...
}
Category.hbm.xml
<class
name="Category"
table="CATEGORY"
>
...
<!-- uni-directional many-to-many association to Product -->
<set
name="products"
table="CATEGORY_PRODUCT_ASSC"
lazy="false"
cascade="none"
>
<key column="CATEGORY_ID" />
<many-to-many class="Product" column="PRODUCT_ID" />
</set>
</class>
Product.java, Product.hbm.xml do not have a set of Categories, as this is uni-directional many-to-many
The CATEGORY_PRODUCT_ASSC table is a simple join table that only has 2 columns: CATEGORY_ID and PRODUCT_ID.
Right now, we are calling Session.saveOrUpdate on the Category instance object for the sole purpose of getting the inserts in the CATEGORY_PRODUCT_ASSC join table (nothing changed on the Category)
I turn on Hibernate show_sql and see the following:
update CATEGORY set NAME=?, DESCRIPTION=?, where category_id=?
insert into CATEGORY_PRODUCT_ASSC (CATEGORY_ID, PRODUCT_ID) values (?, ?)
The problem is that we have many products being created at the exact same second on multiple servers, all for the same Category.
When we see deadlocks, the update CATEGORY call is inevitably involved. We need to prevent these update CATEGORY SQL statements from being executed.
Option 1: Is there any way that I can call Session.saveOrUpdate(category) and have it not update Category (since that has not changed), but still do the insert into the join table CATEGORY_PRODUCT_ASSC ?
Option 2: If not, we have thought about just doing a straight INSERT of the CATEGORY_PRODUCT_ASSC rows via JDBC. However, one concern is stale Hibernate objects (Category objects) in the cache. Any ideas/recommendations on this possible approach?
Thank you very much in advance for your help. :-)
We resolved this issue. It did turn out to be the update category statement. Instead of using the CATEGORY_PRODUCT_ASSC table as a join-through for the many-to-many relationship, we created a Hibernate-managed entity that represents this join table ... CategoryProductAssc.
This way, we could directly persist the relationship without having to call Session.saveOrUpdate on the Category instance object for the sole purpose of getting the inserts in the CATEGORY_PRODUCT_ASSC join table when nothing changed on the Category object.
I created Cactus tests that spun up 20 simultaneous executions, tested old vs new code and our DBAs monitored and saw concurrency with the old code and no concurrency with the new code.

JPA one-to-many unidirectional relationship using a join table

I would like to evaluate JPA on an existing project. The database model and the java classes exists and are currently mapped via self generated code. The database model and the java classes do not fit ideally together - but the custom mapping works well. Nevertheless the usage of JPA in general seems worth a try.
As you see I am new to JPA and have to do the work with xml configuration. Currently I am working on a one-to-many unidirectional relationship using a join table (please do not discuss this szenario here).
A (one - relationship owner) <-> AB (JoinTable) <-> B (many)
The tables look like this
A
--
ID
BREF
...
B
--
ID
...
AB
--
A_BREF (foreign key to a reference column in A which is NOT the id)
B_ID
I would like to define a unidirectional one-to-many relationship for class A.
class A {
private List<B> bs;
}
and did it like this:
<one-to-many name="bs">
<join-table name="ab">
<join-column name="a_bref">
<referenced-column-name name="bref" />
</join-column>
<inverse-join-column name="b_id">
<referenced-column-name name="id" />
</inverse-join-column>
</join-table>
</one-to-many>
Althoug this does not force an error it is not working. The problem is that the join table does not work on the ID column of A. The query to select the "B" entities works with the A.ID column value instead of the A.BREF column value to select the entities.
(How) can I make this mapping work (I use eclipselink 2.2.0)?
Thanks for any suggestion!
EDIT:
After looking at a link provided in #SJuan76 answer I slightly modified my mapping to
<one-to-many name="bs">
<join-table name="ab">
<join-column name="a_bref" referenced-column-name="bref" />
<inverse-join-column name="b_id" referenced-column-name="id" />
</join-table>
</one-to-many>
This now causes the following errors (tested with eclipselink 2.1.0 and 2.2.0)
eclipselink 2.1.0
Exception Description: The parameter
name [bref] in the query's selection
criteria does not match any parameter
name defined in the query.
eclipselink 2.2.0
Exception Description: The reference
column name [bref] mapped on the
element [field bs] does not
correspond to a valid field on the
mapping reference.
By the way - if I remove the referenced-column-name="bref" from the definition I get the same exception for the referenced-column-name="id" on the inverse-join-column element. So I doubt that I have understood referenced-column-name correct. I used it to specify the database column name of the tables which are related to the join table. Is this correct?
SOLUTION:
The final error in my szenario was that I did not have the BREF field definied in my class
class A {
private long bref; // missing !
private List<B> bs;
}
and in my orm.xml mapping file for this class
<basic name="bref">
<column name="bref" />
</basic>
I was not aware that I have to define the used join mapping referenced-column-name attributes somewhere in my mapping classes (as I also did not have the join-table itself or the name attributes of join-column/inverse-join-column mapped to a class or class members.)
Also the tip to check the case issue was helpful for me. I feel now quite to verbose in specifying my mapping as I overwrite all default (uppercase) mappings with lowercase values. As my database is not case sensitive I will use upper case notation if special mapping is needed to go with the default.
+1 for all!
Can you try defining the field as "BREF" or the same exact case used if you defined it on the attribute mapping, or you can try setting the eclipselink.jpa.uppercase-column-names persistence property to true. This is likely the issue with "id" when referenced-column-name="bref" is removed, since it is likely the field in the entity defaults to "ID".
In general JPA requires that the foreign keys/join columns reference the primary key/Id of the Entity. But, this should work with EclipseLink, so please include the SQL that is being generated, and if it is wrong, please log a bug.
How is the Id of A defined, is it just ID or ID and BREF?
You can use a DescriptorCustomizer to customize the ManyToManyMapping for the relationship and set the correct foreign key field name.

JPA or Hibernate - Joining tables on columns of different types

Is there a way to tell Hibernate to wrap a column in a to_char when using it to join to another table or conversely convert a NUMBER to a VARCHAR? I have a situation where I have a table which contains a generic key column of type VARCHAR which stores the Id of another table which is a Number. I am getting a SQL exception when Hibernate executes the SQL it generates which uses '=' to compare the two columns.
Thanks...
P.S. I know this is not ideal but I am stuck with the schema so I have to deal with it.
This should be possible using a formula in your many-to-one. From section 5.1.22. Column and formula elements (solution also mentioned in this previous answer):
column and formula attributes can
even be combined within the same
property or association mapping to
express, for example, exotic join
conditions.
<many-to-one name="homeAddress" class="Address"
insert="false" update="false">
<column name="person_id" not-null="true" length="10"/>
<formula>'MAILING'</formula>
</many-to-one>
With annotations (if you are using Hibernate 3.5.0-Beta-2+, see HHH-4382):
#ManyToOne
#Formula(value="( select v_pipe_offerprice.offerprice_fk from v_pipe_offerprice where v_pipe_offerprice.id = id )")
public OfferPrice getOfferPrice() { return offerPrice; }
Or maybe check the #JoinColumnsOrFormula:
#ManyToOne
#JoinColumnsOrFormulas(
{ #JoinColumnOrFormula(formula=#JoinFormula(value="SUBSTR(product_idnf, 1, 3)", referencedColumnName="product_idnf")) })
#Fetch(FetchMode.JOIN)
private Product productFamily;

Spring-hibernate mapping problem

I have a spring-hibernate application which is failing to map an object properly: basically I have 2 domain objects, a Post and a User. The semantics are that every Post has 1 corresponding User.
The Post domain object looks roughly as follows:
class Post {
private int pId;
private String attribute;
...
private User user;
//getters and setters here
}
As you can see, Post contains a reference to User. When I load a Post object, I want to corresponding User object to be loaded (lazily - only when its needed).
My mapping looks as follows:
<class name="com...Post" table="post">
<id name="pId" column="PostId" />
<property name="attribute" column="Attribute" type="java.lang.String" />
<one-to-one name="User" fetch="join"
class="com...User"></one-to-one>
</class>
And of course I have a basic mapping for User set up.
As far as my table schema is concerned, I have a table called post with a foreign UserId which links to the user table.
I thought this setup should work, BUT when I load a page that forces the lazy loading of the User object, I notice the following Hiberate query being generated:
Select ... from post this_ left outer join user user2_ on this.PostId=user2_.UserId ...
Obviously this is wrong: it should be joining UserId from post with UserId from user, but instead its incorrectly joining PostId from post (its primary key) with UserId from user.
Any ideas? Thanks!
Update:
Thanks to a couple of the posts below I now realize that I should have been using a many-to-one mapping instead of a one-to-one. I changed the mapping under post to the following:
<many-to-one name="User" class="com...User" column="uId"/>
But now I get a run-time error telling me that there is no attribute called uId. This makes sense since I do not have a uId column in my post domain object (I simply have a reference to a user object). Now I am really confused as to how I can get Hibernate to realize that it needs to map the foreign key from the post table to the user table. Should explicitly add a uId attribute to my post domain object to be a placeholder for the foreign key?
I hope I am making sense...
Since a user has many posts, your association is in fact a "many-to-one", not a "one-to-one". It should work if you map it accordingly.
Edit: Yes, you can map the property Post.user on the Post with a "many-to-one", or the set User.posts in User with a "one-to-many", or both. Have you specified the name of your foreign key column?
Edit2: In Hibernate speak, a "column" in the database is mapped to a "property" in your Java-Class. That is, the column attribute contains the name of your foreign key column in the database, not the name of any property in your Java class. If I read your question right, you should use "UserId", not "uId".
Oh, and a fetch="join" can not be lazy, as it mandates that the user is fetched in the same query as the post.
That is the behaviour of a one-to-one mapping. They usually share a primary key. Hibernate is assuming that the primary key of post is teh same as the primary key of user. This page summarizes this behaviour.
I suspect that one user can actually have more than one posts though. That makes your mapping a one-to-many.

Categories