SQL Query to Hibernate ORM Query - java

I am trying to convert my plain SQL statements into proper Hibernate ORM ones. I've read alot about it, but still can't figure it out completely just yet. I hope some of you can help me :)
Here's some of my classes that I think relevant for this task:
WarehouseProduct (my entity class):
package exercise.java.basics.storage;
public class WarehouseProduct {
private int productID;
private String productName;
private int productCount;
public WarehouseProduct( final String productName, final int productCount ) {
this.productName = productName;
this.productCount = productCount;
}
public WarehouseProduct() {
}
public int getProductID() {
return this.productID;
}
public void setProductID( final int productID ) {
this.productID = productID;
}
public String getProductName() {
return this.productName;
}
public void setProductName( final String productName ) {
this.productName = productName;
}
public int getProductCount() {
return this.productCount;
}
public void setProductCount( final int productCount ) {
this.productCount = productCount;
}
}
my DAO.
package exercise.java.basics.storage;
import javax.transaction.Transactional;
import exercise.java.basics.storage.ProductEnum.Product;
#Transactional
public interface WarehouseDAO {
public void initializeWarehouse();
public void storeProducts( final Product product, final int count );
public void removeProducts( final Product product, final int count );
public void updateStock();
}
storeProduct() method from my DAO implementation:
public void storeProducts( final Product product, final int count ) {
//Plain-SQL, works just fine
Session session = getSessionFactory().getCurrentSession();
SQLQuery storeProductQuery = session.createSQLQuery( "UPDATE WAREHOUSE SET product_count = " + count //$NON-NLS-1$
+ " WHERE product_name = '" + product + "';" ); //$NON-NLS-1$ //$NON-NLS-2$
storeProductQuery.executeUpdate();
//Hibernate attempt, doesn't work just yet
session.get( "WarehouseProduct.class", "product_count" ); //$NON-NLS-1$ //$NON-NLS-2$
Criteria createCriteria = session.createCriteria( WarehouseProduct.class ); // Object.class = Entity
createCriteria.add( Property.forName( "product_name" ).like( product ) );
createCriteria.list();
}
When testing I don't use both (plain sql / hibernate) attempts at once of course.
I think that I am like halfway there already, but still couldn't figure out the complete hibernate approach and that's where I hope you can help me.
Basically all I need is a transformation of the plain sql string in the storeProduct() method to proper hibernate commands.
Would greatly appreciate any help you can give me.
best regards
daZza
EDIT: Here's the mapping:
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="exercise.java.basics.storage.WarehouseProduct" table="WAREHOUSE">
<id name="productID" type="integer">
<column name="product_ID" not-null="true"/>
<generator class="identity" />
</id>
<property name="productName" type="string">
<column name="product_name" length="100"/>
</property>
<property name="productCount" type="integer">
<column name="product_count"/>
</property>
</class>
</hibernate-mapping>
As to the problem, it's in the incomplete code imho. I am pretty sure that my hibernate commands are still missing something and/or are simply wrong.
This is the source SQL string I want to translate to hibernate: "UPDATE WAREHOUSE SET product_count = " + count + " WHERE product_name = '" + product + "';"

You can use the Restrictions class to get your result. use the following:
createCriteria.add(Restrictions.like("productName", product.getProductName()));
Note that I used the field name in your Product object rather than the column name in the database.Your way may also work by changing
createCriteria.add( Property.forName( "product_name" ).like( product ) );
to
createCriteria.add( Property.forName( "product_name" ).like( product.getProductName() ) );
I noticed that you were are matching "product_name" with "product" which is the entire object and not the field you are matching against.

I read even more examples and documentations and came up with the following, which I think is working now. At least there are no error messages and the hibernate logger messages look good.
Unfortunately I can't check if the data within the database is correct, as I didn't figure out how to convert hibernate query results (objects) to readable stuff like a string.
I also changed my primary key in the hibernate mapping file to be the productName instead of an auto incremented value. The reason behind that was mainly hibernates get() function, as it seems to need the PK als the second attribute. The auto generated key was kinda random so I probably would have required another select before the update to identify the PK of the row with the corresponding product. As I don't know how to convert the results of a hibernate select query that wouldn't have worked.
So here's my new class:
public void storeProducts( final Product product, final int count ) {
Session session = getSessionFactory().getCurrentSession();
WarehouseProduct productToStore = (WarehouseProduct) session.get( WarehouseProduct.class,
String.valueOf( product ) );
productToStore.setProductCount( productToStore.getProductCount() + count );
session.update( productToStore );
}

Related

org.hibernate.hql.internal.ast.QuerySyntaxException: table is not mapped

I have example web application Hibernate 4.3.5 + Derby database 10.10.1.1+ Glassfish4.0 with IDE NetBeans 8.0Beta.
I have the next exception:
Caused by: org.hibernate.hql.internal.ast.QuerySyntaxException: CUSTOMERV is not mapped
at org.hibernate.hql.internal.ast.util.SessionFactoryHelper.requireClassPersister(SessionFactoryHelper.java:189)
at org.hibernate.hql.internal.ast.tree.FromElementFactory.addFromElement(FromElementFactory.java:109)
at org.hibernate.hql.internal.ast.tree.FromClause.addFromElement(FromClause.java:95)
at org.hibernate.hql.internal.ast.HqlSqlWalker.createFromElement(HqlSqlWalker.java:331)
at org.hibernate.hql.internal.antlr.HqlSqlBaseWalker.fromElement(HqlSqlBaseWalker.java:3633)
at org.hibernate.hql.internal.antlr.HqlSqlBaseWalker.fromElementList(HqlSqlBaseWalker.java:3522)
at org.hibernate.hql.internal.antlr.HqlSqlBaseWalker.fromClause(HqlSqlBaseWalker.java:706)
at org.hibernate.hql.internal.antlr.HqlSqlBaseWalker.query(HqlSqlBaseWalker.java:562)
at org.hibernate.hql.internal.antlr.HqlSqlBaseWalker.selectStatement(HqlSqlBaseWalker.java:299)
at org.hibernate.hql.internal.antlr.HqlSqlBaseWalker.statement(HqlSqlBaseWalker.java:247)
at org.hibernate.hql.internal.ast.QueryTranslatorImpl.analyze(QueryTranslatorImpl.java:278)
at org.hibernate.hql.internal.ast.QueryTranslatorImpl.doCompile(QueryTranslatorImpl.java:206)
... 72 more
Form from index.xhtml
<h:panelGrid id="panel1" columns="2" border="1"
cellpadding="5" cellspacing="1">
<f:facet name="header">
<h:outputText value="Add Customer Information"/>
</f:facet>
<h:outputLabel value="First Name:"/>
<h:inputText value="#{customer.firstName}" id="fn"/>
<h:outputLabel value="Last Name:"/>
<h:inputText value="#{customer.lastName}" id="ln"/>
<h:outputLabel value="Email:"/>
<h:inputText value="#{customer.email}" id="eml"/>
<h:outputLabel value="Date of Birth:"/>
<h:inputText value="#{customer.sd}" id="s"/>
<f:facet name="footer">
<h:outputLabel value="#{customer.msg}" id="msg" styleClass="msg"/>
<h:commandButton value="Save" action="#{customer.saveCustomer}">
</h:commandButton>
</f:facet>
</h:panelGrid>
Customer.java
package com.javaknowledge.entity;
import com.javaknowledge.dao.CustomerDao;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.SessionScoped;
import javax.persistence.*;
#ManagedBean
#SessionScoped
public class Customer implements java.io.Serializable {
private Integer custId;
private String firstName;
private String lastName;
private String email;
private Date dob;
private String sd, msg, selectedname;
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
public Customer() {
}
public Customer(String firstName, String lastName, String email, Date dob) {
this.firstName = firstName;
this.lastName = lastName;
this.email = email;
this.dob = dob;
}
public String getSd() {
return sd;
}
public void setSd(String sd) {
this.sd = sd;
}
public Integer getCustId() {
return this.custId;
}
public void setCustId(Integer custId) {
this.custId = custId;
}
public String getFirstName() {
return this.firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return this.lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
#Column(name = "EMAIL")
public String getEmail() {
return this.email;
}
public void setEmail(String email) {
this.email = email;
}
#Column(name = "DOB")
public Date getDob() {
return this.dob;
}
public void setDob(Date dob) {
this.dob = dob;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
public String getSelectedname() {
return selectedname;
}
public void setSelectedname(String selectedname) {
this.selectedname = selectedname;
}
public void saveCustomer() {
try {
Date d = sdf.parse(sd);
System.out.println(d);
this.dob = d;
} catch (ParseException e) {
e.printStackTrace();
}
CustomerDao dao = new CustomerDao();
dao.addCustomer(this);
this.msg = "Member Info Saved Successfull!";
clearAll();
}
public void updateCustomer() {
try {
Date d = sdf.parse(sd);
System.out.println(d);
this.dob = d;
} catch (ParseException e) {
e.printStackTrace();
}
CustomerDao dao = new CustomerDao();
dao.updateCustomer(this);
this.msg = "Member Info Update Successfull!";
clearAll();
}
public void deleteCustomer() {
CustomerDao dao = new CustomerDao();
dao.deleteCustomer(custId);
this.msg = "Member Info Delete Successfull!";
clearAll();
}
public List<Customer> getAllCustomers() {
List<Customer> users = new ArrayList<Customer>();
CustomerDao dao = new CustomerDao();
users = dao.getAllCustomers();
return users;
}
public void fullInfo() {
CustomerDao dao = new CustomerDao();
List<Customer> lc = dao.getCustomerById(selectedname);
System.out.println(lc.get(0).firstName);
this.custId = lc.get(0).custId;
this.firstName = lc.get(0).firstName;
this.lastName = lc.get(0).lastName;
this.email = lc.get(0).email;
this.dob = lc.get(0).dob;
this.sd = sdf.format(dob);
}
private void clearAll() {
this.firstName = "";
this.lastName = "";
this.sd = "";
this.email = "";
this.custId=0;
}
}
hibernate.cfg.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<property name="hibernate.dialect">org.hibernate.dialect.DerbyDialect</property>
<property name="hibernate.connection.driver_class">org.apache.derby.jdbc.ClientDriver</property>
<property name="hibernate.connection.url">jdbc:derby://localhost:1527/derbyDB</property>
<property name="hibernate.connection.username">user1</property>
<property name="hibernate.connection.password">user1</property>
<property name="hibernate.hbm2ddl.auto">create</property>
<property name="c3p0.min_size">1</property>
<property name="c3p0.max_size">5</property>
<property name="c3p0.timeout">300</property>
<property name="c3p0.max_statements">50</property>
<property name="c3p0.idle_test_period">300</property>
<mapping class="com.javaknowledge.entity.Customer" resource="com/javaknowledge/entity/Customer.hbm.xml"/>
</session-factory>
</hibernate-configuration>
Customer.hbm.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="com.javaknowledge.entity.Customer" table="CUSTOMERV" schema="APP">
<id name="custId" type="java.lang.Integer">
<column name="cust_id" />
<generator class="increment" />
</id>
<property name="firstName" type="string">
<column name="first_name" length="45" not-null="true" />
</property>
<property name="lastName" type="string">
<column name="last_name" length="45" not-null="true" />
</property>
<property name="email" type="string">
<column name="email" length="45" not-null="true" />
</property>
<property name="dob" type="date">
<column name="dob" length="10" not-null="true" />
</property>
</class>
</hibernate-mapping>
Finally I found a mistake! Hope this is useful to someone. When doing a request to the database(in my case it Apache Derby), name of base need write the first letter upper case other in lower case.
This is wrong query:
session.createQuery("select first_name from CUSTOMERV").
This is valid query
session.createQuery("select first_name from Customerv").
And class entity must be same name as database, but I'm not sure.
in HQL query, Don't write the Table name, write your Entity class name in your query like
String s = "from Entity_class name";
query qry = session.createUqery(s);
In my case I just forgot to add nativeQuery = true
#Query( value = "some sql query ...", nativeQuery = true)
For Spring Boot with Spring Data JPA
If you are using the JPA annotations to create the entities and then make sure that the table name is mapped along with #Table annotation instead of #Entity.
Incorrectly mapped :
#Entity(name="DB_TABLE_NAME")
public class DbTableName implements Serializable {
....
....
}
Correctly mapped entity :
#Entity
#Table(name="DB_TABLE_NAME")
public class DbTableName implements Serializable {
....
....
}
hibernate.cfg.xml file should have the mapping for the tables like below. Check if it is missing in your file.
......
<hibernate-configuration>
......
......
<session-factory>
......
<mapping class="com.test.bean.dbBean.testTableHibernate"/>
......
</session-factory>
</hibernate-configuration>
.....
None of the other solution worked for me.
Even if I don't think its the best practice, I Had to add it into the code like this
configuration.addAnnotatedClass(com.myOrg.entities.Person.class);
here
public static SessionFactory getSessionFactory() {
Configuration configuration = new Configuration().configure();
configuration.addAnnotatedClass(com.myOrg.entities.Person.class);
StandardServiceRegistryBuilder builder = new StandardServiceRegistryBuilder()
.applySettings(configuration.getProperties());
SessionFactory sessionFactory = configuration.buildSessionFactory(builder.build());
return sessionFactory;
}
May be this will make it more clear, and of course makes sense too.
#Entity
#Table(name = "users")
/**
*
* #author Ram Srinvasan
* Use class name in NamedQuery
* Use table name in NamedNativeQuery
*/
#NamedQueries({ #NamedQuery(name = "findUserByName", query = "from User u where u.name= :name") })
#NamedNativeQueries({ #NamedNativeQuery(name = "findUserByNameNativeSQL", query = "select * from users u where u.name= :name", resultClass = User.class) })
public class User implements Principal {
...
}
There is one more chance to get this exception even we used class name i.e., if we have two classes with same name in different packages. we'll get this problem.
I think hibernate may get ambiguity and throws this exception, so the solution is to use complete qualified name(like com.test.Customerv)
I added this answer that will help in scenario as I mentioned. I got the same scenario got stuck for some time.
I too have faced similar issue when i started to work on Hibernate. All in all i can say is in the createQuery one needs to pass the name of the entity class not the table name to which the entity is mapped to.
In Hibernate,
session.createQuery("select first_name from Customerv").
The Customerv is your Entity Name, not your Table Name
It means your table is not mapped to the JPA.
Either Name of the table is wrong (Maybe case sensitive), or you need to put an entry in the XML file.
Happy Coding :)
Other persons that are using mapping classes for Hibernate, make sure that have addressed correctly to model package in sessionFactory bean declaration in the following part:
<property name="packagesToScan" value="com.mblog.model"></property>
In my case: spring boot 2 ,multiple datasource(default and custom). entityManager.createQuery go wrong: 'entity is not mapped'
while debug, i find out that the entityManager's unitName is wrong(should be custom,but the fact is default)
the right way:
#PersistenceContext(unitName = "customer1") // !important,
private EntityManager em;
the customer1 is from the second datasource config class:
#Bean(name = "customer1EntityManagerFactory")
public LocalContainerEntityManagerFactoryBean entityManagerFactory(EntityManagerFactoryBuilder builder,
#Qualifier("customer1DataSource") DataSource dataSource) {
return builder.dataSource(dataSource).packages("com.xxx.customer1Datasource.model")
.persistenceUnit("customer1")
// PersistenceUnit injects an EntityManagerFactory, and PersistenceContext
// injects an EntityManager.
// It's generally better to use PersistenceContext unless you really need to
// manage the EntityManager lifecycle manually.
// 【4】
.properties(jpaProperties.getHibernateProperties(new HibernateSettings())).build();
}
Then,the entityManager is right.
But, em.persist(entity) doesn't work,and the transaction doesn't work.
Another important point is:
#Transactional("customer1TransactionManager") // !important
public Trade findNewestByJdpModified() {
//test persist,working right!
Trade t = new Trade();
em.persist(t);
log.info("t.id" + t.getSysTradeId());
//test transactional, working right!
int a = 3/0;
}
customer1TransactionManager is from the second datasource config class:
#Bean(name = "customer1TransactionManager")
public PlatformTransactionManager transactionManager(
#Qualifier("customer1EntityManagerFactory") EntityManagerFactory entityManagerFactory) {
return new JpaTransactionManager(entityManagerFactory);
}
The whole second datasource config class is :
package com.lichendt.shops.sync;
import javax.persistence.EntityManagerFactory;
import javax.sql.DataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
import org.springframework.boot.autoconfigure.orm.jpa.HibernateSettings;
import org.springframework.boot.autoconfigure.orm.jpa.JpaProperties;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.orm.jpa.EntityManagerFactoryBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
#Configuration
#EnableTransactionManagement
#EnableJpaRepositories(entityManagerFactoryRef = "customer1EntityManagerFactory", transactionManagerRef = "customer1TransactionManager",
// 【1】这里写的是DAO层的路径 ,如果你的DAO放在 com.xx.DAO下面,则这里写成 com.xx.DAO
basePackages = { "com.lichendt.customer1Datasource.dao" })
public class Custom1DBConfig {
#Autowired
private JpaProperties jpaProperties;
#Bean(name = "customer1DatasourceProperties")
#Qualifier("customer1DatasourceProperties")
#ConfigurationProperties(prefix = "customer1.datasource")
public DataSourceProperties customer1DataSourceProperties() {
return new DataSourceProperties();
}
#Bean(name = "customer1DataSource")
#Qualifier("customer1DatasourceProperties")
#ConfigurationProperties(prefix = "customer1.datasource") //
// 【2】datasource配置的前缀,对应上面 【mysql的yaml配置】
public DataSource dataSource() {
// return DataSourceBuilder.create().build();
return customer1DataSourceProperties().initializeDataSourceBuilder().build();
}
#Bean(name = "customer1EntityManagerFactory")
public LocalContainerEntityManagerFactoryBean entityManagerFactory(EntityManagerFactoryBuilder builder,
#Qualifier("customer1DataSource") DataSource dataSource) {
return builder.dataSource(dataSource).packages("com.lichendt.customer1Datasource.model") // 【3】这里是实体类的包路径
.persistenceUnit("customer1")
// PersistenceUnit injects an EntityManagerFactory, and PersistenceContext
// injects an EntityManager.
// It's generally better to use PersistenceContext unless you really need to
// manage the EntityManager lifecycle manually.
// 【4】
.properties(jpaProperties.getHibernateProperties(new HibernateSettings())).build();
}
#Bean(name = "customer1TransactionManager")
public PlatformTransactionManager transactionManager(
#Qualifier("customer1EntityManagerFactory") EntityManagerFactory entityManagerFactory) {
return new JpaTransactionManager(entityManagerFactory);
}
}
If you by any chance using java for configuration, you may need to check the below bean declaration if you have package level changes. Eg: com.abc.spring package changed to com.bbc.spring
#Bean
public SessionFactory sessionFactory() {
LocalSessionFactoryBuilder builder = new LocalSessionFactoryBuilder(dataSource());
//builder.scanPackages("com.abc.spring"); //Comment this line as this package no longer valid.
builder.scanPackages("com.bbc.spring");
builder.addProperties(getHibernationProperties());
return builder.buildSessionFactory();
}
Should use Entity class name for em.createQuery method
or
Should use em.createNativeQuery method for native query without entity class
With Entity class:
em.createQuery("select first_name from CUSTOMERV")
Without Entity class or Native query:
em.createNativeQuery("select c.first_name from CUSTOMERV c")
Another solution that worked:
The data access object that actually throwed this exception is
public List<Foo> findAll() {
return sessionFactory.getCurrentSession().createQuery("from foo").list();
}
The mistake I did in the above snippet is that I have used the table name foo inside createQuery. Instead, I got to use Foo, the actual class name.
public List<Foo> findAll() {
return sessionFactory.getCurrentSession().createQuery("from Foo").list();
Thanks to this blog: https://www.arundhaj.com/blog/querysyntaxexception-not-mapped.html
Other persons that are using mapping classes for Hibernate, make sure that have addressed correctly to model package in sessionFactory bean declaration in the following part:
public List<Book> list() {
List<Book> list=SessionFactory.getCurrentSession().createQuery("from book").list();
return list;
}
The mistake I did in the above snippet is that I have used the table name foo inside createQuery. Instead, I got to use Foo, the actual class name.
public List<Book> list() {
List<Book> list=SessionFactory.getCurrentSession().createQuery("from Book").list();
return list;
}
add parameter nativeQuery = true
ex:
#Query(value="Update user set user_name =:user_name,password =:password where user_id =:user_id",nativeQuery = true)
In Apache Derby DB, refrain from using table names as "user" or so because they are reserved keywords on Apache Derby but will work fine on MySql.
In the Query, you must specify the name of the Entity class that you want to fetch the data from in the FROM clause of the Query.
List<User> users=session.createQuery("from User").list();
Here, User is the name of my Java Entity class(Consider the casing of the name as in Java it matters.)
in my case was that i forgot the "nativeQuery = true"
Problem partially was solved. Besides creating jdbc/resource(DB Derby) had to create JDBC Connection Pool for db resource in Glassfish admin console, and check it on pinging. Now all CRUD operation work just fine. I check, object Customer in database adding properly, update and delete too. But in Glassfish output log have same exception:
SEVERE: org.hibernate.hql.internal.ast.QuerySyntaxException: CUSTOMERV is not mapped [select concat(first_name, ' ', last_name) as name from CUSTOMERV]
at org.hibernate.hql.internal.ast.QuerySyntaxException.generateQueryException(QuerySyntaxException.java:96)
at org.hibernate.QueryException.wrapWithQueryString(QueryException.java:120)
at org.hibernate.hql.internal.ast.QueryTranslatorImpl.doCompile(QueryTranslatorImpl.java:234)
.......
Caused by: org.hibernate.hql.internal.ast.QuerySyntaxException: CUSTOMERV is not mapped
at org.hibernate.hql.internal.ast.util.SessionFactoryHelper.requireClassPersister(SessionFactoryHelper.java:189)
at org.hibernate.hql.internal.ast.tree.FromElementFactory.addFromElement(FromElementFactory.java:109)
The mistake in my case is that I used session.createQuery() instead of session.createSQLQuery()

How Lazy loading of child records works in Hibernate?

I know the above question is very common but I just wanted to know exactly when and How Hibernate is fetching the lazily loaded child records.
below is the sample table structure:
Table Structure:
employee_table(e_id, e_name, e_sal)
(100, XYZ, 20000)
mobile_table(m_id, m_number, e_id)
(1, 8728271817, 100)
(2, 0983813919, 100)
Employee.java
public class Employee implements Serializable {
private static final long serialVersionUID = 1930751473454928876L;
private long employeeId;
private String employeeName;
private double employeeSal;
private Set<Mobile> mobiles;
public long getEmployeeId() {
return employeeId;
}
public void setEmployeeId(long employeeId) {
this.employeeId = employeeId;
}
public String getEmployeeName() {
return employeeName;
}
public void setEmployeeName(String employeeName) {
this.employeeName = employeeName;
}
public double getEmployeeSal() {
return employeeSal;
}
public void setEmployeeSal(double employeeSal) {
this.employeeSal = employeeSal;
}
public Set<Mobile> getMobiles() {
return mobiles;
}
public void setMobiles(Set<Mobile> mobiles) {
this.mobiles = mobiles;
}
}
Employee.hbm.xml
<hibernate-mapping>
<class name="edu.sandip.hibernate.Employee" table="EMPLOYEE_T">
<id name="employeeId" column="e_id" type="long">
<generator class="increment" />
</id>
<property name="employeeName" column="e_name" type="string" />
<property name="employeeSal" column="e_sal" type="double" />
<set name="mobiles" table="MOBILE_T" inverse="true" lazy="true" fetch="select" >
<key>
<column name="e_id" not-null="true" />
</key>
<one-to-many class="edu.sandip.hibernate.Mobile" />
</set>
</class>
</hibernate-mapping>
Mobile.java
public class Mobile implements Serializable {
private static final long serialVersionUID = 6279006639448045512L;
private long mobId;
private String mobileNumber;
private Employee employee;
public long getMobId() {
return mobId;
}
public void setMobId(long mobId) {
this.mobId = mobId;
}
public String getMobileNumber() {
return mobileNumber;
}
public void setMobileNumber(String mobileNumber) {
this.mobileNumber = mobileNumber;
}
public Employee getEmployee() {
return employee;
}
public void setEmployee(Employee employee) {
this.employee = employee;
}
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result
+ ((mobileNumber == null) ? 0 : mobileNumber.hashCode());
return result;
}
#Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Mobile other = (Mobile) obj;
if (mobileNumber == null) {
if (other.mobileNumber != null)
return false;
} else if (!mobileNumber.equals(other.mobileNumber))
return false;
return true;
}
}
Mobile.hbm.xml
<hibernate-mapping>
<class name="edu.sandip.hibernate.Mobile" table="MOBILE_T">
<id name="mobId" column="m_id" type="long">
<generator class="increment" />
</id>
<property name="mobileNumber" column="m_number" type="string" />
<many-to-one name="employee" class="edu.sandip.hibernate.Employee" fetch="select">
<column name="e_id" not-null="true" />
</many-to-one>
</class>
</hibernate-mapping>
Below is the code block to fetch the list of employees:
public List<Employee> getAllEmployees() {
List<Employee> list = null;
Session session = null;
try {
session = getSession();
Query query = session.createQuery("FROM Employee");
list = query.list();
for(Employee employee : list) {
System.out.println("Emaployee Name: " + employee.getEmployeeName);
Set<Mobile> mobileSet = employee.getMobiles();
System.out.println("Mobile Numbers: ");
for(Mobile mobile : mobileSet) {
System.out.println(mobile.getMobileNumber());
}
}
} catch(Exception e) {
e.printStackTrace();
} finally {
if(session != null) {
System.out.println("session is still alive");
releaseSession(session);
}
}
return (list);
}
Now, Here the Mobile numbers is the child record of Employee which is been loaded by the Hibernate lazily as per the hibernate configuration in Employee.hbm.xml (lazy = true)
In the above code, after printing the employee name I am printing the mobile numbers of that
employee by iterating the Set mobiles.
I have checked and found that the iteration of the mobileSet is actually fetching the mobileNumbers.
i.e.
for(Mobile mobile : mobileSet) {
System.out.println(mobile.getMobileNumber());
}
SO, How It is happening? Because I am not using any hibernate specific API to fetch the lazily loaded child records. Just Iterating over the set of mobile numbers.
Then how Hibernate is fetching the child records internally? What is the background job that Hibernate is doing while I am iterating over the mobileSet?
Please help in understanding my doubt.
In hibernate lazy loading is triggered whenever a lazy loaded property is accessed and it is still managed by the em. If you would access a lazy loaded property when the entity is detached from the entity manager you would get a org.hibernate.LazyInitializationException
To apply the logic to your example:
mobile.getMobileNumber() will trigger the lazy loading in your code. Hibernate will then automaticly issue a query to the database. This works because hibernate instantiates proxy objects for lazy loaded propertys, once you try to access these proxys hibernate tries to load them in from the database. this will work if the object is managed and this will result in forementioned exception if the entity is detached. There is no need at all to use a hibernate API to trigger the lazy loading it just happens automaticly when a lazy configured property is accessed
In your Employee.hbm.xml file you have stated that the fetch strategy for Mobile is lazy.
That tells hibernate to generate a simple SQL statement to the table that corresponds to Employee without performing any joins with the corresponding Mobile table.
However when employee.getMobiles() is called,
Hibernate will issue a select query to the latter table using a where clause with the primary key of the former table.
The way the fetching is implemented here leads to the infamous N+1 problem
This topic could help you to understand what is the trick behind lazy loading :
How proxy loads the lazy property in Hibernate/JPA

Hibernate could not deserialize error

I have this Oracle table:
SQL> Name Null? Type
----------------------------------------- -------- ----------------------------
JOB_ID NOT NULL VARCHAR2(13)
TYPE NOT NULL NUMBER
COMPONENT_DESCRIPTION NOT NULL VARCHAR2(255)
COMPONENT_ID VARCHAR2(13)
STATUS NOT NULL NUMBER(1)
REASON VARCHAR2(255)
NOTES VARCHAR2(255)
SQL>
There is no defined primary key but JOB_ID, TYPE and COMPONENT_DESCRIPTION combined are unique. I cannot make any changes to the database structure and the code I'm working will only ever read from the DB, it will never write to it.
I have made this Hibernate map file:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"classpath://org/hibernate/hibernate-mapping-3.0.dtd">
<hibernate-mapping schema="ARCHIVE">
<class name="myclass.ArchiveJobHeaderComponents" table="JOB_HEADER_COMPONENTS">
<composite-id>
<key-property name="jobId" column="JOB_ID" type="java.lang.String" />
<key-property name="type" column="TYPE" type="java.lang.Number" />
<key-property name="componentDescription" column="COMPONENT_DESCRIPTION" type="java.lang.String" />
</composite-id>
<property name="componentId" column="COMPONENT_ID" type="java.lang.String" not-null="false" />
<property name="status" column="STATUS" type="java.lang.Number" />
<property name="reason" column="REASON" type="java.lang.String" not-null="false" />
<property name="notes" column="NOTES" type="java.lang.String" not-null="false" />
</class>
<query name="JobHeaderComponents.lookupJobHeaderComponents">
<![CDATA[from myclass.ArchiveJobHeaderComponents where
jobId = :jobId and
type = :type and
componentDescription = :componentDescription ]]>
</query>
<query name="JobHeaderComponents.listJobHeaderComponentsByComponentId">
<![CDATA[from myclass.ArchiveJobHeaderComponents where componentId = :id]]>
</query>
</hibernate-mapping>
This is the corresponding Java class file:
package myclass;
import org.apache.commons.lang.builder.EqualsBuilder;
import org.apache.commons.lang.builder.HashCodeBuilder;
import org.apache.commons.lang.builder.ToStringBuilder;
import java.io.Serializable;
import java.lang.Number;
import java.util.HashSet;
public class ArchiveJobHeaderComponents implements Serializable {
private String jobId;
private Number type;
private String componentDescription;
private String componentId;
private Number status;
private String reason;
private String notes;
public String getJobId() {
return jobId;
}
public void setJobId(String jobId) {
this.jobId = jobId;
}
public Number getType() {
return type;
}
public void setType(Number type) {
this.type = type;
}
public String getComponentDescription() {
return componentDescription;
}
public void setComponentDescription(String componentDescription) {
this.componentDescription = componentDescription;
}
public String getComponentId() {
return componentId;
}
public void setComponentId(String componentId) {
this.componentId = componentId;
}
public Number getStatus() {
return status;
}
public void setStatus(Number status) {
this.status = status;
}
public String getReason() {
return reason;
}
public void setReason(String reason) {
this.reason = reason;
}
public String getNotes() {
return notes;
}
public void setNotes(String notes) {
this.notes = notes;
}
public int hashCode() {
return new HashCodeBuilder().
append(getJobId()).
append(getType()).
append(getComponentDescription()).
append(getComponentId()).
append(getStatus()).
append(getReason()).
append(getNotes()).toHashCode();
}
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (!(o instanceof ArchiveJobHeaderComponents)) {
return false;
}
ArchiveJobHeaderComponents that = (ArchiveJobHeaderComponents) o;
return new EqualsBuilder().append(this.getJobId(), that.getJobId()).
append(this.getType(), that.getType()).
append(this.getComponentDescription(), that.getComponentDescription()).
append(this.getComponentId(), that.getComponentId()).
append(this.getStatus(), that.getStatus()).
append(this.getReason(), that.getReason()).
append(this.getNotes(), that.getNotes()).isEquals();
}
public String toString() {
return new ToStringBuilder(this).
append("jobId", getJobId()).
append("type", getType()).
append("componentDescription", getComponentDescription()).
append("componentId", getComponentId()).
append("status", getStatus()).
append("reason", getReason()).
append("notes", getNotes()).toString();
}
}
Whenever I get data back from a query, I get 'Could not deserialize' followed by an 'EOFException' error.
I've checked:
- There are no variables in the Java class of type serialize
- The Java class is implementing Serializable
I don't want to split the three columns (JOB_ID, TYPE and COMPONENT_DESCRIPTION) into a separate 'Id' class as I'm having conceptual problems with how the data is accessed. (I realize this is not recommended but is supported).
Can anyone point out what I've done wrong with how I've implemented this?
Thanks
EDIT:
I've changed the hbm.xml to not have a composite key, just an id on JOB_ID with no improvement.
I've added not-null="false" to the columns that can be empty, also no improvement.
Actually, having a look over the code and the Hibernate mapping file, I believe that the problem is that you are trying to map columns TYPE and STATUS to a Number. Number is an abstract class, so cannot be instantiated directly.
As both TYPE and STATUS are NOT NULL, I'd use primitive Java types to store their values, eg:
public class ArchiveJobHeaderComponents implements Serializable {
private String jobId;
private int type; // int should give you a large enough range - but change to long if required
private String componentDescription;
private String componentId;
private boolean status; // status appears to be a boolean (NUMBER(1))
private String reason;
private String notes;
// remainder omitted
}
Also, please remember to update the Hibernate mapping file to reflect the above!!
In case anyone else is working on a legacy Hibernate (3.0) app, something else that causes this error is running the app with Java 8 and OJDBC 1.4. Upgrading to OJDBC 6 resolved it.

Hibernate mapping returns null properties

I have a Hibernate mapping setup. The table is species, my Java class is Species. hibernate.cfg.xml points to mappings in species.hbn.xml
In my code I'm using a simple HQL query and then throwing the resultant Species instances into a "SpeciesLister" class (which I'm passing over to the presentation layer).
SpeciesLister speciesList = new SpeciesLister();
Query q = session.createQuery("SELECT s FROM Species s");
for (Species s : (List<Species>) q.list()){
speciesList.addSpecies(s);
}
The Species class looks like this:
package springwildlife;
public class Species implements Serializable
{
long id;
String commonName;
String latinName;
String order;
String family;
ArrayList<Sighting> sightings;
public Species()
{
}
public Species(String commonName, String latinName)
{
sightings = new ArrayList<Sighting>();
this.commonName = commonName;
this.latinName = latinName;
}
public long getId()
{
return id;
}
public String getCommonName()
{
return commonName;
}
public String getLatinName()
{
return latinName;
}
public String getOrder()
{
return order;
}
public String getFamily()
{
return family;
}
public ArrayList<Sighting> getSightings()
{
return sightings;
}
public void addSighting(Sighting s)
{
sightings.add(s);
}
public void setId(long id)
{
this.id = id;
}
public void setCommonName(String cn)
{
commonName = cn;
}
public void setLatinName(String ln)
{
commonName = ln;
}
public void setFamily(String f)
{
family = f;
}
public void setOrder(String o)
{
order = o;
}
}
My database schema looks like this:
CREATE TABLE species
(
id serial NOT NULL,
common_name text,
latin_name text,
order_name text,
family_name text,
CONSTRAINT id PRIMARY KEY (id)
)
species.hbn.xml looks like this:
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="springwildlife.Species" table="species">
<id name="id" type="java.lang.Long" column="id" >
<generator class="native">
<param name="sequence">species_id_seq</param>
</generator>
</id>
<property name="commonName" type="java.lang.String">
<column name="common_name" />
</property>
<property name="latinName" type="java.lang.String">
<column name="latin_name"/>
</property>
<property name="order" type="java.lang.String">
<column name="order_name"/>
</property>
<property name="family" type="java.lang.String">
<column name="family_name"/>
</property>
</class>
</hibernate-mapping>
My SpeciesLister instance gets a full slate of all the expected number of Species instances. However, when I examine the resultant Species instances, all their fields are null except for the id (long), all the others like familyName, latinName, commonName all are null in the mapped object.
This is unexpected and I can't figure out why it is happening. Am I doing something wrong?
I'm suspicious about two things, but I'm not sure of what to make of them:
I think the fact that the id is being property set, but not the other string fields might be a clue.
I suspect something might be wrong with the way I'm casting the objects into a list of Species instances.
The code looks ok. Without getting into debugger it's hard to tell for sure, however my guess is that you have compile time class instrumentation somewhere in the build. If that's a case, I've seen cases when assignment to actual field in the class is deferred until you call getter method.
So I suggest, that you put some print statements that rely on getters to get data instead of direct access to properties and see what gets printed.
Finally, please put # sign in front of names in comments (#Mark). This way, your correspondents will get notified and you may get response sooner.

Hibernate doesn't work anymore... why?

I was doing simple things with hibernate, as I have to learn it for a project. I created this simple example:
package hibtests;
import hibtests.beans.newBean;
import org.hibernate.Session;
/**
*
* #author dario
*/
public class Main {
public void test(){
Session session = NewHibernateUtil.getSessionFactory().getCurrentSession();
session.beginTransaction();
newBean nb = new newBean();
nb.setNome("FooFoo");
session.save(nb);
session.getTransaction().commit();
}
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
// TODO code application logic here
Main main = new Main();
main.test();
}
}
...and it was working fine, putting rows in the db. Then I worked on another class for a couple of hours. I try this example again and Hibernate makes this strange query:
Hibernate:
insert
into
TEST
(ID, NOME)
values
(default, ?)
Hibernate:
values
identity_val_local()
like it just can't read the property that is FooFoo. I checked if I changed the source... but it is not the case. Everything is just like before and there are not exceptions. The newBean instance is not null and FooFoo is in the Nome field. Why this?
Oh, I forgot, I'm using Netbeans 6.8 and JavaDB.
As requested, my mapping follows:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="hibtests.beans.newBean" table="APP.TEST">
<id name="id" column="ID">
<generator class="identity"/>
</id>
<property name="nome" column="NOME" type="string"/>
</class>
</hibernate-mapping>
Last minute update: turns out that insertion is working. Anyway I can still see the query with a ? instead of the string. Why?
As requested newbean source code follows:
public class newBean {
Long id;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getNome() {
return nome;
}
public void setNome(String nome) {
this.nome = nome;
}
String nome;
}
You will never see the value of the strings being inserted in the DB, you will always see them as question marks (?) there are sniffers that will show their contents, but in standard hibernate you will not see any values.

Categories