I am following Hibernate Documentation and trying to implement the example given for section 9.4. Components as composite identifiers but facing issues on how to implement it.
Here is what I have done:
My entity classes:
Order.java
public class Order {
private int id;
private Set<OrderLine> lines = new HashSet<OrderLine>();
// Setters & Getters
}
OrderLine.java
public class OrderLine {
private OrderLineId id;
private String name;
private Order order;
// Setters & Getters
}
OrderLineId.java
public class OrderLineId implements Serializable{
private int lineId;
private int orderId;
private int customerId;
// Setters & Getters
}
My mapping file which is having issues:
<hibernate-mapping>
<class name="Order" table="TEST_Order">
<id name="id" type="int" column="id">
<generator class="native"/>
</id>
<set name="lines" cascade="all">
<key column="orderId"/>
<one-to-many class="OrderLine"/>
</set>
</class>
<class name="OrderLine" table="TEST_OrderLine">
<composite-id name="id" class="OrderLineId">
<key-property name="lineId"/>
<key-property name="orderId"/>
<key-property name="customerId"/>
</composite-id>
<property name="name"/>
<many-to-one name="order" class="Order"
insert="false" update="false">
<column name="orderId"/>
<column name="customerId"/>
</many-to-one>
</class>
</hibernate-mapping>
When I am trying to create a session factory which parses this mapping file, I am getting an exception saying:
Caused by: org.hibernate.MappingException: Foreign key (FK_89b4nqt5l2n6tfd1d5tq0ill0:TEST_OrderLine [orderId,customerId])) must have same number of columns as the referenced primary key (TEST_Order [id])
at org.hibernate.mapping.ForeignKey.alignColumns(ForeignKey.java:110)
at org.hibernate.mapping.ForeignKey.alignColumns(ForeignKey.java:93)
at org.hibernate.cfg.Configuration.secondPassCompileForeignKeys(Configuration.java:1816)
at org.hibernate.cfg.Configuration.originalSecondPassCompile(Configuration.java:1739)
at org.hibernate.cfg.Configuration.secondPassCompile(Configuration.java:1424)
at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:1844)
Can someone please help me how to implement the example given in the documentation.
The OrderLine needs to reference the Order PK, which is not a composite key.
It means the many-to-one must be:
<many-to-one name="order" class="Order"
insert="false" update="false">
<column name="orderId"/>
</many-to-one>
The orderId is the FK to Order.id.
Then the one-to-many side will become:
<set name="lines" cascade="all">
<key>
<column name="orderId"/>
</key>
<one-to-many class="OrderLine"/>
</set>
So even if the OrderLine has a composite-key, the reference is made after Order.id, which is a simple key.
If you want to map other association to OrderLine, like OrderLineProduct then you'll need to use the composite-key to map the association between the parent (OrderLine) and the child (OrderLineProduct), so that OrderLineProduct has a composite-foreign-key back to OrderLine.
In both the table hbm mapping you should have same no. of column ,what you are using to make composite.You need to add these 3 column in TEST_Oder.
Example to follow :
<many-to-one name="orderLine" class="OrderLine">
<column name="lineId"/>
<column name="orderId"/>
<column name="customerId"/>
</many-to-one>
Refer same URL
Related
I cannot get it to work. The error says Broken column mapping. It has to be one-sided one-to-one mapping. Only 1 part of composite-id is connected to the other entity. Channel gonna be inserted based on logged user's access channel. I do have 2 classes.
public class UserModel implements Serializable{
private final Long id;
private final UserLimitsModel userLimitsModel;
}
public class UserLimitsModel implements Serializable{
private final Long userId;
private final String channel;
}
Then 2 hibernate .xml mappings
<hibernate-mapping>
<class name="abcdefgh.UserLimitsModel" table="USER_LIMITS" lazy="false">
<composite-id>
<key-property name="userId" type="long">
<column name="USER_ID" not-null="true"/>
</key-property>
<key-property name="channel" type="string" length="20">
<column name="CHANNEL" not-null="true"/>
</key-property>
</composite-id>
</class>
</hibernate-mapping>
<hibernate-mapping>
<class name="abcdefgh.UserModel" table="USER" lazy="false">
<id name="id" type="long">
<column name="ID" precision="19" scale="0" />
<generator class="sequence">
<param name="sequence_name">ID_SEQ</param>
</generator>
</id>
<one-to-one name="userLimitsModel" class="abcdefgh.UserLimitsModel" cascade="all"/>
</class>
</hibernate-mapping>
So the only connection is UserModel.id = UserLimitsModel.userId, while channel stays separate.
Hi the problem is that you are not joining with the primary key. Also the column name in UserModel is ID and in UserLimitsModel is USER_ID. You need to tell hibernate how to perform the join
you can do this by defining property-ref on the one-to-one element.
<one-to-one name="userLimitsModel" class="abcdefgh.UserLimitsModel" property-ref="userId" cascade="all"/>
property-ref (optional): the name of a property of the associated
class that is joined to the primary key of this class. If not
specified, the primary key of the associated class is used.
I'm trying to achieve lazy load on the following.
User.java
public class User {
private int id;
private String userName;
private String password;
private Employee employee;
//getter and setters
}
User.hbm.xml
<hibernate-mapping>
<class name="com.site.dto.User" table="user">
<id name="id" type="java.lang.Integer">
<column name="id" />
<generator class="identity" />
</id>
<property name="userName" type="string" update="false">
<column name="user_name" length="50" not-null="true" unique="true" />
</property>
<property name="password" type="string">
<column name="password" length="50" not-null="true" unique="true" />
</property>
<one-to-one name="employee" class="com.site.dto.Employee" fetch="select" cascade="save-update" />
</class>
</hibernate-mapping>
Employee.java
public class Employee implements Serializable{
private int id;
private String name;
private String email;
private User user;
// getter and setters
}
Employee.hbm.xml
<hibernate-mapping>
<class name="com.site.dto.Employee" table="employee">
<id name="id" type="java.lang.Integer">
<column name="id" />
<generator class="identity" />
</id>
<property name="name" type="string">
<column name="name" length="50" not-null="true" unique="true" />
</property>
<property name="email" type="string" update="false">
<column name="email" length="50" not-null="true" unique="true" />
</property>
// enforcing many-to-one to one-to-one by putting unique="true"
<many-to-one name="user" column="user_id" class="com.site.dto.User" unique="true" not-null="true" fetch="select" cascade="save-update" />
</class>
</hibernate-mapping>
First I'm getting the User Object based on username. Now I'm trying to load the employee object which gives me null pointer exception. So after digging on some debug, it seems to be using a select statement with wrong where clause. Here is the hibernate debug
select employee0_.id as id1_1_0_, employee0_.name as name2_1_0_, employee0_.email as email3_1_0_,employee0_.user_id as user_id25_1_0_, from employee employee0_ where employee0_.id=?
Why is the where clause is based on employee.id and not employee.user.id ? I think this is due to the reason on how one-to-one mapping works in hbm.xml configuration where one-to-one will be linked to child table's primary key id but not user_id. I'm forcing the many-to-one to one-to-one in employee by using unique="true". I can fetch the employee in Hibernate annotation's one-to-one by defining #Join-column but I can't figure out how to map the one-to-one in hbm.xml which should refer the child's user_id.
Figured out the solution a while back, but forget to post it.
The above problem is coz, by default one-to-one mapping will be implemented for a child table which have the parent's primary key as the Child's primary key. So if we're going to eliminate that default property and use one-to-one with many-to-one (unique=true), we should define property-ref
I've added property-ref in one-to-one mapping in User.hbm.xml and now it works fine.
<one-to-one name="employee" property-ref="user" class="com.site.dto.Employee" fetch="select" cascade="save-update" />
I know there are loads of entries about this topic. I have already read all of them searching for the problem I am facing.
I have a class/table with a self-reference. This is the Class
public class Comment {
private Integer id;
private Comment parent;
private Issue issue;
private User author;
private String body;
private Date created;
private Date updated;
private Set<Comment> childs;
// All setters and getters
}
And here you can see the hbm.xml file:
<hibernate-mapping>
<class name="Comment" table="COMMENTS">
<id name="id" type="java.lang.Integer">
<column name="ID"/>
<generator class="native"/>
</id>
<many-to-one cascade="all"
class="Comment" fetch="join" name="parent">
<column name="PARENT" not-null="false"/>
</many-to-one>
<many-to-one class="Issue"
fetch="join" name="issue">
<column name="ISSUE" not-null="true"/>
</many-to-one>
<many-to-one class="User"
fetch="join" name="author">
<column name="AUTHOR" not-null="true"/>
</many-to-one>
<property generated="never" lazy="false" name="body" type="java.lang.String">
<column name="BODY" not-null="true"/>
</property>
<property generated="never" lazy="false" name="created" type="java.util.Date">
<column name="CREATED"/>
</property>
<property generated="never" lazy="false" name="updated" type="java.util.Date">
<column name="UPDATED"/>
</property>
<set cascade="delete" fetch="select" inverse="true" lazy="true"
name="childs" sort="unsorted" table="COMMENTS">
<key>
<column name="ID" not-null="true"/>
</key>
<one-to-many class="Comment"/>
</set>
</class>
</hibernate-mapping>
So far everything is fine. But I must have an error somewhere because, when I am running this unit test
Session session = sessionFactory.getCurrentSession();
User user = new User("loginName", "password", "firstName", "lastName", "eMail");
session.save(user);
session.flush();
session.clear();
Issue issue = new Issue();
session.save(issue);
session.flush();
session.clear();
Comment parent = new Comment(issue, user, "body_parent");
session.save(parent);
Comment child = new Comment(issue, user, "body_child_1");
child.setParent(parent);
parent.getChilds().add(child);
session.save(child);
session.flush();
session.clear();
parent = (Comment) session.createQuery("from Comment comment where comment.body='body_parent'").uniqueResult();
System.out.println(parent);
System.out.println(parent.getChilds().iterator().next());
I added the last two lines in order to show you the problem I am facing right now:
COMMENT = [id=1, parentId=<root>, issueId=1, authorId=1, body=body_parent, created=2014-03-08 19:28:54.832, updated=2014-03-08 19:28:54.832, numChilds=1]
COMMENT = [id=1, parentId=<root>, issueId=1, authorId=1, body=body_parent, created=2014-03-08 19:28:54.832, updated=2014-03-08 19:28:54.832, numChilds=1]
Both parent and child are the same! I don't really understand the problem here. When I retrieve the child through a query the result is correct but when I get the child through getChild() there's not query to retrieve the its childs.
Have you any idea? Any clue? I do not see the light at the end of the tunnel here :-/
Thanks a lot in advance!
I finally have realized what the problem was! Well, there was couple of problems.
First, there was no parent_id field in neither the DB nor the class.
Second, there was no exception because I activated inverse property to the set.
The combination of both issues was the problem.
Below, I post the working solution.
Comment.java
public class Comment {
private Integer id;
private Integer parentId; // Parent ID was missing
private Issue issue;
private User author;
private String body;
private Date created;
private Date updated;
private Comment parent; // Reference to parent though parentId
private Set<Comment> childs = new HashSet<Comment>(0);
protected Comment () {
}
// All setters and getters
}
Comment.hbm.xml
<hibernate-mapping>
<class name="es.kazbeel.geckobugtracker.model.Comment" table="COMMENTS">
<id name="id" type="java.lang.Integer">
<column name="ID"/>
<generator class="native"/>
</id>
<property name="parentId" type="java.lang.Integer" update="false" insert="false" column="PARENT_ID" />
<many-to-one class="es.kazbeel.geckobugtracker.model.Issue"
fetch="join" name="issue">
<column name="ISSUE"/>
</many-to-one>
<many-to-one class="es.kazbeel.geckobugtracker.model.User"
fetch="join" name="author">
<column name="AUTHOR"/>
</many-to-one>
<property generated="never" lazy="false" name="body" type="java.lang.String">
<column name="BODY"/>
</property>
<property generated="never" lazy="false" name="created" type="java.util.Date">
<column name="CREATED"/>
</property>
<property generated="never" lazy="false" name="updated" type="java.util.Date">
<column name="UPDATED"/>
</property>
<many-to-one name="parent" class="es.kazbeel.geckobugtracker.model.Comment" column="PARENT_ID" not-null="false" />
<set name="childs" table="COMMENTS" lazy="false" cascade="all-delete-orphan" inverse="false">
<key column="PARENT_ID" />
<one-to-many class="es.kazbeel.geckobugtracker.model.Comment"/>
</set>
</class>
</hibernate-mapping>
I hope this is helpful for someone in the future. This is the first time I post a self-solution :)
Take a shot using the code below:
Comment parent = new Comment(issue, user, "body_parent");
Comment child = new Comment(issue, user, "body_child_1");
child.setParent(parent);
parent.getChilds().add(child);
session.save(parent);
session.flush();
session.clear();
from this
your issue will be resolved by properly defining cascading depedencies
or by saving the referenced entities before saving the entity that
references. Defining cascading is really tricky to get right because
of all the subtle variations in how they are used.
cascade="delete"
check the options all|none|save-update|delete|all-delete-orphan
session.save(child); instead of this try cascade="all" and session.save(parent) so that all child objects will be saved with parent.
I am developing a e-shopping program with the help of hibernate. Because I am a very fresh beginner on jsp, servlet, hibernate, I don't know how to do many-to-many relation mapping with additional column in the relation table. I have read through several tutorial but most of them are focus on the annotation mapping and the documentation on hibernate community but none of them suit my situation.
I have three tables movie_information, order_ and order_details(the relationship) where the structure of order_details is as the following.
order_details
-------------
order_id FK
movie_id FK
quantity
-------------
I am trying to use four POJO classes to achieve the many-to-many relation. Here comes the code.
MovieInformation.java
// other variable, constructor, getter, setter
private Set<OrderDetails> orderDetails = new HashSet<OrderDetails>();
Order.java
// other variable, constructor, getter, setter
private Set<OrderDetails> orderDetails = new HashSet<OrderDetails>();
OrderDetails.java
private OrderDetailsPK primaryKey;
private Order order;
private MovieInformation movie;
private int quantity;
OrderDetailsPK.java
private long orderId;
private long movieId;
MovieInformation.hbm.xml
<id column="movie_id" name="movieId" type="long">
<generator class="native"/>
</id>
// some property
<set name="orderDetails" table="order_details" inverse="true" lazy="true" fetch="select">
<key>
<column name="movie_id" not-null="true" />
</key>
<one-to-many class="cart.hibernate.orderDetails.orderDetails" />
</set>
Order.hbm.xml
<id column="order_id" name="orderId" type="long">
<generator class="native"/>
</id>
//some properties
<set name="orderDetails" table="order_details" inverse="true" lazy="true" fetch="select">
<key>
<column name="order_id" not-null="true" />
</key>
<one-to-many class="cart.hibernate.orderDetails.orderDetails" />
</set>
OrderDetails.hbm.xml
<composite-id name="orderDetailsPK" class="cart.hibernate.orderDetailsPK.OrderDetailsPK">
<key-property column="order_id" name="orderId" type="long"/>
<key-property column="movie_id" name="movieId" type="long"/>
</composite-id>
<many-to-one name="order" class="cart.hibernate.order.Order" insert="false" update="false"/>
<many-to-one name="movie" class="cart.hibernate.movieInformation.MovieInformation" insert="false" update="false"/>
<property column="quantity" name="quantity" type="int"/>
OrderDetailsPK.hbm.xml
<class name="cart.hibernate.orderDetailsPK.OrderDetailsPK" >
<property name="orderId" type="long"/>
<property name="movieId" type="long"/>
hibernate.cfg.cml
<mapping class="cart.hibernate.MovieInformation" package="cart.hibernate.movieInformation" resource="cart/hibernate/movieInformation/MovieInformation.hbm.xml"/>
<mapping class="cart.hibernate.Order" package="cart.hibernate.order" resource="cart/hibernate/order/Order.hbm.xml"/>
<mapping class="cart.hibernate.OrderDetails" package="cart.hibernate.orderDetails" resource="cart/hibernate/orderDetails/OrderDetails.hbm.xml"/>
<mapping class="cart.hibernate.OrderDetailsPK" package="cart.hibernate.orderDetailsPK" resource="cart.hibernate.orderDetailsPK/OrderDetailsPK.hbm.xml"/>
When I try to run the testing main method, exception thrown from the program
887 [main] INFO org.hibernate.cfg.Configuration - Reading mappings from resource :
cart.hibernate.orderDetailsPK/OrderDetailsPK.hbm.xml
Initial SessionFactory creation failed.org.hibernate.MappingNotFoundException: resource: cart.hibernate.orderDetailsPK/OrderDetailsPK.hbm.xml not found
Exception in thread "main" java.lang.ExceptionInInitializerError
at cart.hibernate.HibernateUtil.buildSessionFactory(HibernateUtil.java:28)
at cart.hibernate.HibernateUtil.<clinit>(HibernateUtil.java:18)
at cart.hibernate.order.ManageOrder.main(ManageOrder.java:40)
Caused by: org.hibernate.MappingNotFoundException: resource: cart.hibernate.orderDetailsPK/OrderDetailsPK.hbm.xml not found
at org.hibernate.cfg.Configuration.addResource(Configuration.java:799)
at org.hibernate.cfg.Configuration.parseMappingElement(Configuration.java:2344)
at org.hibernate.cfg.Configuration.parseSessionFactory(Configuration.java:2310)
at org.hibernate.cfg.Configuration.doConfigure(Configuration.java:2290)
at org.hibernate.cfg.Configuration.doConfigure(Configuration.java:2243)
at org.hibernate.cfg.Configuration.configure(Configuration.java:2158)
at cart.hibernate.HibernateUtil.buildSessionFactory(HibernateUtil.java:23)
... 2 more
After I read through the question ask by the others, I believe that the error in my code is caused by wrong mapping code. Hope you guys could help me to solve this problem. It would be nice if some simple examples of many-to-many mapping with extra column are provided.
I believe that your code is clean because the console told you MappingNotFoundException Once it loaded the hibernate file, it couldn't find it. Make sure that the hibernate file in the package of the model bean is not in the source.
On the database, I have these two tables:
- Destination:
- idDestination
- name
- Airport:
- idAirport
- idDestination // FK into Destination city
- name
where:
1 Destination(read: city) has many Airports
1 Airport belongs to 1 City
Hence: 1-Many relation between Destination-Airports
My Java classes look like this:
class Destination{
private Integer idDestination;
private String name;
// getter and setters
}
class Airport{
private Integer idAirport;
private Destination city;
private String name;
}
// Separate class for airports in city, since city is being used in a lot of other places
// and I'd like to keep Destination class clean
class CityAirports{
private Destination City;
private Set<Airport> airports;
}
Hibernate Mappings: Airport.hbm.xml
<hibernate-mapping>
<class name="org.wah.dao.Airport" table="AIRPORT">
<id name="idAirport" type="java.lang.Integer">
<column name="IDAIRPORT" />
<generator class="identity" />
</id>
<property name="name" type="java.lang.String">
<column name="NAME" />
</property>
<many-to-one name="city" class="org.wah.dao.Destination">
<column name="IDCITY" />
</many-to-one>
</class>
</hibernate-mapping>
I need to define another mapping for CityAirports to
- retrieve all the airports within the city.
- add a new airport to the city.
I am not sure what the hibernate mapping would look like ? Can someone please guide me on how to do it?
for destination mapping,
<hibernate-mapping>
<class name="org.wah.dao.Destination" table="DESTINATION">
<id name="idDestination" type="int" column="DESTINATION_ID">
<generator class="native" />
</id>
<property name="name" type="string" not-null="true" length="100"
column=DESTINATION_NAME" />
<set name="destinationAirPorts" table="DESTINATION_AIRPORT" cascade="all">
<key column="DESTINATION_ID" />
<many-to-many column="IDAIRPORT" unique="true" class="org.wah.dao.Airport" />
</set>
</class>
for airport mapping,
<hibernate-mapping>
<class name="org.wah.dao.Airport" table="AIRPORT">
<id name="idAirport" type="int" column="IDAIRPORT">
<generator class="native" />
</id>
<property name="name" type="string" not-null="true" length="100"
column=NAME" />
</class>
</hibernate-mapping>
Read the example mentioned here which describes the solution you are looking for neatly.
Basic idea is that you use many-to-many relationship from city-to-airport and place unique constraint on IDAIRPORT
Could be something like this ??
<class name="org.wah.dao.CityAirports">
<id name="caID">
<generator class="assigned" />
</id>
<set name="Airport" cascade="save-update" >
<key column="apID" />
<one-to-many class="org.wah.dao.Airport" />
</set>
<many-to-one name="city" class="org.wah.dao.Destination">
<column name="IDCITY" />
</many-to-one>
</class>
Is the CityAirport class really necessary? Why not just put a list of Airports into your Destination?
Not sure about the .xml configuration for this. But using annotations, you would get something like this:
#Entity
public class Destination {
#Id
#GeneratedValue
private Integer idDestination;
private String name;
#OneToMany(mappedBy="destination")
Set<Airport> airports;
[...]
}
And the Airport class:
#Entity
public class Airport {
#Id
#GeneratedValue
private Integer id;
#ManyToOne
private Destination destination;
[...]
}