Hibernate not saving Object in the Database? - java

I have the following problem, when trying to insert an object in the database Hibernate gets stuck, no error returned, the object is not saved correctly.
Debugging it I found out that it hangs at
Hibernate: select nextval('hibernate_sequence')
I am using PostgreSQL as a database.
My configuration file:
<hibernate-mapping>
<class name="src.MyClass" table="MyClass">
<cache usage="read-write"/>
<id name="id" column="classid">
<generator class="native" />
</id>
<property name="name" column="name" not-null="true" unique="true" length="160"/>
</class>
</hibernate-mapping>
#Override
public void save( Myclass mc)
{
Session session = sessionFactory.getCurrentSession();
session.save( mc);
}
The SELECT part works.
I'm not sure what I'm missing.
Also using native SQL Insert command, it works.

I did'nt see you that flushing your session
Session session = sessionFactory.openSession();
session.save(mc);
session.flush();
session.close();
But most preferable is
Session session = factory.openSession();
Transaction tx = null;
try {
tx = session.beginTransaction();
session.save(mc);
tx.commit(); // Flush happens automatically
}
catch (RuntimeException e) {
tx.rollback();
throw e; // or display error message
}
finally {
session.close();
}

Related

optimistic lock exception on datetime column with hibernate + microsoft sql server 2016 + microsoft jdbc

i have a problem with the combination hibernate + mssql 2016 + microsoft jdbc driver + datetime column.
the same software it works perfectly with other databases (oracle, mysql, but also mssql <2016) and with mssql 2016 using the jtds driver, so i believe the problem is in the microsoft jdbc driver.
i use this libraries versions:
<dependencies>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>5.3.5.Final</version>
</dependency>
<dependency>
<groupId>com.microsoft.sqlserver</groupId>
<artifactId>mssql-jdbc</artifactId>
<version>7.0.0.jre8</version>
</dependency>
</dependencies>
hibernate.cfg.xml:
<hibernate-configuration>
<session-factory>
<property name="connection.driver_class">com.microsoft.sqlserver.jdbc.SQLServerDriver</property>
<property name="connection.url">jdbc:sqlserver://sql2016host\Sql2016;databaseName=problem</property>
<property name="connection.username">user</property>
<property name="connection.password">password</property>
<!-- JDBC connection pool (use the built-in) -->
<property name="connection.pool_size">1</property>
<!-- SQL dialect -->
<property name="dialect">org.hibernate.dialect.SQLServer2012Dialect</property>
<!-- Enable Hibernate's automatic session context management -->
<property name="current_session_context_class">thread</property>
<!-- Disable the second-level cache -->
<property name="cache.provider_class">org.hibernate.cache.NoCacheProvider</property>
<!-- Echo all executed SQL to stdout -->
<property name="show_sql">true</property>
<mapping resource="User.hbm.xml"/>
</session-factory>
</hibernate-configuration>
User.hbm.xml:
<hibernate-mapping package="...">
<class name="User" table="USERS">
<id name="id" type="long" column="ID">
<generator class="native">
<param name="sequence_name">HIBERNATE_SEQUENCE</param>
</generator>
</id>
<timestamp name="lastChange" column="LAST_CHANGE"/>
<property name="userId" column="USERID" type="string" not-null="true"/>
<property name="domain" column="DOMAIN" type="string" />
<property name="expiredOn" column="EXPIRED_ON" type="timestamp" />
<property name="firstName" column="FIRSTNAME" type="string" not-null="true"/>
<property name="lastName" column="LASTNAME" type="string" not-null="true"/>
<property name="language" column="LANGUAGE" type="string" not-null="true"/>
<property name="role" column="ROLE" type="long" not-null="true"/>
<property name="powerManager" column="POWERMANAGER" type="boolean" not-null="true"/>
<property name="notes" column="DESCRIPTION" type="string" not-null="false"/>
<property name="company" column="COMPANY" type="string" not-null="false"/>
<property name="organization" column="ORGANIZATION" type="string" not-null="false"/>
</class>
</hibernate-mapping>
Database table:
CREATE TABLE USERS(
ID numeric(19, 0) IDENTITY(1,1) NOT NULL,
LAST_CHANGE datetime NOT NULL,
USERID nvarchar(64) NOT NULL,
DOMAIN nvarchar(64) NULL,
SID nvarchar(255) NULL,
EXPIRED_ON datetime NULL,
FIRSTNAME nvarchar(255) NOT NULL,
LASTNAME nvarchar(255) NOT NULL,
LANGUAGE nvarchar(255) NOT NULL,
ROLE numeric(19, 0) NOT NULL,
POWERMANAGER tinyint NULL,
AUTH_TYPE int NULL,
AUTH_PWD_ID numeric(19, 0) NULL,
AUTH_PWD_CHANGE tinyint NULL,
AUTH_PWD_NOEXPIRE tinyint NULL,
AUTH_PWD_ENFORCE_POLICIES tinyint NULL,
AUTH_LOGIN_SUCCESS_DATE datetime NULL,
AUTH_LOGIN_ERROR_DATE datetime NULL,
AUTH_LOGIN_ERROR_COUNT int NOT NULL,
DESCRIPTION nvarchar(255) NULL,
COMPANY nvarchar(64) NULL,
ORGANIZATION nvarchar(64) NULL
)
User.java:
public class User {
private long id;
private Date lastChange;
private String userId;
private String domain;
private String firstName;
private String lastName;
private String language;
private String notes;
private String company;
private String organization;
private Date expiredOn;
private long role;
private boolean powerManager;
public User() {
}
public long getId() ..
public void setId(long id) ...
public Date getLastChange() ...
public void setLastChange(Date lastChange) ...
public String getUserId() ...
public void setUserId(String userId) ...
public String getDomain() ...
public void setDomain(String domain) ...
....
}
Main.java, it's a command line, single thread main():
private void test() {
try {
Session session = HibernateUtil.getSessionFactory().getCurrentSession();
session.beginTransaction();
// i load and update the user 'USER'
User u = getUserAuth(session, "USER");
u.setCompany("NEWCO");
session.update(u);
session.getTransaction().commit();
} catch(Exception e) {
e.printStackTrace();
}
}
private User getUserAuth(Session session, String userId) throws Exception
{
TypedQuery<User> query = session.createQuery("from User u where u.domain = NULL and upper(u.userId) = upper(:userId)", User.class);
query.setParameter("userId", userId);
List<User> users = query.getResultList();
if (users.size() != 1)
throw new Exception(userId);
return users.get(0);
}
Hibernate's SQL log:
Hibernate: select user0_.ID as 0_, user0_.LAST_CHANGE as JS2_0_, user0_.USERID as JS3_0_, user0_.DOMAIN as JS4_0_, user0_.EXPIRED_ON as JS5_0_, user0_.FIRSTNAME as JS6_0_, user0_.LASTNAME as JS7_0_, user0_.LANGUAGE as JS8_0_, user0_.ROLE as JS9_0_, user0_.POWERMANAGER as JS10_0_, user0_.DESCRIPTION as JS11_0_, user0_.COMPANY as JS12_0_, user0_.ORGANIZATION as JS13_0_ from USERS user0_ where (user0_.DOMAIN is null) and upper(user0_.USERID)=upper(?)
Hibernate: update USERS set LAST_CHANGE=?, USERID=?, DOMAIN=?, EXPIRED_ON=?, FIRSTNAME=?, LASTNAME=?, LANGUAGE=?, ROLE=?, POWERMANAGER=?, DESCRIPTION=?, COMPANY=?, ORGANIZATION=? where ID=? and LAST_CHANGE=?
Exception log:
ERROR: HHH000346: Error during managed flush [Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect) : [....User#6]]
javax.persistence.OptimisticLockException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect) : [.....User#6]
at org.hibernate.internal.ExceptionConverterImpl.wrapStaleStateException(ExceptionConverterImpl.java:226)
at org.hibernate.internal.ExceptionConverterImpl.convert(ExceptionConverterImpl.java:93)
at org.hibernate.internal.ExceptionConverterImpl.convert(ExceptionConverterImpl.java:181)
at org.hibernate.internal.ExceptionConverterImpl.convert(ExceptionConverterImpl.java:188)
at org.hibernate.internal.SessionImpl.doFlush(SessionImpl.java:1460)
at org.hibernate.internal.SessionImpl.managedFlush(SessionImpl.java:511)
at org.hibernate.internal.SessionImpl.flushBeforeTransactionCompletion(SessionImpl.java:3283)
at org.hibernate.internal.SessionImpl.beforeTransactionCompletion(SessionImpl.java:2479)
at org.hibernate.engine.jdbc.internal.JdbcCoordinatorImpl.beforeTransactionCompletion(JdbcCoordinatorImpl.java:473)
at org.hibernate.resource.transaction.backend.jdbc.internal.JdbcResourceLocalTransactionCoordinatorImpl.beforeCompletionCallback(JdbcResourceLocalTransactionCoordinatorImpl.java:178)
at org.hibernate.resource.transaction.backend.jdbc.internal.JdbcResourceLocalTransactionCoordinatorImpl.access$300(JdbcResourceLocalTransactionCoordinatorImpl.java:39)
at org.hibernate.resource.transaction.backend.jdbc.internal.JdbcResourceLocalTransactionCoordinatorImpl$TransactionDriverControlImpl.commit(JdbcResourceLocalTransactionCoordinatorImpl.java:271)
at org.hibernate.engine.transaction.internal.TransactionImpl.commit(TransactionImpl.java:98)
at ....BugTimestamp.test(BugTimestamp.java:43)
at ....BugTimestamp.main(BugTimestamp.java:19)
Caused by: org.hibernate.StaleObjectStateException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect) : [...User#6]
at org.hibernate.persister.entity.AbstractEntityPersister.check(AbstractEntityPersister.java:2522)
at org.hibernate.persister.entity.AbstractEntityPersister.update(AbstractEntityPersister.java:3355)
at org.hibernate.persister.entity.AbstractEntityPersister.updateOrInsert(AbstractEntityPersister.java:3229)
at org.hibernate.persister.entity.AbstractEntityPersister.update(AbstractEntityPersister.java:3630)
at org.hibernate.action.internal.EntityUpdateAction.execute(EntityUpdateAction.java:146)
at org.hibernate.engine.spi.ActionQueue.executeActions(ActionQueue.java:604)
at org.hibernate.engine.spi.ActionQueue.executeActions(ActionQueue.java:478)
at org.hibernate.event.internal.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:356)
at org.hibernate.event.internal.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:39)
at org.hibernate.internal.SessionImpl.doFlush(SessionImpl.java:1454)
... 10 more
if i switch from <timestam/p> to <version/> (with a numeric database column) it works.
Anybody had the same issue?
Thanks,
David
Probably there is something wrong when the driver is asking for the date to the database, which is the default Hibernate behavior using timestamp type as optimistic locking. Try to use the other approach, getting the time directly from the JVM. This is possible using the predefined attribute in the HBM file.
<timestamp name="lastChange" column="JS1_LAST_CHANGE" source="vm"/>
Try, this should work, but be aware of the drawback of this solution, as highlighted on the official Hibernate documentation (cluster, more jvm, ...)
There was a change in datetime precision with 2016 and this causes the update statement to fail to match on any rows.
I've had some success by setting sendTemporalDataTypesAsStringForBulkCopy=false in the connection properties. This only works with Microsoft's SQLServer driver, version 8.4+
We ran into a similar problem and changing source = db for the timestamp worked for us.

Hibernate inconsistently generating duplicate primary keys

I'm using Spring and Hibernate (hibernate-core 3.3.1.GA), and as a result of a web call, the code does a transaction with several inserts. Sometimes, one of the inserts fails with Hibernate saying 'Duplicate entry ... for key 'PRIMARY'. I have not been able to identify any pattern on when this happens -- it may work for 4 - 5 requests, and then it fails, then works on retrying, and then may fail on the next request.
Below are the relevant parts of the code:
Controller
#RequestMapping(value = "/users", method = RequestMethod.POST)
public #ResponseBody Map<Object, Object> save(<params>) throws IllegalArgumentException {
...
try {
map = userHelper.save(<parameters>);
...
} catch (Exception e) {
e.printStackTrace();
}
}
The exception is thrown in the above part.
UserHelper.save() method
#Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)
public HashMap<String, Object> save(<parameters>) throws NumberParseException, IllegalArgumentException, HibernateException {
....
userService.save(<parameters>);
return save;
}
UserService
HBDao dao;
#Autowired
public UserService(org.hibernate.SessionFactory sessionFactory) {
dao = new HBDao(sessionFactory);
}
...
#Transactional(propagation = Propagation.SUPPORTS, rollbackFor = Exception.class)
public HashMap<String, Object> save(<parameters>) throws NumberParseException {
...
User user;
// several lines to create User object
dao.save(user);
...
lookupService.saveUserConfigurations(user, userType, loginById);
...
return response;
}
HBDao
This class wraps hibernate sessions.
public HBDao(SessionFactory sf) {
this.sessionFactory = sf;
}
private Session getSession() {
sessionFactory.getCurrentSession();
}
public void save(Object instance) {
try {
getSession().saveOrUpdate(instance);
} catch (RuntimeException re) {
throw re;
}
}
lookupService.saveUserConfigurations(user, userType, loginById) call results in the below methods in LookupRepository class to be executed:
LookupRepository
#Transactional(propagation = Propagation.SUPPORTS, rollbackFor = Exception.class)
public LookupMapping save(LookupMapping configuration) {
dao.save(configuration);
return configuration;
}
public Collection<LookupMapping> saveAll(Collection<LookupMapping> configurations) {
configurations.forEach(this::save);
return configurations;
}
LookupMapping
#Entity
public class LookupMapping {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long configId;
...
}
Hibernate Mapping for LookupMapping class
<hibernate-mapping package="com...configuration.domain">
<class name="LookupMapping" table="lookup_mapping" mutable="false">
<id column="id" name="configId" type="long">
<generator class="increment"/>
</id>
...
</class>
</hibernate-mapping>
Hibernate config
<hibernate-configuration>
<session-factory name="sosFactory">
<!-- Database connection settings -->
...
<property name="connection.pool_size">2</property>
<!-- SQL dialect -->
<property name="dialect">com. ... .CustomDialect</property>
<!-- Enable Hibernate's current session context -->
<property name="current_session_context_class">org.hibernate.context.ManagedSessionContext</property>
<!-- Disable the second-level cache -->
<property name="cache.provider_class">org.hibernate.cache.NoCacheProvider</property>
<!-- Echo all executed SQL to stdout -->
<property name="show_sql">true</property>
<property name="format_sql">true</property>
...
</session-factory>
</hibernate-configuration>
Below are the lines from the log:
2018-05-04 10:24:51.321 7|13|60f566fa-4f85-11e8-ba9b-93dd5bbf4a00 ERROR [http-nio-8080-exec-1] org.hibernate.util.JDBCExceptionReporter - Duplicate entry '340932' for key 'PRIMARY'
2018-05-04 10:24:51.321 7|13|60f566fa-4f85-11e8-ba9b-93dd5bbf4a00 WARN [http-nio-8080-exec-1] org.hibernate.util.JDBCExceptionReporter - SQL Error: 1062, SQLState: 23000
2018-05-04 10:24:51.322 7|13|60f566fa-4f85-11e8-ba9b-93dd5bbf4a00 ERROR [http-nio-8080-exec-1] org.hibernate.event.def.AbstractFlushingEventListener - Could not synchronize database state with session
org.hibernate.exception.ConstraintViolationException: Could not execute JDBC batch update
at org.hibernate.exception.SQLStateConverter.convert(SQLStateConverter.java:94) ~[hibernate-core-3.3.1.GA.jar:3.3.1.GA]
at org.hibernate.exception.JDBCExceptionHelper.convert(JDBCExceptionHelper.java:66) ~[hibernate-core-3.3.1.GA.jar:3.3.1.GA]
at org.hibernate.jdbc.AbstractBatcher.executeBatch(AbstractBatcher.java:275) ~[hibernate-core-3.3.1.GA.jar:3.3.1.GA]
at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:266) ~[hibernate-core-3.3.1.GA.jar:3.3.1.GA]
at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:167) ~[hibernate-core-3.3.1.GA.jar:3.3.1.GA]
at org.hibernate.event.def.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:321) [hibernate-core-3.3.1.GA.jar:3.3.1.GA]
at org.hibernate.event.def.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:50) [hibernate-core-3.3.1.GA.jar:3.3.1.GA]
at org.hibernate.impl.SessionImpl.flush(SessionImpl.java:1027) [hibernate-core-3.3.1.GA.jar:3.3.1.GA]
at com.arl.mg.helpers.UserHelper.save(UserHelper.java:329) [classes/:?]
...
I'm working on a legacy codebase (so cannot upgrade Hibernate easily), and the code that I wrote are in LookupRepository class (and LookupService which is called in UserService).
The Duplicate entry error happens while persisting the LookupMapping objects. There are always two of this object being persisted, and when the error occurs, the duplicate ID is created same as the last entry. That is, if for the first request, IDs 999 and 1000 were inserted, and if the error occurs for the next request, the duplicate ID will be 1000 (and not 999).
Another, possibly important thing to note is that Hibernate shows this line:
org.hibernate.jdbc.ConnectionManager [] - transaction completed on session with on_close connection release mode; be sure to close the session to release JDBC resources!
This is all the info that I have so far, and I hope I've covered the relevant code as well. Any help will be much appreciated. Do let me know if I have to give more info.
Thanks!
The problem was with the ID generation strategy defined in the Hibernate mapping file.
The strategy was set as increment, which seems to work only when there are no other processes inserting to the table. In my case, it seems that sometimes there were previously open sessions, and new requests ended up inserting to the table simultaneously.
The solution was to change the strategy to native, which uses the underlying database's strategy to generate ID.
<hibernate-mapping package="com...configuration.domain">
<class name="LookupMapping" table="lookup_mapping" mutable="false">
<id column="id" name="configId" type="long">
<generator class="native"/>
</id>
...
</class>
</hibernate-mapping>
I agree with response by #shyam I would switch to some sequence generator.
But also have a look at this peace of code:
User user;
// several lines to create User object
dao.save(user);
...
lookupService.saveUserConfigurations(user, userType, loginById);
In this case you are sending user to saveUserConfigurations which is not managed, and within saveUserConfigurations you might calling merge method. This will cause additional insert statement. Consider refactoring your code to:
User user;
// several lines to create User object
// dao.save should return the stored value of user.
user = dao.save(user);
...
lookupService.saveUserConfigurations(user, userType, loginById);
With such constructions you will be using stored entity (i.e. managed by current hibernate's session). and have a look at all your code and prevent usage of not managed entities once those have been stored.

Delete a Simple row from database

I have 2 tables, testInput and testCases and in testInput i have a FK with the id of the other table.
So basically the rows I want to delete are id of the input, id of the testCase, name and a description.
'43', '21', 'USERNAME', 'USERNAME'
'44', '21', 'PASSWORD', 'PASSWORD'
I tried to delete that row and I get
java.sql.SQLIntegrityConstraintViolationException: Cannot delete or
update a parent row: a foreign key constraint fails
(mydb.testInput, CONSTRAINT fk02 FOREIGN KEY (testCase)
REFERENCES testCases (idtestCase) ON DELETE NO ACTION ON UPDATE NO
ACTION)
I don't want to delete the record of testCase. I just want to delete the inputs of that testCase. What do I do?
code if u want...
List<TestInput> previousInputs = TestInput.getInputs(testCaseName);
for(TestInput in : previousInputs) {
Database.deleteObject(in);
}
//delete the object to the database
public static void deleteObject(Object object) {
SessionFactory factory = HibernateUtil.getSessionFactory();
Session session = factory.openSession();
Transaction tx = null;
try{
tx = session.beginTransaction();
session.delete(object);
tx.commit();
}catch (HibernateException e) {
if (tx!=null) tx.rollback();
e.printStackTrace();
}finally {
session.close();
}
}
xml TestCases
<hibernate-mapping>
<class name="TestCase" table="testCases">
<meta attribute="class-description">
This class contains the testCases details.
</meta>
<id name="id" type="int" column="idtestCase">
<generator class="native"/>
</id>
<property name="name" column="name" type="string"/>
<many-to-one name="type" class="TestType" column="type" fetch="select" lazy="false"/>
<property name="data" column="data" type="binary"/>
<property name="amountOfInputs" column="amountOfInputs" type="int"/>
<property name="creationDate" column="creationDate" type="string"/>
<property name="createdBy" column="createdBy" type="string"/>
<many-to-one name="tellerConfig" class="TellerConfiguration" column="tellerConfig" fetch="select" lazy="false"/>
</class>
</hibernate-mapping>
xml testInput
<hibernate-mapping>
<class name="TestInput" table="testInput">
<meta attribute="class-description">
This class contains the testCases input details.
</meta>
<id name="id" type="int" column="idtestInput">
<generator class="native"/>
</id>
<property name="name" column="name" type="string"/>
<property name="description" column="description" type="string"/>
<many-to-one name="testCase" class="TestCase" column="testCase" fetch="select" cascade="all" lazy="false" />
</class>
Change the constraint on the foreign key fk02 from ´NO ACTION´ to 'SET NULL'
FOREIGN KEY (idtestcase)
REFERENCES testCases(idtestCase)
ON DELETE SET NULL

org.hibernate.hql.internal.ast.QuerySyntaxException: Hibernate not able to map table name

I am new to Hibernate and I'm getting this error:
org.hibernate.hql.internal.ast.QuerySyntaxException: Users is not mapped.
After doing some research I found that since HQL requires the table name to be capitalized which I have done as can be seen from my code below.
public class UserLogin {
#GET
#Path("/login")
#Produces(MediaType.APPLICATION_JSON)
public User login(#QueryParam("username") String username, #QueryParam("password") String password) {
String localUser;
String localPass;
List allUsers;
User user = new User();
if (username == null || password == null) {
user.setReturnComment("Null values sent");
return user;
}
Configuration cfg = new Configuration();
cfg.configure("hibernate.cfg.xml");
SessionFactory factory = cfg.buildSessionFactory();
Session session = factory.openSession();
UserDao userDao = new UserDao();
/*userDao.setUsername(username);
userDao.setPassword(password);*/
Transaction tx = session.beginTransaction();
//session.save(userDao);
Query queryResult = session.createQuery("from Users");
allUsers = queryResult.getResultList();
tx.commit();
session.close();
factory.close();
return user;
}
}
Here is my hbm.xml file:
<?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="dao.UserDao" table="users">
<id column="id" name="Id" type="int">
<generator class="increment"></generator>
</id>
<property column="username" name="Username" type="string" />
<property column="password" name="Password" type="string" />
</class>
</hibernate-mapping>
Not able to understand what I am doing wrong. Thanks in advance.
in HQL you should use the name of the class as mentionned in the HBM config so change your query to
Query queryResult = session.createQuery("from UserDao");

Redundant data while hibernate mapping many to one

I have 2 tables: Trip and Place (Many-To-One) and my problem is that when I add 2 trips with different data but the same place, it adds me 2 records to Trip table and 2 records into Place table, while it should add just one record into Place.
For example I had 2 trips with different dates but they were in the same place - Italy, Rome. So there should be only one record in Place with these data: Italy, Rome.
How can I avoid such behaviour in my application?
Trip class:
public class Trip implements java.io.Serializable {
private int idTrip;
private int idHotel;
private Date date;
private int cost;
private int profit;
private String organisator;
private int period;
private String food;
private String transport;
private int persons;
private int kidsAmount;
private String ownerName;
private String ownerLastName;
private Place place;
+ constructors, get() and set() methods,
Place class:
public class Place implements java.io.Serializable {
private int idPlace;
private String country;
private String city;
private String island;
private String information;
private Set<Trip> trips;
+ constructors, get() and set() methods,
Trip mapping file:
<hibernate-mapping>
<class name="pl.th.java.biuro.hibernate.Trip" table="Trip">
<id column="idTrip" name="idTrip" type="int">
<generator class="native"/>
</id>
<property column="date" name="date" type="date"/>
<property column="cost" name="cost" type="int"/>
<property column="profit" name="profit" type="int"/>
<property column="organisator" name="organisator" type="string"/>
<property column="period" name="period" type="int"/>
<property column="food" name="food" type="string"/>
<property column="transport" name="transport" type="string"/>
<property column="persons" name="persons" type="int"/>
<property column="kidsAmount" name="kidsAmount" type="int"/>
<property column="idHotel" name="idHotel" type="int"/>
<many-to-one fetch="select" name="place" class="pl.th.java.biuro.hibernate.Place">
<column name="idPlace" not-null="true"></column>
</many-to-one>
</class>
</hibernate-mapping>
Place mapping file:
<hibernate-mapping>
<class name="pl.th.java.biuro.hibernate.Place" table="Place">
<id column="idPlace" name="idPlace" type="int">
<generator class="native"/>
</id>
<property column="country" name="country" type="string"/>
<property column="city" name="city" type="string"/>
<property column="island" name="island" type="string"/>
<property column="information" name="information" type="string"/>
<set name="trips" table="Trip" inverse="true" lazy="true" fetch="select">
<key>
<column name="idPlace" not-null="true" />
</key>
<one-to-many class="pl.th.java.biuro.hibernate.Trip" />
</set>
</class>
</hibernate-mapping>
I also add some screenshot with my MySQL database, maybe there is some issue which not allow me to do this properly:
MySQL database
EDIT: Added one-to-many relation in Place mapping file and Set in Place class, but still got the same problem.
EDIT2: Adding code with persisting entites into database:
Session session = DatabaseConnection.getFactory().openSession();
Transaction tx = null;
try {
tx = session.beginTransaction();
trip.setPlace(place);
session.save(place);
session.save(trip);
tx.commit();
} catch (HibernateException e) {
if (tx != null) {
tx.rollback();
}
System.out.println("Exception found while adding new trip: " + e.getMessage());
e.printStackTrace();
} finally {
session.close();
}
But I am still having the same problem... I add one trip with place A, then in next step I add another trip with the same same, and here is what I get:
EDIT3: Creating Trip and Place objects:
Trip trip = null;
Place place = null;
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
String dateInString = tripDateField.getText();
java.util.Date date = null;
try {
date = sdf.parse(dateInString);
place = new Place(tripCountryField.getText(), tripCityField.getText(), tripIslandField.getText(), tripPlaceInfoTextArea.getText());
int period = 0, persons = 0, kidsAmount = 0;
//W razie braku niewymaganych liczbowych danych ustawiane są wartości -1
if (tripPeriodField.getText().equals("")) {
period = -1;
}
if (tripPersonsField.getText().equals("")) {
persons = -1;
}
if (tripKidsAmountField.getText().equals("")) {
kidsAmount = -1;
}
trip = new Trip(new Date(date.getTime()), Integer.parseInt(tripCostField.getText()), Integer.parseInt(tripProfitField.getText()),
tripOrganisatorField.getText(), period, tripFoodField.getText(), tripTransportField.getText(),
persons, kidsAmount, tripClientNameField.getText(), tripClientLastNameField.getText());
} catch (ParseException ex) {
Logger.getLogger(MainFrame.class.getName()).log(Level.SEVERE, null, ex);
try {
date = sdf.parse("0000-00-00");
} catch (ParseException ex1) {
Logger.getLogger(MainFrame.class.getName()).log(Level.SEVERE, null, ex1);
}
} catch (NumberFormatException e) {
place = new Place("111", "111", "111", "111");
trip = new Trip(null, WIDTH, WIDTH, dateInString, WIDTH, dateInString, dateInString, WIDTH, ABORT, dateInString, dateInString);
System.out.println("Exception while getting trip / place data: " + e.toString());
}
dataEdition.addTrip(trip, place, dataEdition.validateTripData(trip, place), addRemoveSearchTripDialog);
I get these objects data from textFields and parse them to int if needed, so that should be ok I guess. After this I pass these 2 objects into another method where I persist them into database.
It looks like you create a NEW place on every submission.
Place place = new Place(tripCountryField.getText(), tripCityField.getText(), tripIslandField.getText(), tripPlaceInfoTextArea.getText());
and then you expect Hibernate to somehow magically determine that you may actually want to work with an existing entry in the database.
This will not work.
If the submitted place already exists then you need somehow to load the existing persistent entity and work with that.
You can either fix this on the front-end by allowing user to select an existing place and sending through the ID for that or you query for a matching place
on form submission.
e.g.
Place place = myDatabaseService.findPlace(country, name ...) //does not account for misspellings
if(place == null){
place = new Place(tripCountryField.getText(), tripCityField.getText(), tripIslandField.getText(), tripPlaceInfoTextArea.getText());
}
I've tried this one, and it is not making any duplicates:
And creation of the two classes and tables in DB, I created two mapping files in the same package as the classes.
The places mapping:
<hibernate-mapping package="pl.th.java.biuro.hibernate">
<class name="Place" table="PLACE">
<id name="idPlace" type="java.lang.Long" column="ID_PLACE">
<generator class="native">
</generator></id>
<set name="trips" table="Trip" inverse="true" lazy="true" fetch="select">
<key>
<column name="idPlace" not-null="true">
</column></key>
<one-to-many class="pl.th.java.biuro.hibernate.Trip">
</one-to-many></set>
</class>
</hibernate-mapping>
And the trip mapping:
<hibernate-mapping package="pl.th.java.biuro.hibernate">
<class name="Trip" table="TRIP">
<id name="idTrip" type="java.lang.Long" column="ID_TRIP">
<generator class="native">
</generator></id>
<many-to-one name="place" class="pl.th.java.biuro.hibernate.Place" fetch="select">
<column name="idPlace" not-null="true">
</column></many-to-one>
</class>
</hibernate-mapping>
I have seen, that your trip id is not generated on your table? Is that correct? And your naming convention is a bit error-prone. Try to name db tables uppercase and the set variable in class Place to maybe trips. In your mappings you mapped the id to an int. Most Databases map it to long, so maybe there is an error too.
I think your entities need to override hashcode() and equals() methods to be able to recognize their uniqueness.

Categories

Resources