Stop Hibernate appending null values to entity on update - java

I searched many posts of Stack overflow dint find anything much related which helps .
The problem:
I have an entity
stock
xml file:
<class entity-name="stock" table="STOCK">
<id name="id" type="java.lang.Long" column="ID">
<generator class="identity" />
</id>
<property name="name" column="NAME" type="string" />
<property name="Type" column="TYPE" type="string" />
<property name="tax" column="TAX" type="double" />
<property name="password" column="PASSWORD" type="string" />
</class>
I am using hibernate to update table.
The problem when send only required fileds for update from my UI
like
[{
"id":2,
"name": "stock1"
}]
In such case hibernate appends
nulls to other attributes of the entity
like
Hibernate Automaticaly genrated nulls for the unchanged columns:
Stock{Type=null, tax=null, password=null, name=stock1, id=2}
Is there a way to avoid this
I tried
this
**dynamic-update** which dint work.
I even tried:
adding interceptor
adding MyInterceptor .
which calls
public boolean onFlushDirty(Object entity, Serializable id, Object[] currentState, Object[] previousState, String[] propertyNames, Type[] types) {
}
I read the json to a List<Map<String, Object>> using gson.
update code:
Session s = this.sessionFactory.withOptions().interceptor(new
MyInterceptor()).openSession();
Transaction tx = s.beginTransaction();
if (list.size() != 0) {
for (int i = 0; i < list.size(); i++) {
s.update(dataEntityName, list.get(i));
}
}
tx.commit();
s.close();
Is there anything other way to achive this in hibernate ?

Related

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

Struts / hibernate NonUniqueObjectException: different object with the same identifier value was already associated with the session

I have the following issue :
org.hibernate.NonUniqueObjectException:
A different object with the same identifier value was already associated with the session.
Strangely it does not occurs systematically.
Entity is loaded from database to Struts and set in HTTP Session.
Once modified it's send to business service for saving.
(sessionFactory.update, sessionFactory.save)
It should always have hibernate issue but the org.hibernate.NonUniqueObjectException occurs only some times (Don't know why)
Does any one have an explanation ?
I think about reload the object from hibernate session in my business service and copy data from entity object from Struts HTTP Session.
#Override
public boolean equals(Object autre) {
if (this == autre) {
return true;
}
if ((autre == null) || (autre.getClass() != this.getClass())) {
return false;
}
MouvementFinancier entite = (MouvementFinancier) autre;
if (pk == null || entite.pk == null) {
return false;
}
return pk.equals(entite.pk);
}
#Override
public int hashCode() {
if (pk == null) {
return super.hashCode();
}
return pk.hashCode();
}
Hibernate mapping
<class name="com.XXX.MouvementFinancier" table="MOUVEMENT_FINANCIER"
discriminator-value="0" abstract="true">
<id name="pk" type="integer" column="PK_MOUVEMENT_FINANCIER" unsaved-value="null">
<generator class="com.XXX.TableGenerator">
<param name="segment_value">MOUVEMENT_FINANCIER</param>
</generator>
</id>
<discriminator column="CLASSE" type="integer" />
<timestamp column="DATE_VERSION" name="version" unsaved-value="null" />
<property name="commentaire" column="COMMENTAIRE" />
...
<set name="actes" cascade="all,delete-orphan" fetch="select" sort="natural">
<key column="PK_MOUVEMENT_FINANCIER" not-null="true" />
<one-to-many class="com.p****.HistoriqueMouvement" />
</set>
<subclass name="com.XXX.Encaissement" discriminator-value="2">
<property name="purpose" column="NATURE_RECUPERATION" />
...
<many-to-one name="emetteur" column="PK_PERSONNE_EMETTEUR"
class="com.XXX.Personne" cascade="none" fetch="join" lazy="false"/>
<many-to-one name="acteurEmetteur" column="PK_ACTEUR_EMETTEUR"
class="com.XXX.Acteur" cascade="none" fetch="join" lazy="false"/>
</subclass>
<subclass name="com.XXX.Reglement" discriminator-value="1">
<property name="dateAutorisation" type="timestamp" column="DATE_AUTORISATION" />
<property name="franchise" column="FRANCHISE" />
...
<many-to-one name="beneficiaire" column="PK_PERSONNE_BENEFICIAIRE"
class="com.XXX.Personne" cascade="none" fetch="join" lazy="false"/>
<many-to-one name="acteurBeneficiaire" column="PK_ACTEUR_BENEFICIAIRE"
class="com.XXX.Acteur" cascade="none" fetch="join" lazy="false"/>
<many-to-one name="adresseCourrierReglementBeneficiaire" column="PK_ADD_COUR_REG_BENEF"
class="com.XXX.AdresseCourrierReglement" cascade="none" fetch="join" lazy="false"/>
<many-to-one name="destinataire" column="PK_PERSONNE_DESTINATAIRE"
class="com.XXX.Personne" cascade="none" fetch="join" lazy="false"/>
<many-to-one name="acteurDestinataire" column="PK_ACTEUR_DESTINATAIRE"
class="com.XXX.Acteur" cascade="none" fetch="join" lazy="false"/>
<many-to-one name="adresseCourrierReglementDestinataire" column="PK_ADD_COUR_REG_DEST"
class="com.XXX.AdresseCourrierReglement" cascade="none" fetch="join" lazy="false"/>
<many-to-one name="rib" column="PK_RIB" class="com.XXX.assureur.RIB"
cascade="all" fetch="select" />
<set name="ventilationDepenses" cascade="all,delete-orphan" fetch="select">
<key column="PK_MOUVEMENT_FINANCIER" not-null="true" />
<one-to-many class="com.XXX.Depense"/>
</set>
</subclass>
</class>
<class name="com.XXXX.Depense" table="VENTILATION_DEPENSES">
<id name="pk" type="integer" column="PK_VENTILATION_DEPENSE" unsaved-value="null">
<generator class="com.XXX.TableGenerator">
<param name="segment_value">VENTILATION_DEPENSES</param>
</generator>
</id>
<property name="nature" column="CODE_NATURE_DEPENSE" />
<property name="montant" column="MONTANT_DEPENSE" />
...
</class>
Issue has been solved by using Hiberante merge in DAO
But I still wonder why in most case there was no issue to send back a "detached" entity to service layer without using merge command or copy in an entity loaded from hibernate session.
// TODO : save (twice) replaced by 1 merge to avoid issue on detached object
// if we have any more pb on this, redo all : load payment from session and copy data in from dto.
session.merge(reglement);
Complete code
#Override
public void update...(...) {
if (logger.isDebugEnabled()) {
logger.debug("Updating a settlement");
}
Session session = sf.getCurrentSession();
final PartieFinanciere partieReglement = (PartieFinanciere) session.get(PartieFinanciere.class, pkPartieFinanciere);
if (partieReglement == null) {
throw new ExceptionPkEntiteInconnu(PartieFinanciere.class, pkPartieFinanciere);
}
// History : last movement amount to remove it on total amount
HistoriqueMouvement movementHistoryLast = reglement.getActes().stream().sorted((h1, h2) -> h2.compareTo(h1)).findFirst().orElse(null);
Double mouvementLastAmount = movementHistoryLast != null ? movementHistoryLast.getMontant() : 0;
// History : add movement modification in history
HistoriqueMouvement histo = new HistoriqueMouvement();
histo.setActe(...);
histo.setDate(...);
histo.setMontant(...);
.....
reglement.getActes().add(histo);
partieReglement.getMouvements().add(reglement);
// Recalculate total amount : remove previous movement amount, set movement modified amount
Double amountProvision = ofNullable(partieReglement.getTotalMouvements()).orElse(0.0).doubleValue() - mouvementLastAmount + reglement.getMontant();
partieReglement.setTotalMouvements(amountProvision);
if (logger.isDebugEnabled()) {
logger.debug("Updating total mouvement and suspens.");
}
mettreAJourTotalMouvementsEtSuspens(session, partieReglement);
ajouterActePartieFinanciere(gestionnaire, partieReglement, getHistoriquePartieActe(null, reglement.getType(), false), reglement.getMontant());
// TODO : save (twice) replaced by 1 merge to avoid issue on detached object
// if we have any more pb on this, redo all : load payment from session and copy data in from dto.
session.merge(reglement);
}

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.

Many to one (item - type relation): identifer was change from x to y

I have 2 class Item & Type. An Item belongs to one type.
It works ok. But when I try to change TYPE of an already saved item, it brings out the error:
identifier of an instance of com.myapp.model.Type was altered from 1
to 2.
It seems that Hibernate is thinking that I try to edit the id of Type. Actually, I want to change the Type of an item, not edit the type it is in. For a quick example, I have an Item name "Ball" belongs to "sport" type, but now I want to change it to "Tool" type. And Hibernate think that I want to change the name (and Id) of "Sport" type into "Tool"!
Pseudo code:
Item item = getItemFromDatabase(itemId);
item.setType(newType);
saveItem(item);
My mapping files:
<hibernate-mapping>
<class name="com.myapp.model.Item" table="ITEM">
<cache usage="read-write"/>
<id name="id" column="ID">
<generator class="sequence">
<param name="sequence">item_seq</param>
</generator>
</id>
<property name="name" column="NAME"/>
<many-to-one name="type"
class="com.myapp.model.Type"
column="type_id"
foreign-key="ITEM_TYPE_FK"
/>
<property name="description" column="DESCRIPTION"/>
</class>
</hibernate-mapping>
And
<hibernate-mapping>
<class name="com.myapp.model.Type" table="TYPE">
<cache usage="read-write"/>
<id name="id" column="ID">
<generator class="sequence">
<param name="sequence">type_seq</param>
</generator>
</id>
<property name="name" column="NAME"/>
</class>
</hibernate-mapping>
Does I misunderstand anything?
UPDATE:
I have an Hibernate utility function like this:
#Override
#SuppressWarnings("unchecked")
public <T> T getById(Class<T> entityClass, Serializable id) {
try {
Object result = this.sessionFactory.getCurrentSession().get(entityClass, id);
if (result != null) {
return (T)result;
} else {
return null;
}
} catch (Exception e) {
logger.error(e.getMessage(), e);
throw new DataAccessException(e);
}
}
Then in my code, I just call:
Type updatedType = repository.getById(Type.class, item.getType().getId());
The way that I think is correct is
Item item = getItemFromDatabase(itemId);
item.setType(getItemTypeFromDatabase(newTypeId));
saveItem(item);

Error SQL syntax?

I am using hibernate in my java program and I got some troubles using it ..
Here is my xml:
<hibernate-mapping>
<class name="revEngMapping.TestNetwork" table="testNetwork">
<id name="id" type="java.lang.Integer">
<column name="id" />
<generator class="identity" />
</id>
<property name="networkElementId" type="string">
<column name="networkElement_id" length="45" />
</property>
<property name="date" type="string">
<column name="Date" length="45" />
</property>
<property name="oid" type="string">
<column name="Oid" length="150" />
</property>
<property name="value" type="string">
<column name="Value" length="200" />
</property>
</class>
here is my java class:
public class TestNetwork implements java.io.Serializable {
private Integer id;
private String networkElementId;
private String date;
private String oid;
private String value;
public TestNetwork() {
}
public TestNetwork(String networkElementId, String date, String oid,
String value) {
this.networkElementId = networkElementId;
this.date = date;
this.oid = oid;
this.value = value;
}
Then its just getters and setters, and in my main, I just want to display it:
Session session = HibernateUtil.getSessionFactory().getCurrentSession();
session.beginTransaction();
Criteria crit = session.createCriteria(TestNetwork.class);
List resultList=crit.list();
for(int i=0;i<resultList.size();i++)
System.out.println(((TestNetwork)resultList.get(i)).getId()+" "+((TestNetwork)resultList.get(i)).getDate());
session.getTransaction().commit();
In my console it says that I have an SQL error ..
10:20:54,626 WARN JDBCExceptionReporter:233 - SQL Error: 1064, SQLState: 42000
10:20:54,628 ERROR JDBCExceptionReporter:234 - You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '.testNetwork this_' at line 1
Exception in thread "main" org.hibernate.exception.SQLGrammarException: could not execute query
at org.hibernate.exception.SQLStateConverter.convert(SQLStateConverter.java:92)
at org.hibernate.exception.JDBCExceptionHelper.convert(JDBCExceptionHelper.java:66)
at org.hibernate.loader.Loader.doList(Loader.java:2536)
Hope s.one can help. thanks
mysql requires you to use backticks `` when accessing table names & columns, please back ticks while defining
eg..
<property name="someProperty" column="`dbColumnName`"/>
this should solve your problem..
Anantha Sharma

Categories

Resources