I’m learning Java and Hibernate. Right now, I’m having trouble understanding how to use a custom physical naming strategy: While the PhysicalNamingStrategy object is indeed instantiated, the toPhysicalTableName or toPhysicalColumnName methods are never called – not that I can see with a debugger, at least.
Versions: Java 1.8, Hibernate 5.2.10.Final, on macOS 10.12.
Here’s a minimal project:
#Entity
public class Cake {
#Id
private long id;
private String name;
private String FLAVOUR;
private int sErViNg;
public Cake(String name, String flavour, int serving) {
this.name = name;
this.FLAVOUR = flavour;
this.sErViNg = serving;
}
// getters and setters
public class Main {
public static void main (String[] args) {
Transaction tx = null;
try (
SessionFactory sessionFactory = new Configuration().configure().buildSessionFactory();
Session session = sessionFactory.openSession();
) {
tx = session.beginTransaction();
Cake cake = new Cake("Molten Chocolate Cake", "chocolate", 1);
session.save(cake);
tx.commit();
}
catch (Exception e) {
e.printStackTrace();
if ( tx != null ) {
tx.rollback();
}
}
}
}
public class AllCapsPhysicalNamingStrategy
extends PhysicalNamingStrategyStandardImpl implements Serializable {
public static final AllCapsPhysicalNamingStrategy INSTANCE
= new AllCapsPhysicalNamingStrategy();
#Override
public Identifier toPhysicalTableName(Identifier name, JdbcEnvironment context) {
return new Identifier(name.getText().toUpperCase(), name.isQuoted());
}
#Override
public Identifier toPhysicalColumnName(Identifier name, JdbcEnvironment context) {
return new Identifier(name.getText().toUpperCase(), name.isQuoted());
}
}
<hibernate-configuration>
<session-factory>
<property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
<property name="hibernate.connection.url">jdbc:mysql://localhost/cake</property>
<property name="hibernate.connection.username">root</property>
<property name="hibernate.connection.password"></property>
<property name="hibernate.dialect">org.hibernate.dialect.MySQL5Dialect</property>
<property name="hibernate.hbm2ddl.auto">create</property>
<property name="hibernate.physical_naming_strategy">com.example.AllCapsPhysicalNamingStrategy</property>
<mapping class="com.example.Cake"/>
</session-factory>
</hibernate-configuration>
Here’s the table I get:
[cake]> SELECT * FROM cake;
+----+-----------+-----------------------+---------+
| id | FLAVOUR | name | sErViNg |
+----+-----------+-----------------------+---------+
| 0 | chocolate | Molten Chocolate Cake | 1 |
+----+-----------+-----------------------+---------+
I would expect:
+----+-----------+-----------------------+---------+
| ID | FLAVOUR | NAME | SERVING |
+----+-----------+-----------------------+---------+
| 0 | chocolate | Molten Chocolate Cake | 1 |
+----+-----------+-----------------------+---------+
What am I doing wrong here?
This isn't very well documented but unfortunately it seems Hibernate doesn't support that particular property being set in hibernate.cfg.xml. To quote from a very old Hibernate forum post:
You can set the properties given Environment.java class only in
hibernate.properties or hibernate.cfg.xml. Rest of the properties like
NamingStrategy has to be configured with Configuration class.
So would recommend removing the property and instead setting this in code on the Configuration instance, as proposed by Shiv Raghuwanshi.
You can set in configuration also.
public class Main {
public static void main (String[] args) {
Transaction tx = null;
try (
Configuration configuration =new Configuration();
configuration.setPhysicalNamingStrategy(new AllCapsPhysicalNamingStrategy());
SessionFactory sessionFactory = configuration.configure().buildSessionFactory();
Session session = sessionFactory.openSession();
) {
tx = session.beginTransaction();
Cake cake = new Cake("Molten Chocolate Cake", "chocolate", 1);
session.save(cake);
tx.commit();
}
catch (Exception e) {
e.printStackTrace();
if ( tx != null ) {
tx.rollback();
}
}
}
}
There is nothing wrong with the your configuration. It is just that bootstrapping hibernate using Configuration object requires you to set some of the config property on configuration object itself. These configuration specified via properties will get ignored.
Also, bootstrapping hibernate using Configuration object is considered as "legacy" way (as per official hibernate docs) and newer way is recommended of bootstrapping the hibernate as shown below.
public static void main(String[] args) {
Transaction tx = null;
StandardServiceRegistry standardRegistry = new StandardServiceRegistryBuilder()
.configure() // using "hibernate.cfg.xml"
.build();
Metadata metadata = new MetadataSources(standardRegistry).buildMetadata();
try (
SessionFactory sessionFactory = metadata.getSessionFactoryBuilder().build();
Session session = sessionFactory.openSession();) {
tx = session.beginTransaction();
Cake cake = new Cake("Molten Chocolate Cake", "chocolate", 1);
session.save(cake);
tx.commit();
} catch (Exception e) {
e.printStackTrace();
if (tx != null) {
tx.rollback();
}
}
}
This will pick the Physical naming strategy specified as hibernate property in hibernate.cfg.xml file.
Related
I have a trouble when running my web application that tries to get all the information from the table snowboard and put it in a list which I will then print out in the xHtml. But I get this exception down below.
org.hibernate.TransactionException: nested transactions not supported
The thing is that I have no clue why this exception happens so would appreciate some explanation. Also if you find any trouble in the code that would be fantastic.
This is the exception that I get.
HibernateUtil
public class HibernateUtil {
private static final SessionFactory sessionFactory;
static {
try {
// Create the SessionFactory from standard (hibernate.cfg.xml)
// config file.
sessionFactory = new AnnotationConfiguration().configure().buildSessionFactory();
} catch (Throwable ex) {
// Log the exception.
System.err.println("Initial SessionFactory creation failed." + ex);
throw new ExceptionInInitializerError(ex);
}
}
public static SessionFactory getSessionFactory() {
return sessionFactory;
}
}
SnowHelper, HelperClass
public class SnowHelper {
Session session = null;
public SnowHelper() {
this.session = HibernateUtil.getSessionFactory().getCurrentSession();
}
public List getSnowboards() {
List<Snowboard> snowboardList = null;
try {
Transaction tx = session.beginTransaction();
Query q = session.createQuery("from Snowboard");
snowboardList = (List<Snowboard>) q.list();
} catch (Exception e) {
e.printStackTrace();
}
return snowboardList;
}
}
HibernateCfg
<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://cpsrv01.misshosting.com:3306/etvffqgz_snowshop</property>
<property name="hibernate.connection.username">etvffqgz_user</property>
<property name="hibernate.connection.password">759486456</property>
<property name="hibernate.show_sql">true</property>
<property name="hibernate.current_session_context_class">thread</property>
<property name="hibernate.query.factory_class">org.hibernate.hql.internal.classic.ClassicQueryTranslatorFactory</property>
<mapping resource="Hibernate/Account.hbm.xml"/>
<mapping resource="Hibernate/Snowboard.hbm.xml"/>
</session-factory>
</hibernate-configuration>
SnowboardBean, ManagedBean class
#Named(value = "snowboardBean")
#Dependent
public class SnowboardBean {
private List<Snowboard> snowList;
private SnowHelper snow;
/**
* Creates a new instance of SnowboardBean
*/
public SnowboardBean() {
snowList = new ArrayList<>();
snow = new SnowHelper();
snowList = snow.getSnowboards();
}
/**
* #return the snowList
*/
public List<Snowboard> getSnowList() {
return snowList;
}
}
In this bit here.. commit your transaction
Transaction tx = session.beginTransaction();
Query q = session.createQuery("from Snowboard");
snowboardList = (List<Snowboard>) q.list();
tx.commit();
Otherwise you simply open a new transaction without closing it every time you invoke this method.. eventually one of them is opened while some other is not commited yet.
If you were using 'container managed transactions' (provided by Spring of EJB's) you would not have to worry about explicitly committing your transactions. Here you are using 'extended transaction management' and you have to take care of that yourself.
I am trying to write some information about the user who opens the web application which i made. The web app is simple. When open it it shows users ip, user-agent, and the time of the last log into the app. I`ve made this app with these technologies: Spring, Maven, Tomcat, Hibernate and MySQL. It seems that everything is working correctly but when i open the app in browser in do note make a record to Data Base and i must start manually Main.java to make a record but the record is no correct because it does not record the real information but it record NULL everywhere. I have two questions - How to do all this automatically (when i open the web app to have a record in DB) and Why my records are every time NULL.
public class Main {
public static void main(String[] args) {
UserInfo user = new UserInfo();
user.setRollNo(7);
user.setIp(UserController.ip);
user.setUserAgent(UserController.userAgent);
user.setDate((Date) UserController.date);
SessionFactory sessionFactory = new AnnotationConfiguration()
.configure().buildSessionFactory();
Session session = sessionFactory.openSession();
session.beginTransaction();
session.save(user);
session.getTransaction().commit();
session.close();
}
}
UserController.java
#Controller
public class UserController {
public static String ip;
public static String userAgent;
public static Date date;
#Autowired
private HttpServletRequest request;
#RequestMapping(value = "/connection")
public ModelAndView getUserInfo() {
ModelAndView modelandView = new ModelAndView("UserInfo");
ip = request.getRemoteAddr();
modelandView.addObject("msg1", "Your IP is " + ip);
userAgent = request.getHeader("User-Agent");
modelandView.addObject("msg2", "Your User agent is " + userAgent);
date = new java.util.Date();
/*
* Calendar calendar = Calendar.getInstance(); java.sql.Date startDate =
* new java.sql.Date(calendar.getTime() .getTime());
*/
modelandView.addObject("msg3", "Your last log was at " + date);
return modelandView;
}
}
UserInfo.java
#Entity
#Table(name = "USER_INFORMATION")
public class UserInfo {
#Id
private int rollNo;
private String ip;
private String userAgent;
private Date date;
public int getRollNo() {
return rollNo;
}
public void setRollNo(int rollNo) {
this.rollNo = rollNo;
}
public String getIp() {
return ip;
}
public void setIp(String ip) {
this.ip = ip;
}
public String getUserAgent() {
return userAgent;
}
public void setUserAgent(String userAgent) {
this.userAgent = userAgent;
}
public Date getDate() {
return date;
}
public void setDate(Date date) {
this.date = date;
}
}
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>
<!-- Database connection settings -->
<property name="connection.driver_class">com.mysql.jdbc.Driver</property>
<property name="connection.url">jdbc:mysql://localhost:3306/browserinfo</property>
<property name="connection.username">root</property>
<property name="connection.password"></property>
<property name="connection.pool_size">1</property>
<property name="dialect">org.hibernate.dialect.MySQLDialect</property>
<property name="cache.provider_class">org.hibernate.cache.NoCacheProvider</property>
<property name="show_sql">true</property>
<property name="hbm2ddl.auto">validate</property>
<mapping class="com.nikola.hellocontroller.UserInfo" />
</session-factory>
</hibernate-configuration>
According to this:
#novy154 I am trying to make e record to DB with the users IP, user-agent info and the time when the user was lastloged in the app. After that i want to display all records in the app.
First of all, you need to have some DataAccessLayer. Something like
public interface UserInfoRepository {
void save(UserInfo userInfo);
Collection<UserInfo> findAll();
}
and it's implementation.
Then maybe some service over the Repository, but it doesn't matter in this case, so let's skip it.
You need to inject your repository into controller and:
1) invoke userInfoRepository.save(newlyCreatedUserInfo)
2) pass all UserInfos to your view (by invoking userInfoRepository.findAll() and putting the result in modelAndView) and display it somehow.
Let me know if you need help implementing this.
But this is just in purpose of learning how to make simple crud-like application, cause this doesn't make any sense. You can't even identify your user, you just want to track each visitor's ip, user info and so on.
// edit:
Repository implementation would be something like:
#Repository
#Transactional
public class UserInfoRepositoryImpl implements UserInfoRepository {
#Autowired
private SessionFactory sessionFactory;
#Override
public void save(UserInfo userInfo) {
getCurrentSession.save(userInfo);
}
#Override
public Collection<UserInfo> findAll() {
final Criteria root = getCurrentSession().createCriteria(UserInfo.class);
return Collections.checkedList(root.list(), UserInfo.class);
}
private Session getCurrentSession() {
return sessionFactory.getCurrentSession();
}
}
Then in your controller method you need to invoke save(newlyCreatedUserInfo) to store your info in the database and
modelAndView.addObject("userInfos", repository.findAll());
to retrieve all data and pass it to a view.
Of course remember about
...
#Autowired
private UserInfoRepository userInfoRepository;
...
in controller.
Note that since spring-data-jpa provides you more powerful repositories out of the box, this is reinventing a wheel.
Can someone please tell me why am I geting java.sql.SQLException: This function is not supported using HSQL and Spring? I am trying to insert a new row into my database..
Below is my DAO and I get the error on the mySession.save(message) line:
#Transactional
#Repository
public class MessageDaoImpl implements MessageDao
{
private Log log = null;
#Autowired
private SessionFactory sessionFactory;
public MessageDaoImpl()
{
super();
log = LogFactory.getLog(MessageDaoImpl.class);
}
#SuppressWarnings("unchecked")
#Transactional(readOnly = true, propagation = Propagation.REQUIRED)
public List<Message> listMessages()
{
try
{
return (List<Message>) sessionFactory.getCurrentSession()
.createCriteria(Message.class).list();
} catch (Exception e)
{
log.fatal(e.getMessage());
return null;
}
}
#SuppressWarnings("unchecked")
#Transactional(readOnly = false, propagation = Propagation.REQUIRED)
public void SaveOrUpdateMessage(Message message)
{
try
{
Session mySession = sessionFactory.getCurrentSession();
mySession.save(message);
mySession.flush();
} catch (Exception e)
{
log.fatal(e.getMessage());
}
}
}
Here is my main class:
public static void main(String[] args)
{
ApplicationContext context = new AnnotationConfigApplicationContext(HelloWorldConfig.class);
MessageService mService = context.getBean(MessageService.class);
HelloWorld helloWorld = context.getBean(HelloWorld.class);
/**
* Date: 4/26/13 / 9:26 AM
* Comments:
*
* I added Log4J to the example.
*/
LOGGER.debug("Message from HelloWorld Bean: " + helloWorld.getMessage());
Message message = new Message();
message.setMessage(helloWorld.getMessage());
//
mService.SaveMessage(message);
helloWorld.setMessage("I am in Staten Island, New York");
LOGGER.debug("Message from HelloWorld Bean: " + helloWorld.getMessage());
}
}
Here is my DatabaseConfig:
public class DatabaseConfig
{
private static final Logger LOGGER = getLogger(DatabaseConfig.class);
#Autowired
Environment env;
#Bean
public DataSource dataSource() {
EmbeddedDatabaseBuilder builder = new EmbeddedDatabaseBuilder();
EmbeddedDatabase db = builder.setType(EmbeddedDatabaseType.HSQL).
addScript("schema.sql").build();
return db;
}
#Bean
public DataSource hsqlDataSource() {
BasicDataSource ds = new BasicDataSource();
try {
ds.setDriverClassName("org.hsqldb.jdbcDriver");
ds.setUsername("sa");
ds.setPassword("");
ds.setUrl("jdbc:hsqldb:mem:mydb");
}
catch (Exception e)
{
LOGGER.error(e.getMessage());
}
return ds;
}
#Bean
public SessionFactory sessionFactory()
{
LocalSessionFactoryBean factoryBean = new LocalSessionFactoryBean();
factoryBean.setDataSource(hsqlDataSource());
factoryBean.setHibernateProperties(getHibernateProperties());
factoryBean.setPackagesToScan(new String[]{"com.xxxxx.HelloSpringJavaBasedJavaConfig.model"});
try
{
factoryBean.afterPropertiesSet();
} catch (IOException e)
{
LOGGER.error(e.getMessage());
e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates.
}
return factoryBean.getObject();
}
#Bean
public Properties getHibernateProperties()
{
Properties hibernateProperties = new Properties();
hibernateProperties.setProperty("hibernate.dialect", env.getProperty("hibernate.dialect"));
hibernateProperties.setProperty("hibernate.show_sql", env.getProperty("hibernate.show_sql"));
hibernateProperties.setProperty("hibernate.use_sql_comments", env.getProperty("hibernate.use_sql_comments"));
hibernateProperties.setProperty("hibernate.format_sql", env.getProperty("hibernate.format_sql"));
hibernateProperties.setProperty("hibernate.hbm2ddl.auto", env.getProperty("hibernate.hbm2ddl.auto"));
hibernateProperties.setProperty("hibernate.generate_statistics", env.getProperty("hibernate.generate_statistics"));
hibernateProperties.setProperty("javax.persistence.validation.mode", env.getProperty("javax.persistence.validation.mode"));
//Audit History flags
hibernateProperties.setProperty("org.hibernate.envers.store_data_at_delete", env.getProperty("org.hibernate.envers.store_data_at_delete"));
hibernateProperties.setProperty("org.hibernate.envers.global_with_modified_flag", env.getProperty("org.hibernate.envers.global_with_modified_flag"));
return hibernateProperties;
}
#Bean
public HibernateTransactionManager hibernateTransactionManager()
{
HibernateTransactionManager htm = new HibernateTransactionManager();
htm.setSessionFactory(sessionFactory());
htm.afterPropertiesSet();
return htm;
}
}
and here is what I am getting to the console:
Exception in thread "main" org.hibernate.AssertionFailure: null id in com.xxx.HelloSpringJavaBasedJavaConfig.model.Message entry (don't flush the Session after an exception occurs)
Here is my message model bean:
#Entity
#Table(name = "messages")
public class Message
{
#Id
#Column(name = "id")
#GeneratedValue(strategy = GenerationType.AUTO)
private String id;
#Column(name = "message")
private String message;
public String getId()
{
return id;
}
public void setId(String id)
{
this.id = id;
}
public String getMessage()
{
return message;
}
public void setMessage(String message)
{
this.message = message;
}
#Override
public String toString()
{
return "Message{" +
"id='" + id + '\'' +
", message='" + message + '\'' +
'}';
}
}
This relates to the version of hsql being used probably the version causing issue was 1.8 with hibernate 4. Need to use 2.2.9 instead
You can't use a String with #GenerateValue with the Strategy GenerationType.AUTO since it uses sequence generator and those can't be used with non-numerical values. You have to set it yourself. Use an Integer or Long if you want it to be generated for you.
Hibernate docs
Or use an id generator that uses string values
#Id
#GeneratedValue(generator="system-uuid")
#GenericGenerator(name="system-uuid", strategy = "uuid")
it was a version issues. I updated the versions and now everything works
I had the same issue after I upgraded hibernate to version 4.2.8 .Looking in the logs, I noticed that the sql query generated by hibernate tried to insert a record with a null primary key. The field was annotated just with: #Id #GeneratedValue
Upgrading hsqldb to version 2.2.9 made this disappear just like Denis said and I am very thankful to him for the reply.
It seems very likely that this issue is related to attempting to use a Session which has already signaled an error. As Sotirios mentioned, it is a bad idea to catch exceptions in your DAO, and if you do, it is critical that you re-throw them.
Normally, once you catch a Hibernate exception, you must roll back your transaction and close the session as the session state may no longer be valid (Hibernate core documentation).
If the Session throws an exception, including any SQLException, immediately rollback the database transaction, call Session.close() and discard the Session instance. Certain methods of Session will not leave the session in a consistent state. No exception thrown by Hibernate can be treated as recoverable. Ensure that the Session will be closed by calling close() in a finally block.
Since you're using Spring, most of that doesn't apply to you, but the exception message you are seeing indicates that the actual cause of your problem probably was related to catching an exception and then continuing to use the same session.
Context
I'm creating a database environment where I'd like to split data in several different schemas to be used for different groups of users. Although, one of these databases should be shared to everyone due to it contains common entities.
Suppose databases:
DB1 - Common entities;
Wheels entity
DB2 - Group "A";
Cars entity
DB3 - Group "B";
Motorcycles entity
I have three different projects:
Project 1:
Wheels bean
Project 2:
Cars constructor
Project 3:
Motorcycles constructor
Problem
I'm trying to access wheels (Project 1) from projects/schemas (2,"A") and (3,"B")
First question: Is it possible?
Second: How can I do it?
hibernate.cfg.xml in project 2 is configured to
<property name="hibernate.connection.url">jdbc:mysql://99.999.999.99:3306/DB2</property>
This necessarily must restrict all the connections to DB2, or there's another way to add a new connection or work with all databases in 3306 port, or at least DB1?
Mapping the entities from project1 in project 2 seems not to be succeeded too, like:
<mapping class="com.company.project1.Wheels"
package="com.company.project1.Wheels" resource="com/company/project1/Wheels.hbm.xml"/>
Configuration
Eclipse Indigo
MySql 5.5
Hibernate 3.0 (mapping through xml instead annotations)
Win 7
Thanks for helping!
You can use #Table(catalog="") to specify database to which they belong to and then also can make relation across database.
in your case Wheel maps to DB1, Car to DB2 and MotorCycle to DB3 using catalog attribute.
i have used this solution with MySQL and MSSQL and works perfectly fine. only constraint this has all three DB has to be in same database server and user which is being used to access db should have appropriate permission to all DB.
As this solution just adds schema name against table in all queries.
I would divide my project in multiple self sustained projects. The Wheel project will be self sufficient project which takes care of Wheel entity.
Project 1: Wheel
This project will define Hibernate entities and DAO to access / modify wheel definitions.
Also I would configure a separate datasource in this project which points to DB1.
Entity classes:
#Entity
public class Wheel {
}
DAO classes:
#Repository
public class WheelDAO {
#Persistence
private EntityManager em;
}
Basically the idea is to separate application at DAO level. And manage transactions at Service level. Imaging WheelDAO (wired to DB1 datasource) and CarDAO (wired to DB2 datasource) and inject these in CarService.
DB1 DB2 DB2
| | |
WheelDAO CarDAO MotorcycleDAO
\_____________/ |
\_____|__________________________/
| |
| |
CarService MotorCycleService
I suggest to use Spring as IOC container to manage these dependency. Although you can achieve this without using Spring too.
What you need is just a db connection factory which allows you to use db that you want when you need it.
Take a look at the class below which you can adapte to resolve your issue
import java.net.URL;
import java.util.HashMap;
import javax.security.auth.login.Configuration;
public class HibernateUtil {
private static Log log = LogFactory.getLog(HibernateUtil.class);
private static HashMap<String, SessionFactory> sessionFactoryMap = new HashMap<String, SessionFactory>();
public static final ThreadLocal sessionMapsThreadLocal = new ThreadLocal();
public static Session currentSession(String key) throws HibernateException {
HashMap<String, Session> sessionMaps = (HashMap<String, Session>) sessionMapsThreadLocal.get();
if(sessionMaps == null) {
sessionMaps = new HashMap();
sessionMapsThreadLocal.set(sessionMaps);
}
// Open a new Session, if this Thread has none yet
Session s = (Session) sessionMaps.get(key);
if(s == null) {
s = ((SessionFactory) sessionFactoryMap.get(key)).openSession();
sessionMaps.put(key, s);
}
return s;
}
public static Session currentSession() throws HibernateException {
return currentSession("");
}
public static void closeSessions() throws HibernateException {
HashMap<String, Session> sessionMaps = (HashMap<String, Session>) sessionMapsThreadLocal.get();
sessionMapsThreadLocal.set(null);
if(sessionMaps != null) {
for(Session session : sessionMaps.values()) {
if(session.isOpen())
session.close();
}
;
}
}
public static void closeSession() {
HashMap<String, Session> sessionMaps = (HashMap<String, Session>) sessionMapsThreadLocal.get();
sessionMapsThreadLocal.set(null);
if(sessionMaps != null) {
Session session = sessionMaps.get("");
if(session != null && session.isOpen())
session.close();
}
}
public static void buildSessionFactories(HashMap<String, String> configs) {
try {
// Create the SessionFactory
for(String key : configs.keySet()) {
URL url = HibernateUtil.class.getResource(configs.get(key));
SessionFactory sessionFactory = new Configuration().configure(url).buildSessionFactory();
sessionFactoryMap.put(key, sessionFactory);
}
} catch(Exception ex) {
ex.printStackTrace(System.out);
log.error("Initial SessionFactory creation failed.", ex);
throw new ExceptionInInitializerError(ex);
} // end of the try - catch block
}
public static void buildSessionFactory(String key, String path) {
try {
// Create the SessionFactory
URL url = HibernateUtil.class.getResource(path);
SessionFactory sessionFactory = new Configuration().configure(url).buildSessionFactory();
sessionFactoryMap.put(key, sessionFactory);
} catch(Throwable ex) {
log.error("Initial SessionFactory creation failed.", ex);
throw new ExceptionInInitializerError(ex);
} // end of the try - catch block
}
public static void closeSession(String key) {
HashMap<String, Session> sessionMaps = (HashMap<String, Session>) sessionMapsThreadLocal.get();
if(sessionMaps != null) {
Session session = sessionMaps.get(key);
if(session != null && session.isOpen())
session.close();
}
}
} // end of the class
http://www.java-forums.org/
Trying to understand more about Hibernate,I wrote some code which creates some entities and saves them in db and then tries to delete one of the entities.
The mapping file for entity Customer has id generator set to native.I am using postgresql as db.
...
<class name="Customer" table="CUSTOMER">
<id column="CUSTOMER_ID" name="customer_id" type="java.lang.Long">
<generator class="native"/>
</id>
...
I came across hibernate.NonUniqueObjectException.
org.hibernate.NonUniqueObjectException: a different object with the same identifier value was already associated with the session: [org.me.hibernatestore.Customer#129]
Full stack trace here
I fired up the eclipse debugger and found that the object involved has same address in all the involved methods ..
The relevant part of the code is
public class Main {
CustomerDao custdao;
Customer mark;
public void storeDemo(){
custdao = DaoFactory.getCustomerDao();
createCustomers();
updateEntities();
deleteCustomer(mark);
}
private void createCustomers() {
mark = new Customer();
mark.setName("mark");
mark.setEmailAddress("mark#home");
mark.setAddress("121,3rd avenue");
mark.setCity("San Diego");
mark.setState("CA");
mark.setCountry("U.S.A");
}
private void updateEntities() {
Transaction tx = null;
Session session = HibernateUtil.getCurrentSession();
try{
tx = session.beginTransaction();
custdao.saveOrUpdateCustomer(mark);
tx.commit();
}catch(RuntimeException e){
tx.rollback();
throw e;
}
}
private void deleteCustomer(Customer cust){
Transaction tx = null;
Session session = HibernateUtil.getCurrentSession();
try{
tx = session.beginTransaction();
String custName = cust.getName();
custdao.deleteCustomer(cust);
tx.commit();
}catch(RuntimeException e){
tx.rollback();
throw e;
}
}
public static void main(String[] args) {
new Main().storeDemo();
}
}
With the help of debugger I found the address of object 'mark'
Main.createCustomers(): mark-> Customer#2bc3f5
CustomerDaoImpl.saveOrUpdateCustomer(Customer customer):customer-> Customer#2bc3f5
BaseDaoImpl.saveOrUpdate(T obj):obj-> Customer#2bc3f5
Main.deleteCustomer(Customer customer):customer-> Customer#2bc3f5
CustomerDaoImpl.deleteCustomer(Customer customer):customer-> Customer#2bc3f5
BaseDaoImpl.delete(T obj):obj-> Customer#2bc3f5
Experimenting further,I modified the code and through dao.findById() got a different object with same id and used that in deleteCustomer().This time the code worked without throwing any exception
public class Main {
CustomerDao custdao;
Customer mark;
public void storeDemo(){
custdao = DaoFactory.getCustomerDao();
createCustomers();
updateEntities();
Long mark_id = mark.getCustomer_id();
Customer mark2 = getCustomer(mark_id);
deleteCustomer(mark2);
}
private Customer getCustomer(Long id){
Transaction tx = null;
Customer cust = null;
Session session = HibernateUtil.getCurrentSession();
try{
tx = session.beginTransaction();
return custdao.findCustomerById(id);
}catch(RuntimeException e){
throw e;
}
}
...
}
Can someone explain this behaviour?My understanding about the 'a different object with the same identifier value' part of the error message is fuzzy ..The object as shown in debugger in the first case has same memory address everywhere in the code.Then how can it be a different object?
sincerely
Jim
This exception usually occurs when dealing with detached objects. In order to avoid that, you have to get the object and delete it in the same session or reattach it to the session and then delete it.
Hope this helps!