Hibernate 5.x migration Criteria to CriteriaBuilder - java

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())

Related

Hibernate Mapping issue when lazily loading a Set of custom Objects

I have trying to read from my database using Hibernate but I am having a problem. Any help would be appreciated. It has to do with the fact that there is a set of Answers in a Question object and I'm not sure the implications this is having on the subject.
List<Question> questions = session
.createQuery("from Question where topic = :id")
.setParameter("id", topic)
.list();
Code:
<class name="cdd.model.Question" table="question">
<id column="question_id" name="questionID" type="org.hibernate.type.PostgresUUIDType">
<generator class="org.hibernate.id.UUIDGenerator"/>
</id>
<many-to-one class="cdd.model.User" column="submitted_by" name="submittedBy" not-null="true" lazy = "false"/>
<many-to-one class="cdd.model.Topic" column="question_topic" name="topic" not-null="true" lazy = "false"/>
<property column="title" name="title" type="org.hibernate.type.TextType"/>
<property column="correct_answer" name="correctAnswer" type="org.hibernate.type.TextType"/>
<property column="date_submitted" name="dateSubmitted" type="org.hibernate.type.TimestampType"/>
<property column = "approved" name = "approved" type = "org.hibernate.type.BooleanType"/>
<set cascade="all" name="answers" >
<key column="question_id" not-null="true" lazy = "false" />
<one-to-many class="cdd.model.Answer"/>
</set>
</class>
Java Class That was mapped:
public class Question {
UUID questionID;
User submittedBy;
Topic topic;
String title;
String correctAnswer;
Timestamp dateSubmitted;
boolean approved;
Set<Answer> answers;
/*Constructor, Getters and Setters*/
}
Error:
XML validation started.
Checking file:/Users/rhamblin/Desktop/CDDExamGen/src/hibernateMapping.hbm.xml...
Attribute "lazy" must be declared for element type "key". [69]
XML validation finished.

Row was updated or deleted by another transaction-multithreading

I want to restirict user table updation when two threads are working together, which throws "Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect): [com.dmainc.commons.security.User#4]"
. When second threads hits first starts rolling back and throw the exception.
Actually each thread is modifying the passwordAttemptDate.I need to update this field each time when user is logging, how to perform this operation simulaneously for both thread. I am calling this method within a transaction.
ANY SUGGESSION WILL BE APPRECIATED.
public AuthenticationStatus authenticateEncrypted(User user, String password, ISecurityParameters params,
Boolean encryptedPassword)
{
AuthenticationStatus status = new AuthenticationStatus();
status.setUser(user);
status.setStatus(AuthenticationStatus.Status.UNKNOWN_ERROR);
// hash password
String hashedPass = (encryptedPassword == true ? password : hashPassword(password, user.getSalt()));
// compare to stored password and check on attempts if failed
if(hashedPass.equals(user.getPassword()))
{
user.setPasswordAttempts(0);
//THIS IS GETTING UPDATED EACH TIME
user.setPasswordAttemptDate(Calendar.getInstance());
if(getPasswordNotify(user, params) >= 0)
{
status.setStatus(AuthenticationStatus.Status.SUCCESS_NOTIFY);
}
else if(isPasswordExpired(user, params))
{
status.setStatus(AuthenticationStatus.Status.PASSWORD_EXPIRED);
}
else
{
status.setStatus(AuthenticationStatus.Status.SUCCESS);
}
status.setArg(arg);
return status;
}
else
{
user.setPasswordAttempts((user.getPasswordAttempts() == null) ? 1 : user.getPasswordAttempts() + 1);
user.setPasswordAttemptDate(Calendar.getInstance());
status.setStatus(AuthenticationStatus.Status.FAIL);
}
}
catch(Exception pe)
{
if(LOG.isErrorEnabled())
LOG.error(pe, pe);
}
return status;
}
User.hbm.xml
<hibernate-mapping>
<class name="com.dmainc.commons.security.User" table="userdata">
<id name="userId" type="long">
<column name="UserId" />
<generator class="native" />
</id>
<version name="version" type="int">
<column name="Version" not-null="true" />
</version>
<property name="username" type="string">
<column name="Username" length="50" not-null="true" />
</property>
<property name="firstName" type="string">
<column name="FirstName" length="50" />
</property>
<property name="lastName" type="string">
<column name="LastName" length="50" />
</property>
<property name="email" type="string">
<column name="Email" length="50" />
</property>
<property name="status" type="string">
<column name="Status" length="2" />
</property>
<property name="passwordAttempts" type="int">
<column name="PasswordAttempts" />
</property>
<property name="passwordAttemptDate" type="calendar">
<column name="PasswordAttemptDate" length="19" />
<many-to-one name="organization" class="com.dmainc.commons.security.Organization" fetch="select">
<column name="OrganizationId" />
</many-to-one>
<set name="userpasswords" table="userpasswords" inverse="true" lazy="true" fetch="select" cascade="all,delete-orphan">
<key>
<column name="UserId" not-null="true" />
</key>
<one-to-many class="com.dmainc.commons.security.UserPassword" />
</set>
<bag name="userroles" table="userrole" inverse="true" lazy="false" fetch="select" cascade="all,delete-orphan">
<key>
<column name="UserId" not-null="true" />
</key>
<one-to-many class="com.dmainc.commons.security.UserRole" />
</bag>
</class>
Can you try enclosing your code in transaction blocks?
something like:
Session session = null;
Transaction tx = null;
try {
session = sessionFactory.openSession();
tx = session.beginTransaction();
//YOUR CODE HERE
tx.commit();
}catch (Exception ex) {
ex.printStackTrace();
tx.rollback();
}
finally {session.close();}

Hibernate One-to-Many relationship cascade delete

I am new to Hibernate so please guide me.
I have 2 entities Companies & Employees. One Company should have many employees.
Employees Hibernate Mapping File
<hibernate-mapping>
<class name="com.hibernate.demo.Employees" table="employees">
<meta attribute="class-description">
This class contains the employee detail.
</meta>
<id name="empId" type="int" column="emp_id">
<generator class="native"/>
</id>
<property name="empCId" column="emp_cid" type="int"/>
<property name="empName" column="emp_name" type="string"/>
<property name="empContact" column="emp_contact" type="int"/>
</class>
</hibernate-mapping>
Companies Hibernate Mapping File
<hibernate-mapping>
<class name="com.hibernate.demo.Companies" table="companies" >
<meta attribute="class-description">
This class contains the companies detail.
</meta>
<id name="compId" type="int" column="comp_id">
<generator class="native"/>
</id>
<set name="employees" cascade="all" >
<key column="emp_cid"/>
<one-to-many class="com.hibernate.demo.Employees" />
</set>
<property name="compName" column="comp_name" type="string"/>
<property name="compCity" column="comp_city" type="string"/>
</class>
</hibernate-mapping>
Hibernate Configuration File
<hibernate-configuration>
<session-factory>
<property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>
<property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
<property name="hibernate.connection.url">jdbc:mysql://localhost:3306/hibernatedbdemo</property>
<property name="hibernate.connection.username">root</property>
<property name="hibernate.connection.password">knowarth</property>
<property name="show_sql">true</property>
<property name="format_sql">true</property>
<property name="hbm2ddl.auto">update</property>
<mapping resource="employees.hbm.xml"/>
<mapping resource="companies.hbm.xml"/>
</session-factory>
</hibernate-configuration>
Simple POJO Classes.
Employees.java
public class Employees {
public Employees(){}
private int empId;
private int empCId;
private String empName;
private int empContact;
//Getter & Setter
}
Companies.java
public class Companies {
public Companies(){}
private int compId;
private String compName;
private String compCity;
private Set<Employees> employees;
//Getter & Setter
}
I want to delete Company Record from the companies table and all the employees from that company should be deleted. But the problem I am facing is that Company Record is deleted by all the Employees Record relative to that Company aren't deleted.
Below is the Delete Code
public class CompanyDao {
Configuration cfg = new Configuration().configure("hibernate.cfg.xml");
SessionFactory sf = cfg.buildSessionFactory();
Session session = sf.openSession();
Companies comp = new Companies();
Scanner compSc = new Scanner(System.in);
public void deleteComp(){
session.beginTransaction();
System.out.println("Enter Company ID to delete it");
int cmp_id = compSc.nextInt();
Companies company = new Companies();
company.setCompId(cmp_id);
session.delete(company);
session.getTransaction().commit();
return;
}
}
You can rely on the database for cascading the DELETE statement, in which case you need to change the mapping to:
<set name="employees" cascade="all" inverse="true" >
<key column="emp_cid" on-delete="cascade" />
<one-to-many class="com.hibernate.demo.Employees" />
</set>
If you don't want to change the mapping, you need to fetch the entity from the database and let Hibernate handle the children deletion:
public void deleteComp(){
session.beginTransaction();
System.out.println("Enter Company ID to delete it");
int cmp_id = compSc.nextInt();
Companies company = session.get(Companies.class, cmp_id);
session.delete(company);
session.getTransaction().commit();
return;
}

Hibernate org.hibernate.LazyInitializationException: could not initialize

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>

HibernateTemplate Save Not Throwing Exception when trying to insert duplicate Key row

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.

Categories