I have an issue, what I am close to solution or better said I come closer how to avoid the issue EntityExistsException: A different object with the same identifier value BUT without understanding what is really the problem. To keep code short and concentrated I did a small application to simulate the problem in my project.
First I have a CrudRepository what stays always the same:
public interface EntityARepository extends CrudRepository<EntityA, Long> {}
First I have to entities, one of them has a relation to the other:
#Entity
#EqualsAndHashCode(of={"name"})
#ToString(of={"name"})
#XmlRootElement
public class EntityA {
#Id
#GeneratedValue
#Setter
private Long id;
#Setter
#Column(nullable=false, unique=true)
private String name;
#Setter
#ManyToOne(fetch=EAGER, cascade={PERSIST, MERGE})
private EntityB entityB;
}
#ToString(of = { "name" })
#EqualsAndHashCode(of = { "name" })
#Entity
class EntityB {
#Id
// #GeneratedValue => produces issue!
#Setter
private Long id;
#Setter
#XmlAttribute
#Column(nullable=false, unique=true)
private String name;
}
Then I generate data and try to save them:
#Component
public class DatabaseInitializer implements InitializingBean {
#Autowired EntityARepository repository;
#Override
public void afterPropertiesSet() throws Exception {
final Set<EntityA> aEntities = createAEntities();
repository.save(aEntities);
}
private Set<EntityA> createAEntities() throws Exception {
Set<EntityA> aEntities = new HashSet<>();
aEntities.add(getFirstEntityA());
aEntities.add(getSecondEntityA());
return aEntities;
}
private EntityA getFirstEntityA(){
EntityA a = new EntityA();
// a.setId(1L);
a.setName("a-1");
a.setEntityB(getFirstEntityB());
return a;
}
private EntityA getSecondEntityA(){
EntityA a = new EntityA();
// a.setId(2L);
a.setName("a-2");
a.setEntityB(getFirstEntityB());
return a;
}
private EntityB getFirstEntityB() {
EntityB b = new EntityB();
b.setId(1l);
b.setName("b-1");
return b;
}
}
With this constallation I become a EntityExistsException:
Caused by: javax.persistence.EntityExistsException: A different object with the same identifier value was already associated with the session : [com.example.EntityB#1]
When firstEntityA gets saved, it also saves firstEntityB (because of cascade). So after that Session contains already entity of class EntityB with id 1.
When secondEntityA gets persisted, it also calls save on firstEntityB and then exception gets thrown.
The reason is that you can't call save on an object with id that is already present in the Session.
There are many ways to fix it. For example you can call merge instead of save.
repository.merge(aEntities);
Another way to fix this would to make both instances of EntityA have a referene to the same EntityB object:
private Set<EntityA> createAEntities() throws Exception {
Set<EntityA> aEntities = new HashSet<>();
EntityB entityB = getFirstEntityB();
aEntities.add(getFirstEntityA(entityB));
aEntities.add(getSecondEntityA(entityB));
return aEntities;
}
private EntityA getFirstEntityA(EntityB entityB){
EntityA a = new EntityA();
// a.setId(1L);
a.setName("a-1");
a.setEntityB(entityB);
return a;
}
private EntityA getSecondEntityA(EntityB entityB){
EntityA a = new EntityA();
// a.setId(2L);
a.setName("a-2");
a.setEntityB(entityB);
return a;
}
private EntityB getFirstEntityB() {
EntityB b = new EntityB();
b.setId(1l);
b.setName("b-1");
return b;
}
Related
We have tables,
'Lin_Code_Groups' with fields,
Project_ID (PK),
CG_ID(PK),
CG_Name
Corresponding entity class,
public class Lin_Code_Groups implements Serializable {
#EmbeddedId
private LinCodeGroupPK pk;
private String CG_name;
#Embeddable
public static class LinCodeGroupPK implements Serializable {
private Integer Project_ID;
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer CG_ID;
}
#OneToMany(mappedBy = "lin_Code_Groups")
private List<Lin_CG_Params> lin_CG_Params;
}
table Lin_CG_Params with fields,
Project_ID (PK)..FK to Lin_Code_Groups,
CG_ID(PK)...FK to Lin_Code_Groups,
Param_name(PK),
Param_value
Corresponding entity class,
public class Lin_CG_Params implements Serializable {
#EmbeddedId
private LinCodeGroupParamPK pk;
private String Param_value;
#Embeddable
public static class LinCodeGroupParamPK implements Serializable {
private String Param_name;
private LinCodeGroupPK linCodeGroupPK;
}
#MapsId("linCodeGroupPK")
#ManyToOne
#JoinColumns( {
#JoinColumn(name = "Project_ID",referencedColumnName= "Project_ID"),
#JoinColumn(name = "CG_ID",referencedColumnName= "CG_ID")
})
private Lin_Code_Groups lin_Code_Groups;
}
in controller class, i am using JPA's .Save method to save the data in to the tables.
#PostMapping(value = {"/hello"}, consumes = "application/json", produces = "application/json")
public ResponseEntity<Object> saveNewCodeGroupsDetails(#RequestBody Lin_Code_Groups objLin_Code_Groups ) {
respository.save(objLin_Code_Groups);
}
but getting an error 'Unable to find Lin_CG_Params with id Lin_CG_Params.LinCodeGroupParamPK'
Can anyone is please help ?
I am making an application based on JPA/EclipseLink, I implemented a soft delete functionality using #AdditionalCriteria in the root class of the hierarchy of entities (all entities inherit from this).
My problem is that now, I need to create a special entity that contains multiple relationships with other entities; and I need recover all relationed entities, including soft deleted ones. It is possible disabled #AdditionalCriteria only in the relations of this special entity with EclipseLink? If not, what is the best option to do this? My code looks like the following:
////Example of my top entity class (all others inherit from this)
#AdditionalCriteria("this.deleted = false")
public abstract class TopHierarchyClass {
···
#Column(name = "deleted")
protected boolean deleted = false;
···
}
//Example of entity that needs recover all relationed entities including soft deleted
#Entity
#Table(name = "special_entities")
public class SpecialEntity extends EntityBase {
···
#JoinColumn(name = "iditem", referencedColumnName = "id")
#ManyToOne(fetch = FetchType.LAZY)
private Item item;
···
}
#Entity
#Table(name = "items")
public class Item extends EntityBase {
···
}
Thanks in advance
Create a new entity for the same table without the #AdditionalCriteria. This way you can retrieve all records from that table without applying the additional filter.
For example:
public interface Person {
Long getId();
void setId(Long id);
Date getDeletedAt();
void setDeletedAt(Date deletedAt);
}
#MappedSupperclass
public class PersonEntity implements Person {
#Id
private Long id;
private Date deletedAt;
public Long getId() { return id; }
public void setId(Long id) { this.id = id; }
public Date getDeletedAt() { return deletedAt; }
public void setDeletedAt(Date deletedAt) { this.deletedAt = deletedAt; }
}
#Entity
#Table(name = "people")
#AdditionalCriteria("this.deletedAt is null")
public class ActivePersonEntity extends PersonEntity {
}
#Entity
#Table(name = "people")
public class RawPersonEntity extends PersonEntity {
}
public class PeopleRepository {
#PersistenceContext
private EntityManager em;
public List<Person> all() {
return all(false);
}
public List<Person> all(boolean includeSoftDeleted) {
if (!includeSoftDeleted) {
return em.createQuery("select p from ActivePersonEntity p", ActivePersonEntity.class).getResultList();
} else {
return em.createQuery("select p from RawPersonEntity p", RawPersonEntity.class).getResultList();
}
}
}
Also, if your #AdditionalCriteria is in a super class, you may override it by declaring a new empty #AdditionalCriteria in a sub class:
You can define additional criteria on entities or mapped superclass.
When specified at the mapped superclass level, the additional criteria
definition applies to all inheriting entities, unless those entities
define their own additional criteria, in which case those defined for
the mapped superclass are ignored.
#AdditionalCriteria doc
I have have two classes that inherit from an abstract class and have a parent-children relation.
So I use annotation OneToMany and ManyToOne but the parent entity in child class is always null.
Can Someone help me please, I have spend several hours to googling and test many conf without success.
These are code from my classes :
public #Table(name="flowentity") #Entity abstract class FlowEntity {
final static Logger log = LoggerFactory.getLogger(FlowEntity.class);
//Globals informations concerning the flow state
private #Id #GeneratedValue(strategy = GenerationType.IDENTITY) Integer flowId = 0;
private String flowName;
private #OneToMany(fetch=FetchType.EAGER, cascade=CascadeType.ALL)
Set<PeopleEntity> actorSet = new HashSet<>();
//Global parameters for most of flows
//Organizational parameters
private #OneToOne(fetch=FetchType.EAGER, cascade=CascadeType.ALL)
#JoinColumn(name="organisationalEntity_Id")
OrganisationalEntity organisationalEntity;
...
public #Table(name="ams_newCPEntity") #Entity class NewMultiCPEntity extends FlowEntity {
private #OneToMany(targetEntity=NewCPEntity.class, fetch=FetchType.EAGER, cascade=CascadeType.ALL,mappedBy="parent")
Set<NewCPEntity> cpList = new HashSet<NewCPEntity>();
//Constructor
public NewMultiCPEntity(){
setFlowName(EnumFlow.N_CP_M.getFlowAcronym());
}
...
public #Table(name="ams_newCPEntity") #Entity class NewCPEntity extends FlowEntity {
final static Logger log = LoggerFactory.getLogger(NewCPEntity.class);
private boolean formNCPValidated;
private #ManyToOne #JoinColumn(name="parent_Id", nullable=false)
NewMultiCPEntity parent;
public NewCPEntity(){
log.debug("Instanciation of a new CP");
setFlowName(EnumFlow.N_CP.getFlowAcronym());
}
public #Override OrganisationalEntity getOrganisationalEntity(){
return parent.getOrganisationalEntity();
}
...
If I don't add the #JoinColumn annotation, JPA create an association table but is not able to retrieve the parent whereas the association can be done directly by requesting in database.
Thankyou very much to help.
Regards,
Thank you Chris for your comment, you are right, I forget to change the name of the table. I don't think it was the problem because the inheritance mapping is in one table flowentity with a DTYPE discriminator column.
Finally I resolve my problem by setting parent attributs when adding a new child like this :
public #Table #Entity class NewMultiCPEntity extends FlowEntity {
private #OneToMany(targetEntity=NewCPEntity.class, fetch=FetchType.EAGER, cascade=CascadeType.ALL)
List<NewCPEntity> cpList = new ArrayList<>();
//Constructor
public NewMultiCPEntity(){
setOrganisationalEntity(new OrganisationalEntity());
setFlowName(EnumFlow.N_CP_M.getFlowAcronym());
}
public List<NewCPEntity> getNCPList(){
if(cpList == null){
cpList = new ArrayList<>();
}
if(cpList.isEmpty()){
addCPEntity(new NewCPEntity());
}
return Collections.unmodifiableList(cpList);}
public boolean removeCPEntity(NewCPEntity entity){
return cpList.remove(entity);
}
public boolean addCPEntity(NewCPEntity entity){
entity.setParent(this);
entity.setOrganisationalEntity(this.getOrganisationalEntity());
return cpList.add(entity);
}
And I remove the override of getOrganizationalEntity in the child :
public #Table #Entity class NewCPEntity extends FlowEntity {
final static Logger log = LoggerFactory.getLogger(NewCPEntity.class);
private #ManyToOne(targetEntity=NewMultiCPEntity.class,cascade=CascadeType.ALL)
NewMultiCPEntity parent;
public NewCPEntity(){
log.debug("Instanciation of a new CP");
setFlowName(EnumFlow.N_CP.getFlowAcronym());
}
public NewMultiCPEntity getParent() {
return parent;
}
public void setParent(NewMultiCPEntity parent){
this.parent = parent;
}
Regards,
I have a situation where lazy-loading is not occurring when I want it to in a one-to-many.
Obviously, I am new to this. Why won't the SongComposers get fetched when I call s.getSongComposers()?
Here is my DAO class:
public class SongDAOImpl implements SongDAO {
#PersistenceContext
private EntityManager em;
#Override
#SuppressWarnings(value = { "unchecked" })
public List<Song> getAllSongsOnAlbum(int albumID) {
Query q = em.createQuery("SELECT s FROM Song s WHERE s.album.albumID = :albumid ORDER BY s.songName");
q.setParameter("albumid", albumID);
List<Song> list = (List<Song>) q.getResultList();
for (Song s: list) {
s.getSongComposers();
}
return list;
}
}
Here is my Song entity:
#Entity
#Table(name="songs")
public class Song {
#Id
#GeneratedValue
#Column(name="SongID")
private int songID;
#Column(name="SongName")
private String songName;
#Column(name="Length")
private int length;
#ManyToOne
#JoinColumn(name="AlbumID", referencedColumnName="AlbumID")
private Album album;
#OneToMany(mappedBy="song")
private List<SongComposer> songComposers;
.... all getters/setters ......
}
Here is my SongComposer entity:
#Entity
#Table(name="song_composer")
public class SongComposer {
#Id
#GeneratedValue
#Column(name="SongComposerID")
private int songComposerID;
#ManyToOne
#JoinColumn(name="SongID", referencedColumnName="SongID")
private Song song;
#ManyToOne
#JoinColumn(name="ComposerID", referencedColumnName="ComposerID")
private Composer composer;
..... getters/setters .......
}
You have to do it explicitly. Try this query:
SELECT DISTINCT s FROM Song s JOIN FETCH s.songComposers WHERE s.album.albumID = :albumid ORDER BY s.songName"
Got GlassFish v3. I have an one-to-many entity. The problem is, that EclipseLink seems to ignore the fetch EAGER mode.
Here is my entities.
#Entity
public class Person implements Serializable
{
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String name;
#OneToMany(mappedBy = "person", fetch = FetchType.EAGER)
private List<Hobby> hobbies;
// getter and setter
}
A 1:n relationship
#Entity
public class Hobby
{
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String name;
#ManyToOne
#JoinColumn
private Person person;
// getter and setter
}
And the bean
#javax.ejb.Remote
public interface Testing
{
public void addTestData();
public List<Person> getTestData();
}
#javax.ejb.Stateless
public class TestingBean implements Testing
{
#javax.persistence.PersistenceContext
private EntityManager entityManager;
public void addTestData()
{
Person p = new Person();
p.setName("JOE");
entityManager.persist(p);
Hobby h1 = new Hobby();
h1.setName("h1");
h1.setPerson(p);
entityManager.persist(h1);
}
public List<Person> getTestData()
{
TypedQuery<Person> gridQuery = entityManager.createQuery("SELECT e FROM Person e", Person.class);
return gridQuery.getResultList();
}
}
EDIT Client:
InitialContext context = new InitialContext();
Testing test = (Testing)context.lookup("java:global/dst2_1/TestingBean");
test.addTestData();
for(Person p: test.getTestData()) {
System.out.println(p.getName());
for(Hobby b : p.getHobbys()) {
System.out.println(b.getName());
}
}
context.close();
Using MySQL - Storing the data works. But if I fetch the data only the person is returned - not hobbies. Coudld you tell me what is wrong in my code?
EDIT sorry have tried so many things ... The code shown as above produces:
Exception Description: An attempt was made to traverse a
relationship using indirection that had a null Session. This often
occurs when a n entity with an uninstantiated LAZY relationship is
serialized and that lazy relationship is traversed after
serialization. To avoid this issue, ins tantiate the LAZY
relationship prior to serialization.
But the Person is returned correctly. Why does it specify LAZY while I am using EAGER?
You code looks correct. I can't see any way that the EAGER could be ignored.
Are you sure you get the error with this attribute, not another one?
Also ensure you recompile and deployed your code correctly. You most like have an old version deployed.
Make the eager object Serializable