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.
Related
As per Hibernate docs for one-to-many xml mapping tag there is an attribute called as not-found
http://docs.jboss.org/hibernate/orm/3.3/reference/en-US/html/collections.html#collections-onetomany
The Doc says:
not-found (optional - defaults to exception): specifies how cached
identifiers that reference missing rows will be handled. ignore will
treat a missing row as a null association.
What is the use of this attribute? I tried to create a mapping between Product and Parts with Product having a set of Parts with below mapping details:
<set name="parts" cascade="all">
<key column="productSerialNumber" not-null="true" />
<one-to-many class="Part" not-found="ignore"/>
</set>
Then I wrote my Java code as:
public static void main(String[] args) {
Session session = HibernateUtil.getSessionFactory().getCurrentSession();
session.beginTransaction();
Product prod = (Product) session.get(Product.class, 1);
session.getTransaction().commit();
System.out.println(prod);
HibernateUtil.getSessionFactory().close();
}
I was expecting null for my set which has Parts as I configured in my mapping file as not-found="ignore". But I got the regular exception - org.hibernate.LazyInitializationException
Please help me in understanding what is the use of this attribute? What are cached identifiers here?
The not-found has nothing to do with lazy loading. It's used to handle incoherences in your database.
Suppose you know nothing about good database practices, and have an order_line table containing an order_id column, supposed to reference the order it belongs to. And suppose that since you know nothing about good practices, you don't have a foreign key constraint on this column.
Deleting an order will thus be possible, even if the order has order lines referencing it. When loading such an OrderLine with Hibernate, Hibernate will load the Order and fail with an exception because it's supposed to exist, but doesn't.
Using not-found=ignore makes Hibernate ignore the order_id in the OrderLine, and will thus initialize the order field to null.
In a well-designed database, this attribute should never be used.
I have two tables in my database USERS, and USER_SESSIONS. USER_SESSIONS has a foreign key on userid that maps to the USERS table's userid, and I have defined the many-to-one relationship in hibernate with:
<many-to-one name="user" column="USERID" class="com.Users" />
How do I write a hibernate method to delete all rows in the USER_SESSIONS database for a given user when only given the username.
My first attempt was to load the user object for the given username, and then do an HSQL to delete all sessions for that user's userid. Is there a simpler way?
I think one other way could be as:
Define cascade as delete on the relationship
Load user entity
Remove all the user session object from user
Save user entity back.
I am trying to map a class Language to a class User.
Many to one mapping is working fine, and through the functionality of the app I can set the value from it's default of null to a particular value.
The problem is that I'd like to set the default mapping to idLanguage 1, but when it maps everything is set to null.
I have tried:
< many-to-one name="language" class="com. \etc\ .language.Language" column="language" default="1" lazy="false" />
However that gives me an error "Attribute default must be declared for element type Many To One.
What to do?
(note: language is mapped to language object in user class, rather than a language id. Setting in the app puts the id of the language in the language column, replacing null value)
I hope the languages will be seed data in your application. Then have a separate langId field in the User mapping and set it to 1. The Object mapping can be used while loading the User bean.So modify the mapping as below.
< many-to-one name="language" class="com. \etc\ .language.Language" column="language" lazy="false" insert="false" update="false"/>
<property name="langId" column="language"/>
This way it will be more performative as you don't have to fetch the Languages bean from the DB before you save the User bean. Also on load of User the languages will be available to.
I'm currently learning Hibernate, and I've stumbled into this issue:
I have defined 3 entities: User, Module, Permission. Both user and module have a one-to-many relationship with Permission, so that Permission's composite id consists of idUser and idModule. The user class has a property that is a set of Permission's and it is appropriately annotated with #OneToMany, cascade=CascadeType.ALL, etc.
Now, I generated the classes with MyEclipse's reverse engineering feature. The id of permission was created as a separate class that has an idUser and idModule property. I thought I could create a User, add some new permissions to it, and thus saving the user would cascade the operation, and permissions would be saved automatically. This is true except that the operation causes an exception. I run the following code:
Permission p = new Permission();
p.setId(new PermissionId(null, module.getId());
user.getPermissions().add(p);
session.save(user);
The problem I have is that, even though the SQL is being generated correctly (first saves User, then Permission), I get an error from the database driver (Firebird) which states that it can't insert a null value for idUser, which is true, but shouldn't hibernate be passing the newly created user id to the second query?
This particular scenario feels very counter-intuitive to me since I'm inclined to pass a null id to the Permission object since it is new and I want it to be created, but on other hand, I have to set the idModule property since the module already exists, so I don't really understand how an operation like this is supposed to work.
Does anyone know what I'm doing wrong? Thanks
You need to specify a cascade action for Hibernate to perform when you save a User with an attached transient (meaning not-yet-saved) Permission.
By the way, you might want to consider using a different ID strategy for the Permission object, such as a generated ID value - how can the primary key of the permission row in the database contain a null value?
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.