javax.persistence.EntityExistsException: A different object with the same identifier value - java

Previously i was not getting any error but suddenly i am started getting error :
javax.persistence.EntityExistsException: A different object with the same identifier value was already associated with the session : [baag.betl.dbimporter.esmatrans.db.EquSecurityReferenceData#baag.db.SECU#59c70ceb]
at org.hibernate.internal.ExceptionConverterImpl.convert(ExceptionConverterImpl.java:116) ~[hibernate-core-5.2.10.Final.jar:5.2.10.Final]
at org.hibernate.internal.ExceptionConverterImpl.convert(ExceptionConverterImpl.java:155) ~[hibernate-core-5.2.10.Final.jar:5.2.10.Final]
at org.hibernate.internal.ExceptionConverterImpl.convert(ExceptionConverterImpl.java:162) ~[hibernate-core-5.2.10.Final.jar:5.2.10.Final]
at org.hibernate.internal.SessionImpl.firePersist(SessionImpl.java:787) ~[hibernate-core-5.2.10.Final.jar:5.2.10.Final]
at org.hibernate.internal.SessionImpl.persist(SessionImpl.java:765) ~[hibernate-core-5.2.10.Final.jar:5.2.10.Final]
There is no sequence created for any column in oracle database table. Also there is combination of unique key column for esn and techid. I dont want to create new sequence column in my database table.
I think i cannot use #GeneratedValue and set it to Auto for unique columns otherwise i will get hibernate sequence error.
I am also doing clear and flush after every 1000 records processed.
if (secuData != null) {
sessionFactory.getCurrentSession().persist(secuData);
}
i++;
if (i % 1000 == 0) {
sessionFactory.getCurrentSession().flush();
sessionFactory.getCurrentSession().clear();
}

To have a composite primary key, you can either have the use of #IdClass or #Embeddable key approach.
To continue with the #IdClass approach you need to follow some rules,
The composite primary key class must be public
It must have a no-arg constructor
It must define equals() and hashCode() methods
It must be Serializable
So in your case, the class would look like,
#Entity
#Table( name = "SECU" )
#IdClass( SECU.class )
public class SECU implements Serializable
{
#Id
#Column(name = "columnName") // use the correct column name if it varies from the variable name provided
protected String esn;
#Id
#Column(name = "columnName") // use the correct column name if it varies from the variable name provided
protected BigDecimal techrcrdid;
#Column(name = "columnName") // use the correct column name if it varies from the variable name provided
protected BigDecimal preTradLrgInScaleThrshld;
#Column(name = "columnName") // use the correct column name if it varies from the variable name provided
#Temporal( TemporalType.TIMESTAMP)
protected LocalDateTime CreDt;
#Column(name = "columnName") // use the correct column name if it varies from the variable name provided
protected String fullnm;
#Override
public boolean equals( Object o )
{
if( this == o ) return true;
if( !( o instanceof SECU ) ) return false;
SECU secu = ( SECU ) o;
return Objects.equals( esn, secu.esn ) &&
Objects.equals( techrcrdid, secu.techrcrdid ) &&
Objects.equals( preTradLrgInScaleThrshld, secu.preTradLrgInScaleThrshld ) &&
Objects.equals( CreDt, secu.CreDt ) &&
Objects.equals( fullnm, secu.fullnm );
}
#Override
public int hashCode()
{
return Objects.hash( esn, techrcrdid, preTradLrgInScaleThrshld, CreDt, fullnm );
}
// getters and setters
}
Also double check your getters and setters in each entity class, the once provided in your question seems incorrect.

Your entity class appears to be modelling a composite primary key, but I only see 2 #Id and no #IdClass annotation used which is necessary.

Related

Default Column value is not working in hibernate

I set "firm_name" default value in Ben using annotation.
But when I insert data it will add NULL in database.
I want to set default value into database so that I just set the values which are require.
Other column values set as default value which is set into Bean.
Following is my Code.But it is not working. I will inset nNULL value into database.
#Entity
#Table(name = "my_leads")
public class My_leads{
#Id
#GeneratedValue(strategy = IDENTITY)
#Column(name = "id")
int id;
#Column(name = "name", length = 100,columnDefinition = "varchar(255) default 'NA'")
String name;
#Column(name = "enrtyDate", insertable = false, updatable = false, nullable = false,columnDefinition = "datetime default NOW()")
Date enrtyDate;
#Column(name = "mobileNo", nullable = false, length = 100)
String mobileNo;
#Column(name = "firm_name", length = 100, nullable = false,columnDefinition = "varchar(255) default 'No Refrence'")
String firm_name;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Date getEnrtyDate() {
return enrtyDate;
}
public void setEnrtyDate(Date enrtyDate) {
this.enrtyDate = enrtyDate;
}
public String getMobileNo() {
return mobileNo;
}
public void setMobileNo(String mobileNo) {
this.mobileNo = mobileNo;
}
public String getFirm_name() {
return firm_name;
}
public void setFirm_name(String firm_name) {
this.firm_name = firm_name;
}
}
My_Leads lead=new My_Leads();
lead.setUser(1);
lead.setMobileNo("1234567896");
lead.setName("Sajan");
lead.setPriority(1);
lead.setStage(1);
lead.setCampain(1);
adminService.SaveLead(lead)
The code you wrote is not a way to set a default value by Hibernate - it's actually for a database.
When you create a table in the database you can define a column such way that if you try inserting a null value the default value will be inserted instead. That's what you did here varchar(255) default 'No Refrence'.
If your table is already created, Hibernate is gonna ignore that statement. That statement is used only when Hibernate is creating the schema for you, using #Entity classes. If you go into your database and check column definitions you will see that your column has no default value, since it was not created by Hibernate.
You can delete your schema and let Hibernate create it for you, then the default value will work. Or you can edit your schema manually, adding default value to already existing column. For example:
ALTER TABLE my_leads ALTER COLUMN firm_name SET DEFAULT 'No Refrence'
If you let Hibernate generate your schema and still have this error - make sure those are actually null values, not NULL strings or something.
If you don't want to have the default values inserted by database, but by Hibernate, do this in your #Entity class:
String firm_name = "No Refrence";
Whenever an entity is to be inserted, Hibernate is going to generate the following DML:
INSERT
INTO my_leads (id, name, enrtyDate, mobilNo, firm_name)
VALUES (?, ?, ?, ?, ?)
In your java code, you're simply not setting a value for firm_name so that property is null.
Whether or not the default value for the column is used is going to depend upon what database platform you're using and how it interprets NULL in this case.
For example, MySQL will see that Hibernate bound NULL for the firm_name and will therefore apply the default value for you rather than setting the column as NULL.
For example, SQL Server will see that the INSERT statement contains the field firm_name in the columns section and therefore will ignore the default value and use whatever value is supplied in the values section, thus the column will be set to NULL. The only way SQL Server will use the default value is if the column is omitted from the columns section of the insert statement.
The only way to guarantee that the default value is set regardless of your database platform is to make sure that your entity state adheres to that rule too. This means you either need to initialize firm_name with the default value in the field definition, in the constructor of your class or your business logic that constructs your entities.

Is Hibernate 4.3 #DynamicUpdate really does not include null values?

I have been trying to implement entity updating using #DynamicUpdate. Documentation (http://docs.jboss.org/hibernate/orm/4.3/manual/en-US/html_single/) says:
dynamicInsert / dynamicUpdate (defaults to false): specifies that INSERT / UPDATE SQL should be generated at runtime and contain only the columns whose values are not null.
But I didn't manage to make it works, so I dug into the sources of AbstractEntityPersister, and saw this:
generateUpdateString( propsToUpdate, j, oldFields, j == 0 && rowId != null )
It is a method for generating SQL query string, based on 'propsToUpdate' which is boolean array obtained from:
getPropertiesToUpdate( dirtyFields, hasDirtyCollection );
where 'dirtyFields' is an integer array of id's of 'dirty' columns.
But nowhere in
getPropertiesToUpdate(final int[] dirtyProperties, final boolean hasDirtyCollection)
i was able to find mechanism for checking if modified column is not null, so even when entity i want to merge has some null fields, all of them are updated in the DB, overriding existing data with nulls.
My question is: where is an error in my reasoning?
EDIT:
Here's my code, as Chaitanya requested:
Entity:
#SuppressWarnings("serial")
#Entity(name = "t_quiz_groups")
#DynamicUpdate
#SelectBeforeUpdate
public class QuizGroupEntity implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "group_id")
private long id;
#Column(name = "description", nullable = false)
private String description;
#Column(name = "create_date", nullable = false)
#Column(name = "create_date")
private Calendar createDate;
+setters and getters
Then, I have a service (#Transactional) method:
public void update(long groupId, String newDescription) throws ServiceException {
QuizGroupEntity quizGroupEntity = quizRepository.getGroupById(groupId);
ExpectedNotNull.of(quizGroupEntity, RegistryErrorCodes.QUIZ_GROUP_NOT_EXISTS);
quizGroupEntity.setCreateDate(null); //for testing purposes i am setting another
field as null (shouldnt be updated then, right?)
quizRepository.lock(quizGroupEntity, LockModeType.WRITE);
quizGroupEntity.updateDescription(newDescription); // new description
quizRepository.merge(quizGroupEntity);
}
And now, in AbstractEntityPersiste, method:
getPropertiesToUpdate( dirtyFields, hasDirtyCollection );
is called, and dirtyFields[] consists of two 'dirty' columns: [0,2] - and that is correct, those two columns were modified (date and description, date set to null)
Then, generateUpdateString( propsToUpdate, j, oldFields, j == 0 && rowId != null ) is called, with 'propsToUpdate' looking like this:
[true, false, true, false, true]
Which is wrong, because indeed first and third columns were modified, but first one is set to null, that is why
generateUpdateString( propsToUpdate, j, oldFields, j == 0 && rowId != null )
Generates query:
[update t_quiz_groups set create_date=?, description=?, version=? where group_id=? and version=?]
That will modify create_date, and override it with null value.

Null pointer exception while using hibernate

I am using Hibernate and spring. this is my model class
#Entity
#NamedNativeQueries({#NamedNativeQuery(
name = "CSI_TARGET",
query = "select * from CSITARGET('CSIINDEX',2)",
resultClass = CSITarget.class)})
public class CSITarget {
#Column(name="csi_target")
private BigDecimal csi_target;
#Id
#Column(name="financialyearfrom" ,nullable = true)
private int financialyearfrom =0;
#Column( name="at_yearhalf" , nullable = true)
private String at_yearhalf = "";
public BigDecimal getCsi_target() {
return csi_target;
}
public void setCsi_target(BigDecimal csi_target) {
this.csi_target = csi_target;
}
public int getFinancialyearfrom() {
return financialyearfrom;
}
public void setFinancialyearfrom(int financialyearfrom) {
this.financialyearfrom = financialyearfrom;
}
public String getAt_yearhalf() {
return at_yearhalf;
}
public void setAt_yearhalf(String at_yearhalf) {
this.at_yearhalf = at_yearhalf;
}
I am using Hibernate to call a stored procedure in postgres database. The stored procedure returns a table which is mapped to this model class. Now my problem is, the table that is returned from the database contains a null value. I am in the need of doing some manipulations on the data. Now since the null value is mapped to the bean class I am getting a null pointer exception. How can I make hibernate ignore the null values in the database and set a default value for the corresponding property in the bean class. As you can see I have used nullable property also. It does'nt work.
financialyearfrom is int which cannot be assigned null value though corresponding column you might be having null value in database if column is defined as nullable.
For handling null values in java primitive variables, remove nullable=true and possible add default value 0, so all null value from db column would convert to 0 or 0.0 etc.
Or
Use wrapper class instead i.e. Integer which will allow you to retain null value assigned from db column.
Again, above two approaches are in general applicable for primitive variables using in Hibernate entities.
Further to add #ID column shouldn't be nullable IMO, if it corresponds to primary key column (in most of the cases it is) so your code would be wrong as primary key column doesn't allow null values.
Would it be possible to use COALESCE in your query to assign a default value to that field if its null? If that's possible that's probably the best way to fix this issue w/o having to tweak your code too much.

Mapping an enum named EMPLOYEE to a VARCHAR discriminator value 'M'

This is basically a follow-up question or repost as question How to use enums with JPA seems to have been solved in a proprietary way. This is the 2012 JPA question of basically the same, here using EclipseLink 2.3.2.
The question is how to map a Java enum to a DB string with different names. There is a DB column with the VARCHAR values 'M', 'C', 'N', and 'F'. They are cryptic and mix English and German. To improve the business layer I want to map a Java enum to the above DB strings:
public enum TradingSector
{
EMPLOYEE( "M" ), // Mitarbeiter
CUSTOMER( "C" ),
NOSTRO( "N" ),
FUND( "F" );
// this works:
// M( "M" ),
// C( "C" ),
// N( "N" ),
// F( "F" );
private final String ch;
private TradingSector( String ch )
{
this.ch = ch;
}
#Override
public String toString()
{
return this.ch;
}
}
Here's the entity:
#Entity
#Table( name = "TRADES" )
#Inheritance( strategy = InheritanceType.SINGLE_TABLE )
#DiscriminatorColumn( name = "NOSTRO_FLAG", discriminatorType = DiscriminatorType.STRING, length = 1 )
public class Trade implements Serializable
{
#Enumerated( EnumType.STRING )
#Column( name = "NOSTRO_FLAG", insertable = false, updatable = false )
private TradingSector discriminator;
...
}
This is kind of special, mapping an enum to what is already the inheritance discriminator. This however should work, as the enums named like the DB strings show.
The TRADES table is the most monolithic one in the whole context right now and it's more than desirable to split the entity up into sub classes.
I had expected simply overriding toString to work from what I read, but this results in:
Caused by: Exception [EclipseLink-116] (Eclipse Persistence Services - 2.3.2.v20111125-r10461): org.eclipse.persistence.exceptions.DescriptorException
Exception Description: No conversion value provided for the value [M] in field [TRADES.TRADING_SECTOR].
Mapping: org.eclipse.persistence.mappings.DirectToFieldMapping[discriminator-->TRADES.TRADING_SECTOR]
Descriptor: RelationalDescriptor(com.company.project.model.Trade --> [DatabaseTable(TRADES)])
at org.eclipse.persistence.exceptions.DescriptorException.noFieldValueConversionToAttributeValueProvided(DescriptorException.java:1052)
at org.eclipse.persistence.mappings.converters.ObjectTypeConverter.convertDataValueToObjectValue(ObjectTypeConverter.java:140)
at org.eclipse.persistence.mappings.converters.EnumTypeConverter.convertDataValueToObjectValue(EnumTypeConverter.java:137)
at org.eclipse.persistence.mappings.foundation.AbstractDirectMapping.getAttributeValue(AbstractDirectMapping.java:699)
at org.eclipse.persistence.mappings.foundation.AbstractDirectMapping.valueFromRow(AbstractDirectMapping.java:1299)
at org.eclipse.persistence.mappings.DatabaseMapping.readFromRowIntoObject(DatabaseMapping.java:1326)
Here's what the EclipseLink error docs at http://wiki.eclipse.org/EclipseLink_Exception_Error_Reference_%28ELUG%29 say:
ECLIPSELINK-00116: No conversion value provided for the value [{0}] in field [{1}].
Cause: The attribute conversion value for the fieldValue was not given in the object type mapping.
Action: Verify the field value, and provide a corresponding attribute value in the mapping.
Ah, yes. :-/
Q:
What's the non-proprietary (JPA 2) way to solve this (if that's possible at all, without going too far into the hacking trenches)?
PS: It's not possible to change the DB values.
Damn, I missed Pascal Thivent's great answer here: https://stackoverflow.com/a/2751896/396732
Here's the final solution:
Enum:
public enum TradingSector
{
EMPLOYEE( "M" ),
CUSTOMER( "C" ),
NOSTRO( "N" ),
FUND( "F" );
private final String ch;
private TradingSector( String ch )
{
this.ch = ch;
}
public String getCharacter()
{
return this.ch;
}
public static TradingSector translate( String ch )
{
for ( TradingSector ts : TradingSector.values() )
{
if ( ch.equals( ts.getCharacter() ) )
{
return ts;
}
}
return null;
}
}
Entity:
#Entity
#Table( name = "TRADES" )
#Inheritance( strategy = InheritanceType.SINGLE_TABLE )
#DiscriminatorColumn( name = "NOSTRO_FLAG", discriminatorType = DiscriminatorType.STRING, length = 1 )
public abstract class Trade implements Serializable
{
#Column( name = "NOSTRO_FLAG", insertable = false, updatable = false )
protected String discriminator;
#Id
#Column( name = "TRADE_ID" )
protected Long id;
...
protected Trade()
{
}
public String getDiscriminator()
{
return this.discriminator;
}
public TradingSector getTradingSector()
{
return TradingSector.translate( this.discriminator );
}
...
}
Still useful to some degree.

Specify foreign key constraint name when using Map and #ElementCollection with Hibernate

I have a sort of exotic mapping for a field:
#ElementCollection
#CollectionTable(name = "studentGradeLevel", joinColumns = #JoinColumn(name = "studentId"))
#MapKeyJoinColumn(name = "schoolYearId")
#Column(name = "gradeLevel", nullable = false)
#ForeignKey(name = "fkStudentGrade2Student")
private Map<SchoolYear, GradeLevel> gradeLevels;
SchoolYear is an entity and GradeLevel is an enum.
I am using Hibernate tools to generate the DDL for the schema. The schema that this generates is below:
create table studentGradeLevel (
studentId numeric(19,0) not null,
gradeLevel int not null,
schoolYearId int not null,
primary key (studentId, schoolYearId)
);
alter table studentGradeLevel
add constraint FK1BCA4A883A97C498
foreign key (schoolYearId)
references schoolYear;
alter table studentGradeLevel
add constraint fkStudentGrade2Student
foreign key (studentId)
references student;
The problem is that I can't seem to change the constraint name for the foreign key between the collection table and the table for the entity used as the map key.
I've used #ForeignKey to specify constraint names for #OneToMany, #ManyToMany and other #ElementCollections with no problem. I've tried #ForiegnKey's "inverseName" attribute but it seems to be ignored. #MapKeyJoinColumn doesn't appear to have any properties that would affect this.
Does anyone know if there is a way to do this?
I had to patch Hibernate to create different foreign key names, because the ones Hibernate created for me weren't really useful.
I took the Hibernate source, and placed the Source of the class org.hibernate.mapping.Table into my source folder, which is a the start of the classpath (the resulting jar in my project starts with a letter lower than the hibernate.jar, so this even works in webapps).
The I replaced the function uniqueColumnString with the following code (Original code at the top of the function):
public String uniqueColumnString(Iterator iterator, String referencedEntityName) {
// int result = 0;
// if ( referencedEntityName != null ) {
// result += referencedEntityName.hashCode();
// }
// while ( iterator.hasNext() ) {
// result += iterator.next().hashCode();
// }
// return ( Integer.toHexString( name.hashCode() ) + Integer.toHexString( result ) ).toUpperCase();
StringBuilder retVal = new StringBuilder();
retVal.append("_").append(referencedEntityName);
while( iterator.hasNext() ) {
Column c = (Column)iterator.next();
retVal.append("_");
retVal.append(c.getName());
}
return retVal.toString();
}
This returns automatically nice strings like "_Entity_attributeName_id", which will be used to create foreign keys like "fk_Entity_attributeName_id"! Never have to specify my names by hand again :)))
I had the same problem and because I couldn't find a way to do it ended up querying the database itself to get that information.
Running this query on SQL SERVER
select kcu.TABLE_NAME, kcu.CONSTRAINT_NAME, tc.CONSTRAINT_TYPE, kcu.COLUMN_NAME
from INFORMATION_SCHEMA.TABLE_CONSTRAINTS as tc
join INFORMATION_SCHEMA.KEY_COLUMN_USAGE as kcu
on kcu.CONSTRAINT_SCHEMA = tc.CONSTRAINT_SCHEMA
and kcu.CONSTRAINT_NAME = tc.CONSTRAINT_NAME
and kcu.TABLE_SCHEMA = tc.TABLE_SCHEMA
and kcu.TABLE_NAME = tc.TABLE_NAME
You will get tablename, constraint name (like FK1BCA4A883A97C498), type (like UNIQUE constraint) and column name.
That should be enought to return a meaningful error message.
I know is not great because you loose the db portability but apparently there is no way to do what you are asking for at the moment...

Categories

Resources