I m using Spring's HibernateDAOSupport class to handle my DAO.
my problem is that when I use getHibernateSupport().save(order) when order already exist in the database, it just update the database row instead of throwing some kind of Exception.
my Hibernate hbm.xml file is :
<hibernate-mapping>
<class name="com.shopping.db.CustomerOrder" table="CUSTOMERORDER" schema="PUBLIC">
<id name="orderID" type="long">
<column name="ORDERID" length="50" />
</id>
<many-to-one name="customer" class="com.shopping.db.Customer" fetch="select">
<column name="USERNAME" length="50" not-null="true" />
</many-to-one>
<property name="status" type="string">
<column name="STATUS" length="50" not-null="true" />
</property>
<property name="totalCost" type="float">
<column name="TOTALCOST" precision="0" scale="0" not-null="true" />
</property>
<property name="orderDate" type="java.sql.Date">
<column name="ORDERDATE" not-null="true" />
</property>
</class>
</hibernate-mapping>
my DAO code is :
public void createDBOrder(CustomerOrder order, List<OrderItem> orderItems)
{
getHibernateTemplate().save(order);
for (OrderItem item : orderItems)
{
getHibernateTemplate().save(item);
}
getHibernateTemplate().flush();
}
my test method is as follow:
#Test
public void testCreateDBOrder()
{
int ordersCount = countRowsInTable("CUSTOMERORDER");
int orderItemsCount = countRowsInTable("ORDERITEM");
// Check for row count before insertion.
assertEquals(1, ordersCount);
assertEquals(1, orderItemsCount);
CustomerOrder order = new CustomerOrder(3, dataAccessObject
.getCustomerByName("Oussama"), "PENDING", 200, new Date(new Long(
"61191781200000")));
OrderItem item = new OrderItem(333, dataAccessObject.getProductByID("Apple iPhone"), order, 3);
OrderItem item2 = new OrderItem(444, dataAccessObject.getProductByID("DV2000"), order, 2);
List<OrderItem> items = new ArrayList<OrderItem>();
items.add(item);
items.add(item2);
dataAccessObject.createDBOrder(order, items);
ordersCount = countRowsInTable("CUSTOMERORDER");
orderItemsCount = countRowsInTable("ORDERITEM");
// Check for row count after insertion.
assertEquals(2, ordersCount);
assertEquals(3, orderItemsCount);
assertEquals(3, dataAccessObject.getOrderByID(3).getOrderID());
assertEquals(2, dataAccessObject.getOrderItemByOrderID(3).size());
order.setStatus("SHIPPED");
// Check for creating a order with the same ID.
dataAccessObject.createDBOrder(order, items);
assertEquals("SHIPPED", dataAccessObject.getOrderByID(3).getStatus());
ordersCount = countRowsInTable("CUSTOMERORDER");
// Check for row count after insertion.
//assertEquals(3, ordersCount);
}
Why is getHibernateTemplate().save() is just updating the database row instead if throwing an exception because of primary key already exists.
The second save is ingored, because you are saving the same instance of CustomerOrder which already have been saved (i.e. a persistent object). If you try to save the different instance (i.e. a transient object) with the same id, you'll get an exception.
Related
I have a table modeled in a legacy .hbm.xml file.
The legacy code to retrieve a row uses an org.hibernate.Criteria to get a uniqueResult(). In migrating to Hibernate 5.x, Criteria is deprecated so I am trying to use CriteriaBuilder to achieve the same. However when I try to add restrictions (Hib 5.x) based on what worked previously (Hib 4.x) I get an IllegalArgumentException:
java.lang.IllegalArgumentException: Unable to locate Attribute with the the given name [xyzKey.plantName] on this ManagedType [com.foo.bar.Plant]
at org.hibernate.metamodel.model.domain.internal.AbstractManagedType.checkNotNull(AbstractManagedType.java:147)
at org.hibernate.metamodel.model.domain.internal.AbstractManagedType.getAttribute(AbstractManagedType.java:118)
at org.hibernate.metamodel.model.domain.internal.AbstractManagedType.getAttribute(AbstractManagedType.java:43)
at org.hibernate.query.criteria.internal.path.AbstractFromImpl.locateAttributeInternal(AbstractFromImpl.java:111)
at org.hibernate.query.criteria.internal.path.AbstractPathImpl.locateAttribute(AbstractPathImpl.java:204)
at org.hibernate.query.criteria.internal.path.AbstractPathImpl.get(AbstractPathImpl.java:177)
Plant.hbm.xml:
<hibernate-mapping>
<class lazy="false" name="com.foo.bar.Plant" table="Plant">
<meta inherit="false" attribute="extends">com.foo.bar.PlantBase</meta>
<id name="id" type="integer" column="plantID" unsaved-value="null">
<meta inherit="false" attribute="scope-set">protected</meta>
<generator class="native" />
</id>
<component name="xyzKey" class="com.foo.bar.PlantKey">
<meta inherit="false" attribute="use-in-tostring">true</meta>
<property name="plantName" type="string" index="xyzKeyndx" unique-key="plantKey">
<column name="plantName" />
</property>
<property name="xyzRevision" type="string" index="xyzKeyndx" unique-key="plantKey">
<column name="xyzRevision" length="100"/>
</property>
</component>
<property name="active" type="java.lang.Boolean" index="xyzKeyndx">
<column name="active" not-null="true"/>
</property>
<property name="description" type="string" not-null="false">
<meta inherit="false" attribute="field-description">User specified description. Does not need to be unique.</meta>
<meta inherit="false" attribute="use-in-tostring">false</meta>
<column name="descr" not-null="false" />
</property>
<property name="location" type="string" not-null="true">
<column name="location" />
</property>
</class>
</hibernate-mapping>
Hibernate 4.x based code that works:
protected Plant selectPlant(Session session, PlantKey xyzKey)
{
Criteria c = session.createCriteria(Plant.class);
if (Util.isEmpty(xyzKey.getXyzRevision()))
{
SimpleExpression plantName = Restrictions.eq("xyzKey.plantName", xyzKey.getPlantName());
SimpleExpression active = Restrictions.eq("active", true);
c.add(Restrictions.and(plantName, active));
}
else
{
c.add( Restrictions.eq("xyzKey", xyzKey) );
}
Plant plant = (Plant)c.uniqueResult();
return plant;
}
Hibernate 5.x based code that fails:
protected Plant selectPlant(Session session, PlantKey xyzKey)
{
ElapsedTimer timer = new ElapsedTimer();
CriteriaBuilder criteriaBuilder = session.getCriteriaBuilder();
CriteriaQuery<Plant> criteriaQuery = criteriaBuilder.createQuery(Plant.class);
Root<Plant> root = criteriaQuery.from(Plant.class);
List<Predicate> restrictions = new ArrayList<>();
if ( FBCUtil.isEmpty(vpeKey.getVpeRevision()) )
{
restrictions.add(criteriaBuilder.equal(root.get("xyzKey.plantName"), xyzKey.getPlantName())); // FAILS HERE!
restrictions.add(criteriaBuilder.equal(root.get("active"), true));
}
else
{
restrictions.add(criteriaBuilder.equal(root.get("xyzKey"), xyzKey));
}
criteriaQuery.where(restrictions.toArray(new Predicate[restrictions.size()]));
Query<Plant> query = session.createQuery(criteriaQuery);
Plant plant = query.uniqueResult();
return plant;
}
Did you try
criteriaBuilder.equal(root.get("xyzKey").get("plantName"), xyzKey.getPlantName())
I am trying to check if a row exists in my database.
Word word = (Word) session.createQuery("select 1 from Word w where w.content = :key").setParameter("key",words[i]).uniqueResult();
I'm also trying:
Word word = session.get(Word.class,contentId);
Besides that I tried session.load,and some others. Everytime Hibernate returns error:
Error indexing: null
or
Error indexing: no row with the given identifier exists.
It is true, row does not existing but why doesn't it just returns null like it should for session.get:
http://docs.jboss.org/hibernate/orm/3.5/javadocs/org/hibernate/Session.html#get(java.lang.Class,%20java.io.Serializable).
In case of not finding a row I wanted to create one and add to a database but I'm not able to check if it exists.
EDIT:
Word.java
public class Word {
private String content;
private Set<Sentence> sentences;
empty constructor, setters and getters
}
Word.hbm.xml
<class name="Word">
<id name="content" column="wordId" type="string">
</id>
<set name="sentences" inverse="true">
<key><column name="wordId"/></key>
<many-to-many class="Sentence" column="sentenceId"/>
</set>
</class>
Sentence.java
public class Sentence {
private long id;
private ProcessedUrl processedUrl;
private List<Word> words;
empty constructor, setters and getters
}
Sentence.hbm.xml
<class name="Sentence">
<id name="id" column="sentenceId">
<generator class="native"/>
</id>
<many-to-one name="processedUrl" column="processedUrlId" not-null="true"/>
<list name="words">
<key>
<column name="sentenceId" not-null="true"/>
</key>
<list-index column="idx" />
<many-to-many class="Word">
<column name="wordId" not-null="true"/>
</many-to-many>
</list>
</class>
I have two classes :
Etudiant and Pays
In the database the table Etudiant has a foreign key for the table Pays.
In my code I have something like this :
List<Etudiant> listEtudiants = (List<Etudiant>) etudiantService.getAll();
for(Etudiant etudiant : listEtudiants) {
if(((JTextField)arg0.getSource()).getText().equals(etudiant.getNom())){
System.out.println(etudiant.getPays().getNom());
}
}
but when I run this code it fails with the exception:
org.hibernate.LazyInitializationException: could not initialize proxy - no Session
In the line :
System.out.println(etudiant.getPays().getNom());
Mapping for Etudiant:
<hibernate-mapping>
<class name="tp.ihm.domain.Etudiant" table="etudiant" schema="public" optimistic-lock="version">
<id name="numInsc" type="java.lang.Long">
<column name="num_insc" />
<generator class="assigned" />
</id>
<many-to-one name="pays" class="tp.ihm.domain.Pays" fetch="select">
<column name="pays" not-null="true" />
</many-to-one>
<property name="nom" type="string">
<column name="nom" length="50" not-null="true" />
</property>
<property name="prenom" type="string">
<column name="prenom" length="50" not-null="true" />
</property>
</class>
</hibernate-mapping>
Mapping for Pays:
<hibernate-mapping>
<class name="tp.ihm.domain.Pays" table="pays" schema="public" optimistic-lock="version">
<id name="id" type="java.lang.Long">
<column name="id" />
<generator class="assigned" />
</id>
<property name="nom" type="string">
<column name="nom" length="45" not-null="true" />
</property>
<set name="etudiants" table="etudiant" inverse="true" lazy="true" fetch="select">
<key>
<column name="pays" not-null="true" />
</key>
<one-to-many class="tp.ihm.domain.Etudiant" />
</set>
</class>
</hibernate-mapping>
I tried to remove the fetch attribute in the mapping for Pays, and then to change it's value to eager but nothing works.
Could someone please help me with this ?
Edit :
This is the code for the getAll method :
public List getAll() throws EntityNotFoundException {
// Get the current session
Session s = getSession();
List list = null;
// If the BLL layer started a transaction
// In this case it is the BLL layer that manages the session and transaction
if (anActiveTransactionExist(s)) {
list = s.createCriteria(Etudiant).list();
} else {
LOGGER.debug("DAO initialize its own transaction");
Transaction tx = null;
try {
// Starts a transaction locally
tx = s.beginTransaction();
list = s.createCriteria(boClass).list();
tx.commit();
} catch (RuntimeException ex) {
// Cancel the transaction if there is a problem
handleDaoOpError(tx, ex);
} finally {
closeSession(s);
}
}
if (list == null || list.size() == 0)
throw new EntityNotFoundException();
return list;
}
You need to change the mapping of Etudiant from fetch=select to fetch=join
fetch-“join” = Disable the lazy loading, always load all the collections and entities.
fetch-“select” (default) = Lazy load all the collections and entities.
<many-to-one name="pays" class="tp.ihm.domain.Pays" fetch="join">
<column name="pays" not-null="true" />
</many-to-one>
I have two objects User and Contact, with many to many relation, and I am using an intermediate table for this relation USER_CONTACT
Saving the data in this association is fine, but the retrieval is an issue.
I need to retrieve the data based on the User, but what I am getting is all the Contacts, for all the Users.
It will be good if you can let me know what wrong I am doing.
public class User {
private Integer userID;
private String userLoginEmail;
private String password;
private Set<Contact> contactSet = new HashSet<Contact>();
.
.
}
public class Contact implements Serializable {
private Integer contactID;
private String givenName;
private String familyName;
private Set<User> userSet = new HashSet<User>();
.
.
}
User.hbm.xml:
<class name="User" table="USERACCOUNT">
<id column="USER_ID" length="500" name="userID">
<generator class="increment" />
</id>
<property column="USER_LOGIN_EMAIL" generated="never" lazy="false" length="100" name="userLoginEmail" />
<property column="USER_FIRSTNAME" generated="never" lazy="false" length="100" name="userFirstName" />
<property column="USER_LASTNAME" generated="never" lazy="false" length="100" name="userLastName" />
<set name="contactSet" table="USER_CONTACT" inverse="false" lazy="false" fetch="select" cascade="all">
<key column="USER_ID"/>
<many-to-many column="CONTACT_ID" class="com.smallworks.model.Contact"/>
</set>
</class>
Contact.hbm.xml
<class name="Contact" table="CONTACT">
<id column="CONTACT_ID" length="500" name="contactID">
<generator class="increment"/>
</id>
<property column="GIVEN_NAME" generated="never" lazy="false" length="100" name="givenName"/>
<property column="FAMILY_NAME" generated="never" lazy="false" length="100" name="familyName"/>
<!-- many to many mapping with the User via User_Contact table -->
<set inverse="true" lazy="false" name="userSet" sort="unsorted" table="USER_CONTACT">
<key column="USER_ID"/>
<many-to-many class="com.smallworks.model.Contact" column="CONTACT_ID" unique="false"/>
</set>
</class>
and this is how I am trying to retrieve the data, which I think is not correct.
List contactList = session.createQuery("from Contact").list();
It will be good if I can know how to go about getting the Contacts based on the User.
// First, retrieve the user you want.
User user = (User) session.get(User.class, user_id_you_want);
// Second, get the contacts of that given user and add them to a list (optional)
List contacts = new ArrayList();
contacts.addAll(user.getContactSet());
return contacts;
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.