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.
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.
We are using Hibernate as persistency layer and have complex object model. Without exposing the real data model I want to explain the problem using the following simple example.
class Person {
private Integer id; //PK
private String name;
private Account account;
// other data, setters, getters
}
class Account {
private Integer id; //PK
// other data, setters, getters
}
The DB mapping is defined using HBM as following:
<class name="Person" table="PERSON">
<id name="id" column="ID">
<generator class="native"/>
</id>
<version name="version" type="java.lang.Long"/>
<property name="name" type="java.lang.String" length="50" column="NAME"/>
<many-to-one name="account" column="ACCOUNT_ID"
class="com.mycompany.model.Account"/>
</class>
I have to save new populated instance of Person linked to existing Account. The call is originated by web client, so at my layer I get instance of Person referenced to instance of Account that holds its ID only.
If I try to call saveOrUpdate(person) the following exception is thrown:
org.hibernate.TransientObjectException:
object references an unsaved transient instance - save the transient instance before flushing:
com.mycompany.model.Account
To avoid this I have to find the persisted object of Account by ID and then call person.setAccount(persistedAccount). In this case everything works fine.
But in real life I deal with dozens of entities referenced to each other. I do not want to write special code for each reference.
I wonder whether there is some kind of generic solution for this problem.
To persist one entity, you just need to have the references to its direct dependencies. The fact that these other entities reference other entities doesn't matter.
The best way to do it is to get a proxy to the referenced entity, without even hitting the database, using session.load(Account.class, accountId).
What you're doing is the right thing to do: get a reference to the persistent account, and set this reference into the newly created account.
Hibernate allows you to use just reference classes with ID, you don't need to do session.load().
The only important thing is that if your reference object has VERSION, than the version must be set.
In your case you have to specify version of Account object.
Use cascade="all" on the * -to- * mapping
Have you tried cascade="save-update" in many-to-one element? Hibernate defaults to cascade="none"...
thank you for helping. I have already implemented my own generic solution but wanted to know whether other solutions exist.
I want to share with you the idea. I call the referenced entities that do not contain anything except ID (or other field that can be used to identify the entity uniquely) placeholder.
So, I created annotation #Placeholder and put it on all referenced fields. In our example it is on account field of Person class.
We already have class named GenericDao that wraps Hibernate API and have method save(). I added yet another method saveWithPlacehodlers() that does the following. It discovers the class of given object by reflection, finds all fields marked with annotation #Placeholder, finds the objects in DB and calls appropriate setter in main entity to replace the referenced placeholder by persistent entity.
Annotation #Placeholder allows to define field that will be used to identify the entity. The default is id.
What do you think, guys about this solution?
There is one more solution to problem - using the default constructor of the entity you want a reference for and set id and version (if it is versioned). We have following dao method:
public <S extends T> S materialize(EId<S> entityId, Class<S> entityClass) {
Constructor<S> c = entityClass.getDeclaredConstructor();
c.setAccessible(true);
S instance = c.newInstance();
Fields.set(instance, "id", entityId.getId());
Fields.set(instance, "version", entityId.getVersion());
return instance; // Try catch omitted for brevity.
}
We can use such an approach because we don't use lazy loading but instead have 'views' of entities which are used on the GUI. That allows us to get away from all the joins Hibernate uses to fill all eager relations. The views always have id and version of the entity. Hence, we can fill the reference by creating an object which would appear to Hibernate as not transient.
I tried both this approach and the one with session.load(). They both worked fine. I see some advantage in my approach as Hibernate won't leak with its proxies elsewhere in the code. If not properly used, I'll just get the NPE instead of the 'no session bound to thread' exception.
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.
Currently when I try to insert new records I am getting an error:
[ERROR] 05/12/11_09:44:20.54 [org.hibernate.event.def.AbstractFlushingEventListener] - Could not synchronize database state with session
Db2 triggers to generate the ID need to remain in place to support legacy applications. How can I configure the hbm.xml to not generate the ID?
I'm not sure what version of Hibernate you are using, but Hibernate currently supports getting an ID that is generated from trigger via a special generator called select.
In short, you can add this generator to your ID column, and then reference a natural key you can use to retrieve the trigger generated ID as follows:
<id name="id" type="long" column="person_id">
<generator class="select">
<param name="key">socialSecurityNumber</param>
</generator>
</id>
If your mapping already has a natural-key entry defined, then you shouldn't even need to specify the key param to the generator.
One problem with this particular generator is that you can only use one entity property as the selection key for it. If you need to select via a composite key, then you'll have to create your own generator for this purpose.
You could extend org.hibernate.id.SelectGenerator or one of it's parents, and then implement the select via multiple columns that way. Then you simply replace the class attribute of the above generator entry with the fully qualified class name of your new generator.
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.