Hibernate One-to-many HashMap not updating on child - java

I have the following parent object which maps to a table in my database:
public Parent {
private Long id;
private String mid;
private Integer days;
private BigDecimal fee;
private DateTime createdDate = new DateTime();
private DateTime lastModifiedDate;
private Map<String, Child> children;
}
With the following .hbm.xml:
<hibernate-mapping default-access="field">
<class name="Parent" table="parent_table">
<id column="id" length="50" name="id" unsaved-value="null">
<generator class="increment"/>
</id>
<property length="50" name="mid"/>
<property name="days"/>
<property name="fee"/>
<property name="createdDate" type="(...)PersistentDateTime"/>
<property name="lastModifiedDate" type="(...)PersistentDateTime"/>
<map cascade="all-delete-orphan" inverse="true" name="children" >
<key column="parentId" />
<map-key column="country" type="string" />
<one-to-many class="Child" />
</map>
</class>
</hibernate-mapping>
The child object is as follows:
public class Child implements Serializable {
private Long parentId;
private String country;
private String cu;
}
With the following .hbm.xml:
<hibernate-mapping default-access="field">
<class name="Child" table="child_table">
<composite-id>
<key-property name="parentId"/>
<key-property name="country"/>
<key-property name="cu"/>
</composite-id>
</class>
</hibernate-mapping>
After acquiring a Parent object from my db via:
getSession().createCriteria(Parent.class)
.add(Restrictions.eq("mid", mid))
.uniqueResult();
After making some changes to Child.cu in the children map I call a saveOrUpdate on the Parent object. After doing so all appears to save / update fine but upon checking the child_table in the db, these changes have not been saved / updated.
I believe this has something to do with the mappings of the map in the Parent class but can't seem to figure it out. Any help would be appreciated.
Thanks in advance.

If I understand correctly, you're modifying a field which is part of the primary key of your entity. This is illegal: the ID should be immutable.
My advice is to follow the good practices: use a non-composite, purely technical, auto-generated primary key. Everything will be much simpler (and also faster).

Related

Is it required to create a DTO for UI for composite identifier class also in Hibernate?

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:
<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"/>
</many-to-one>
</class>
</hibernate-mapping>
I have created Separate DTO's for Order and OrderLine. Do I need to create a separate DTO for OrderLineId (which is a composite Identifier class as mentioned above) as well ?

Hibernate Criteria API where clause in many to many relation

I have relation many to many with extra fileds in linking table(mapping in xml files). Using criteria api how to add restrictions to name of product?
public class Recipe implements Serializable{
private int id_re;
private String name;
private Set<ProductRecipe> listOfRecipe_Product = new HashSet<>(0);
}
public class ProductRecipe implements Serializable{
private ProductRecipeMapping id;
private float quantity;
}
public class ProductRecipeMapping implements Serializable{
private Product product;
private Recipe recipe;
}
public class Product implements Serializable{
private int id_p;
private String name;
}
Mapping:
<class entity-name="recipe" name="Recipe" table="recipe">
<id name="id_re" type="java.lang.Integer">
<column name="id_re" />
<generator class="identity" />
</id>
<set name="listOfRecipe_Product" table="recipe_product" lazy="false" fetch="select" cascade="all">
<key>
<column name="id_re" not-null="true" />
</key>
<one-to-many entity-name="productRecipe" />
</set>
</class>
<class entity-name="productRecipe" name="ProductRecipe" table="recipe_product">
<composite-id name="id" class="ProductRecipeMapping" >
<key-many-to-one name="recipe" entity-name="recipe" column="id_re" />
<key-many-to-one name="product" entity-name="product" column="id_p" />
</composite-id>
<property name="quantity" type="float" column="quantity" />
</class>
<class entity-name="product" name="Product" table="product">
<id name="id_p" type="java.lang.Integer" column="id_p">
<generator class="identity" />
</id>
<property name="name" column="name" type="java.lang.String" not-null="true" length="255"/>
</class>
E.G. I use criteria for get recipe with name test:
Criteria cr = session.createCriteria(Recipe.class);
cr.add(Restrictions.eq("name", "test"));
but I don't know to get all recipes with list name of products
something like cr.add(Restrictions.eq("product.name", "test")); (but not work)
I use 2 idea to resolve this problem but nothing work:
1) Restrictions.eq("listOfRecipe_Product.id.product.name", "test")
but i get error org.hibernate.QueryException: could not resolve property: listOfRecipe_Product.id.product.name of: recipe
2)
cr.createCriteria("listOfRecipe_Product")
.createCriteria("id")
.createCriteria("product")
.add(Restrictions.eq("name", "test"));
I get error org.hibernate.QueryException: Criteria objects cannot be created directly on components. Create a criteria on owning entity and use a dotted property to access component property: listOfRecipe_Product.id
You will need to create aliases in order to add constraints to mapped entities;
e.g.:
Criteria cr = session.createCriteria(Recipe.class)
.createAlias("listOfRecipe_Product", "listOfRecipe_Product")
.createAlias("listOfRecipe_Product.id", "id")
.createAlias("id.product", "product")
.add(Restrictions.eq("product.name", "test"));

Hibernate criteria specific query

I have such entity:
public class LogEntity implements Serializable {
private static final long serialVersionUID = 1L;
private Integer id;
private Date changeTime;
private Set<Integer> changes;
//getters and setters
}
And its mapping:
<hibernate-mapping>
<class name="com.myproject.dao.LogEntity" table="LOG">
<id name="id" column="ID" type="integer">
<generator class="native" />
</id>
<property name="changeTime" column="CHANGE_TIME" type="timestamp"/>
<set name="changes" table="LOG_CHANGES" lazy="false" cascade="all" order-by="CHANGE_ID">
<key column="ID"/>
<element column="CHANGE_ID" type="integer"/>
</set>
</class>
The problem is how to check that Set<Integer> changes contains given parameter using hibernate criteria. I can't createAlias() for this and also try to use sqlRestriction() but get something ugly.
I am pretty sure that there is some easy way but I just cant see it. Thanks for any help.

LazyInitializationException with map collection

I know that it is a trite question, but I could not find a solution. I have two beans and one of them has HashMap collection. I'm getting an exception when trying to read this collection. Mapping config had been specified to load this collection eagerly.
My environment is :
Hibernate 4.2.0
mysql-connector-java 5.1.24
Also I have two beans:
public class FeaturedDoc {
private Long id;
private Map<Feature, Float> features;
public FeaturedDoc() {
features = new HashMap<Feature, Float>();
}
(getters and setters)
}
and
public class Feature {
private Long id;
private String name;
private Long internalId;
(getters and setters)
}
This beans have mapping:
<class name="Feature" table="FEATURE">
<id name="id" type="long" column="ID">
<generator class="increment"/>
</id>
<property name="name" length="255" type="string" unique="true" column="NAME" index="INDEX_NAME"/>
<property name="internalId" type="long" unique="true" not-null="false" column="INTERNAL_ID" index="INDEX_INTID"/>
<sql-insert>insert into FEATURE (NAME, INTERNAL_ID, ID) values (?, ?, ?) on duplicate key update ID = ID</sql-insert>
</class>
<class name="FeaturedDoc" table="FEATURED_DOC">
<id name="id" type="long" column="ID">
<generator class="increment"/>
</id>
<map name="features" table="DOC_FEATURE" cascade="all" lazy="false" fetch="join">
<key column="ID"></key>
<map-key-many-to-many column="FEATURE_ID" class="Feature"/>
<element column="value" type="float"/>
</map>
</class>
Also I have DAO layer with method:
public FeaturedDoc read(long id) {
FeaturedDoc fd = null;
try {
session.beginTransaction();
fd = session.get(FeaturedDoc.class, id);
session.getTransaction().commit();
} catch (Exception e) {
e.printStackTrace();
session.getTransaction().rollback();
} finally {
close();
}
return fd;
}
When I'm trying to do something like this:
FeaturedDoc fd = daoService.read(26);
System.out.println(fd.getFeatures());
I'm getting an exception
org.hibernate.LazyInitializationException: could not initialize proxy - no Session
Do you know how should I fix this error?
Assuming that everything else is okay (in the mappings), have you tried putting lazy="false" before cascade="all". I found that this was a problem in my mapping which resulted in this LazyInitializationException error.
This ordering is shown in the following Hibernate Reference: http://docs.jboss.org/hibernate/orm/3.3/reference/en/html/collections.html
I have solved this problem! The reason was in Feature class. It had not hashCode and equals functions. After implementation of these functions everything has become ok.

How to save parent with children?

I have parent entity Order with child entity OrderItem. I want to save object Order together with Set<OrderItem>, but idOrder is auto increment. Between Order and OrderItem is one-to-many relationship. OrderItem include reference to parent Order. So Order item must be saved firstly and then must be saved OrderItem with assigned idOrder.
Here is Order.hbm.xml
<class name="hibernate.Order" table="order" catalog="my">
<id name="idOrder" type="java.lang.Integer">
<column name="id_order" />
<generator class="identity" />
</id>
<set name="orderItems" inverse="true" cascade="all">
<key>
<column name="id_order" not-null="true" />
</key>
<one-to-many class="hibernate.OrderItem" />
</set>
</class>
class Order
public class Order implements java.io.Serializable {
private Integer idOrder;
private Set<OrderItem> orderItems = new HashSet<OrderItem>(0);
}
Here is OrderItem.hbm.xml
<class name="hibernate.OrderItem" table="order_item" catalog="my">
<id name="idOrderItem" type="java.lang.Integer">
<column name="id_order_item" />
<generator class="identity" />
</id>
<many-to-one name="order" class="hibernate.Order" fetch="select">
<column name="id_order" not-null="true" />
</many-to-one>
</class>
class OrderItem
public class OrderItem implements java.io.Serializable {
private Integer idOrderItem;
private Order order;
}
When I try save it, it throw me exception.
object references an unsaved transient instance - save the transient instance before flushing: hibernate.Order
EDIT:
It works with following statement. Is this correct?
Order order = mapper.map(dtoOrder, Order.class);
Set<OrderItem> orderItems = order.getOrderItems();
for (OrderItem orderItem : orderItems) {
orderItem.setOrder(order);
}
order.setOrderItems(orderItems);
session.save(order);
so basically there are 3 workarounds here.
1.Save Order rather than OrderItem, because regarding to your current hibernate configuration the cascading navigates from Order to OrderItem;
2.have a cascade=save-update in OrderItem.hbm.xml , and then saving an OrderItem will automatically save Order; but this is not recommented ;
3.for some reason,if you insist to save OrderItem and don't want to change your hibernate confiuraion, I'm afraid you have to explicitly save Order first and then invoke session.flush() to get Order instance persistent before saving OrderItem;
I hope this make sense.

Categories