java.lang.IllegalArgumentException: expecting IdClass mapping - java

I have configured composite primary key for my entity Employee as follows
Employee.java:
#Entity
#Table(name="employee")
#Proxy(lazy=false)
#IdClass(EmployeeId.class)
public class Employee implements Serializable {
private static final long serialVersionUID = 1L;
private EmployeeId employeeId;
private Person person;
private Branch branch;
private boolean isActive;
public Employee() {
}
#EmbeddedId
#AttributeOverrides({
#AttributeOverride(name="person", column = #Column(name="person_id")),
#AttributeOverride(name="branch", column = #Column(name="branch_id"))})
public EmployeeId getEmployeeId() {
return employeeId;
}
public void setEmployeeId(EmployeeId employeeId) {
this.employeeId = employeeId;
}
#ManyToOne(fetch=FetchType.LAZY)
#JoinColumn(name="person_id")
public Person getPerson() {
return person;
}
public void setPerson(Person person) {
this.person = person;
}
#ManyToOne(fetch=FetchType.LAZY)
#JoinColumn(name="branch_id")
public Branch getBranch() {
return branch;
}
public void setBranch(Branch branch) {
this.branch = branch;
}
#Column(name="is_active")
public boolean getIsActive() {
return isActive;
}
public void setIsActive(boolean isActive) {
this.isActive = isActive;
}
}
EmployeeId.java:
#Embeddable
public class EmployeeId implements Serializable {
private static final long serialVersionUID = 1L;
private Person person;
private Branch branch;
public EmployeeId() {
}
public EmployeeId(Person argPerson, Branch argbranch) {
this.person = argPerson;
this.branch = argbranch;
}
#ManyToOne(fetch=FetchType.LAZY)
#JoinColumn(name="person_id", insertable=false, updatable=false)
public Person getPerson() {
return person;
}
public void setPerson(Person person) {
this.person = person;
}
#ManyToOne(fetch=FetchType.LAZY)
#JoinColumn(name="branch_id", insertable=false, updatable=false)
public Branch getBranch() {
return branch;
}
public void setBranch(Branch branch) {
this.branch = branch;
}
}
I created a SessionFactory bean using class org.springframework.orm.hibernate5.LocalSessionFactoryBean and mapped all hbm.xml as a MappingLocations.
My code throws the following error:
Caused by: java.lang.IllegalArgumentException: expecting IdClass mapping
at org.hibernate.metamodel.internal.AttributeFactory$3.resolveMember(AttributeFactory.java:971)
at org.hibernate.metamodel.internal.AttributeFactory$5.resolveMember(AttributeFactory.java:1029)
at org.hibernate.metamodel.internal.AttributeFactory.determineAttributeMetadata(AttributeFactory.java:451)
at org.hibernate.metamodel.internal.AttributeFactory.buildIdAttribute(AttributeFactory.java:128)
at org.hibernate.metamodel.internal.MetadataContext.buildIdClassAttributes(MetadataContext.java:337)
at org.hibernate.metamodel.internal.MetadataContext.applyIdMetadata(MetadataContext.java:269)
at org.hibernate.metamodel.internal.MetadataContext.wrapUp(MetadataContext.java:190)
at org.hibernate.metamodel.internal.MetamodelImpl.initialize(MetamodelImpl.java:219)
at org.hibernate.internal.SessionFactoryImpl.<init>(SessionFactoryImpl.java:296)
at org.hibernate.boot.internal.SessionFactoryBuilderImpl.build(SessionFactoryBuilderImpl.java:476)
at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:707)
at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:723)
at org.springframework.orm.hibernate5.LocalSessionFactoryBean.buildSessionFactory(LocalSessionFactoryBean.java:504)
at org.springframework.orm.hibernate5.LocalSessionFactoryBean.afterPropertiesSet(LocalSessionFactoryBean.java:488)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFac
How can I avoid this error? I am using spring-orm-4.3.1-RELEASE and hibernate-core-5.2.0.Final.
Update
I have created a sample project and I am getting the following error while running...
Caused by: org.hibernate.AnnotationException: Property of #IdClass not found in entity sample.domain.Employee: employee
Refer the code: https://www.dropbox.com/s/axr8l01iqh0qr29/idclass-using-hibernate5.tar.gz?dl=0
What I did wrong? Kindly provide your inputs here

Your situation corresponds to the chapter 2.4.1 Primary Keys Corresponding to Derived Identities of the JPA 2.1 Specification.
The identity of Employee is derived from identities of Person and Branch. You haven't shown the code of either of them, so I'll assume they have simple primary keys. In that relationship, Person and Branch are "parent entities" and Employee is a "dependant" entity.
The ID of Employee may be mapped using either IdClass or EmbeddedId, not both at the same time.
See chapter 2.4.1.1 Specification of Derived Identities.
If you want to use IdClass, then:
The names of the attributes of the id class and the Id attributes of the dependent entity class must correspond as follows:
The Id attribute in the entity class and the corresponding attribute in the id class must have the same name.
...
If an Id attribute in the entity is a many-to-one or one-to-one relationship to a parent entity, the corresponding attribute in the id class must be of (...) the type of the Id attribute of the parent entity.
So your classes would look like this (getters, setters, superfluous annotations etc. omitted)
#Entity
#IdClass(EmployeeId.class)
public class Employee {
#Id
#ManyToOne
private Person person;
#Id
#ManyToOne
private Branch branch;
}
public class EmployeeId {
private Long person; // Corresponds to the type of Person ID, name matches the name of Employee.person
private Long branch; // Corresponds to the type of Branch ID, name matches the name of Employee.branch
}
If you use EmbeddedId, then:
If the dependent entity uses an embedded id to represent its primary key, the attribute in the embedded id corresponding to the relationship attribute must be of the same type as the primary key of the parent entity and must be designated by the MapsId annotation applied to the relationship attribute. The value element of the MapsId annotation must be used to specify the name of the attribute within the embedded id to which the relationship attribute corresponds.
And the code would look like this:
#Entity
public class Employee {
#EmbeddedId
private EmployeeId id;
#ManyToOne
#MapsId("personId") // Corresponds to the name of EmployeeId.personId
private Person person;
#ManyToOne
#MapsId("branchId") // Corresponds to the name of EmployeeId.branchId
private Branch branch;
}
#Embeddable
public class EmployeeId {
private Long personId; // Corresponds to the type of Person ID
private Long branchId; // Corresponds to the type of Branch ID
}

A composite key mapping can be either done with an IdClass or an Embeddable. If you want to use an IdClass you have to annotate your fields in Employee with #Id.
#IdClass(EmployeeId.class)
class Person{
#Id
private Person person;
#Id
private Branch branch;
}
If you want to use an Embedded as a composite key please remove the #IdClass(EmployeeId.class) annotation from Person. You also don't need the person and branch field in your Person class because those are defined in your Embedded class.

Change to:
#Entity
#Table(name = "employee")
#Proxy(lazy = false)
#IdClass(EmployeeId.class)
public class Employee implements Serializable {
private static final long serialVersionUID = 1L;
private EmployeeId id;
private Person person;
private Branch branch;
private boolean isActive;
public Employee() {
}
#EmbeddedId
#AttributeOverrides({#AttributeOverride(name = "person", column = #Column(name = "person_id") ),
#AttributeOverride(name = "branch", column = #Column(name = "branch_id") )})
public EmployeeId getId() {
return id;
}
public void setId(EmployeeId id) {
this.id = id;
}
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "person_id")
public Person getPerson() {
return person;
}
public void setPerson(Person person) {
this.person = person;
}
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "branch_id")
public Branch getBranch() {
return branch;
}
public void setBranch(Branch branch) {
this.branch = branch;
}
#Column(name = "is_active")
public boolean getIsActive() {
return isActive;
}
public void setIsActive(boolean isActive) {
this.isActive = isActive;
}
}

The IdClass shouldnt be defined as Embeddable -
#Entity
#Table(name="employee")
#IdClass(EmployeeId.class)
public class Employee implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#ManyToOne
private Person person;
#Id
#ManyToOne
private Branch branch;
private boolean isActive;
public Employee() { }
//....
}
And -
public class EmployeeId implements Serializable {
private static final long serialVersionUID = 1L;
private Person person;
private Branch branch;
public EmployeeId() {}
public EmployeeId(Person argPerson, Branch argbranch) {
this.person = argPerson;
this.branch = argbranch;
}
}
Read your comment - Can I make a suggestion that you map Employee to person_id and branch_id, and not the JPA objects Person and Branch? This will let us test if your hbm config is correct. Id also suggest posting your hbm config as I think there is information missing from this problem
So the table will be similar to -
#Entity
#Table(name="employee")
#IdClass(EmployeeId.class)
public class Employee implements Serializable {
private static final long serialVersionUID = 1L;
#Id
private Long personId;
#Id
private Long branchId;
private boolean isActive;
public Employee() { }
//....
}
And -
And -
public class EmployeeId implements Serializable {
private static final long serialVersionUID = 1L;
private Long personId;
private Long branchId;
public EmployeeId() {}
public EmployeeId(Person argPerson, Branch argbranch) {
this.person = argPerson;
this.branch = argbranch;
}
}

This link could help you
JPA - EmbeddedId with #ManytoOne
Relationship mappings defined within an embedded id class are not supported.Then you need to change the embeddedId class like this
#Embeddable
public class EmployeeId implements Serializable {
private static final long serialVersionUID = 1L;
private Long personId;
private Long branchId;
public EmployeeId() {
}
public EmployeeId(Long argPerson, Long argbranch) {
this.personId = argPerson;
this.branchId = argbranch;
}
#Column(name = "person_id")
public Long getPersonId() {
return personId;
}
public void setPersonId(Long personId) {
this.personId = personId;
}
#Column(name = "branch_id")
public Long getBranchId() {
return branchId;
}
public void setBranchId(Long branchId) {
this.branchId = branchId;
}
}

JPA Composite Primary Key
Specifies a composite primary key class that is mapped to multiple fields or properties of the entity.
The names of the fields or properties in the primary key class and the
primary key fields or properties of the entity must correspond and
their types must be the same.
The answer is in here. read description for you. enter link description here
(Sample code)
#Entity
#Table(name = "EMP_PROJECT")
#IdClass(ProjectAssignmentId.class)
public class ProjectAssignment {
#Id
#Column(name = "EMP_ID", insertable = false, updatable = false)
private int empId;
#Id
#Column(name = "PROJECT_ID", insertable = false, updatable = false)
private int projectId;
#ManyToOne
#JoinColumn(name = "EMP_ID")
Professor employee;
#ManyToOne
#JoinColumn(name = "PROJECT_ID")
Project project;
....
}
public class ProjectAssignmentId implements Serializable {
private int empId;
private int projectId;
...
}

Mention #IdClass annotation with the class which holds the ID.
Check the answer at this post

Related

Hibernate one-to-many relationship java.sql.SQLIntegrityConstraintViolationException: Column 'person_id' cannot be null

I'm new to hibernate, learn doc save persistent object
followed hibernate doc this is person and phone relationship one-to-many
#Entity
#Table(name = "phone")
public class Phone {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
#Column(name = "number")
private String number;
#ManyToOne(fetch = FetchType.LAZY)
private Person person;
//omit setter and getter
}
#Entity
#Table(name = "person")
public class Person {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
private String username;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "person")
private List<Phone> phones = new ArrayList<>();
//omit getter and setter
}
I'm persistent person and add one phone the error be throw
#Test
public void say() {
Person person = new Person();
person.setUsername("aaaa");
Phone phone = new Phone();
phone.setNumber("111");
person.getPhones().add(phone);
personService.save(person);
}
this is Dao persistent
public class PersonDaoImpl implements PersonDao {
#PersistenceContext
private EntityManager entityManager;
#Override
public void save(Person person) {
entityManager.persist(person);
}
Update service code, service just save person
#Service(value = "personService")
public class PersonServiceImpl implements PersonService {
#Autowired
private PersonDao personDao;
#Transactional
#Override
public void save(Person person) {
personDao.save(person);
}
}
error info:
23:35:47.059 [main] DEBUG org.hibernate.engine.spi.ActionQueue - Executing identity-insert immediately
23:35:47.062 [main] DEBUG org.hibernate.SQL -
insert
into
phone
(number, person_id)
values
(?, ?)
23:35:47.297 [main] DEBUG org.hibernate.engine.jdbc.spi.SqlExceptionHelper - could not execute statement [n/a]
java.sql.SQLIntegrityConstraintViolationException: Column 'person_id' cannot be null
Add the #GeneratedValue annotation to specify that the primary key for both entities will be populated outside of your code.
#Entity
#Table(name = "phone")
public class Phone {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private long id;
#Column(name = "number")
private String number;
#JoinColumn("person_id")
#ManyToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
private Person person;
//omit setter and getter
}
public class Person {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private long id;
private String username;
#OneToMany(mappedBy = "person")
private List<Phone> phones = new ArrayList<>();
//omit getter and setter
}
Additionally, you need to persist the Person object instead of the Phone object because there is no cascade configured from Phone to Person. If you can't do that, switch the CascadeType on Person to none and put the cascade on the Phone as shown above.
You should also add a #JoinColumn annotation on the Phone entity so hibernate is aware of the foreign key column.
You Missed something. You can try with this.
Person Entity
#Entity
public class Person {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
private String username;
#OneToMany(mappedBy = "person")
private List<Phone> phones = new ArrayList<>();
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public List<Phone> getPhones() {
return phones;
}
public void setPhones(List<Phone> phones) {
this.phones = phones;
}
//omit getter and setter
}
Phone Entity
#Entity
#Table(name = "phone")
public class Phone {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
#Column(name = "number")
private String number;
#ManyToOne(cascade = CascadeType.PERSIST)
private Person person;
public String getNumber() {
return number;
}
public void setNumber(String number) {
this.number = number;
}
public Person getPerson() {
return person;
}
public void setPerson(Person person) {
this.person = person;
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
//ommit setter and getter
}
Phone Dao
public interface PhoneDao {
public Phone save(Phone phone);
}
PhoneDaoImpl
#Repository
public class PhoneDaoImpl implements PhoneDao {
#PersistenceContext
private EntityManager entityManager;
#Override
public Phone save(Phone phone) {
return entityManager.merge(phone);
}
}
PersonDaoImpl
#Repository
public class PersonDaoImpl implements PersonDao{
#PersistenceContext
private EntityManager entityManager;
#Override
public Person save(Person person) {
return entityManager.merge(person);
}
}
Test Method
#Test
#Transactional
#Commit
public void say()
{
Phone phone = new Phone();
phone.setNumber("jghjkhk");
Person person = new Person();
person.setUsername("7576");
phone.setPerson(person);
Phone pers = phoneDao.save(phone);
Assert.assertNotNull(pers);
}
Try now. It will work.
I think that you need to set the value of the person->id and then also use an getter method to pass the id to your phone object instead of passing the person object
Normally people have hibernate set the id of an entity automatically with a surrogate key.
public class Person {
#Id #GeneratedValue // should pick an appropriate strategy here
private long id;
Since you don't have that you must either add it or set it yourself.
Person p = new Person();
p.setId(1); // hopefully unique
The same goes for phone.
As you are not having any generation type on your #Id and id is the primary key which can not be null so either you have to set value of id or have #GeneratedValue annotation on your id field and set strategy either as Auto or Identity.
You can also have your own sequence generation.
Also, you need to do same for the Phone class.

HIbernate one to many / Many to one using #Embeddable Annotation

When I'm trying to save the Customer Object the exception states that it cannot insert NULL into ()...
I believe the exception is caused due to the #Embeddable Annotation used
But when I am explicit setting the value of customer_id it works fine but the value is not saved as foreign key in Customerphone table.
#Entity
#Table(name = "CUSTOMER")
public class Customer implements java.io.Serializable {
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, mappedBy = "Customer")
public Set<CustomerPhone> getCustomerPhones() {
return this.CustomerPhones;
}
}
#Entity
#Table(name="CUSTOMER_PHONE")
public class CustomerPhone implements java.io.Serializable {
private CustomerPhoneId id;
#EmbeddedId
#AttributeOverrides( {
#AttributeOverride(name="customerId", column=#Column(name="CUSTOMER_ID", nullable=false, precision=22, scale=0) ),
#AttributeOverride(name="phoneTypeCd", column=#Column(name="PHONE_TYPE_CD", nullable=false, length=5) ) } )
public CustomerPhoneId getId() {
return this.id;
}
public void setId(CustomerPhoneId id) {
this.id = id;
}
#ManyToOne(fetch=FetchType.LAZY)
#JoinColumn(name="CUSTOMER_ID", nullable=false, insertable=false, updatable=false)
public Customer getCustomer() {
return this.stpCustomer;
}
}
#Embeddable
public class CustomerPhoneId implements java.io.Serializable {
private BigDecimal customerId;
#Column(name="CUSTOMER_ID", nullable=false, precision=22, scale=0)
public BigDecimal getCustomerId() {
return this.customerId;
}
}
Your ID does not have a Generation schema, like #GeneratedValue(strategy=GenerationType.AUTO).
In these cases, you need to set an ID manually, in your case, you need to set customerId manually before persisting.

One-to-One association with composite ID of owner Entity

I have a to make a one-to-one association between two Entities, but one of them must have two #Id. One is PRI another one is MUL. How must i declare composite id, and how do i need to map the classes?
#Entity
#Table(name = "PERSONS")
public class Person implements Serializable{
private static final long serialVersionUID = -3451407520028311143L;
#Id
#Column(name = "ID")
private Integer id;
#Column(name = "ADDRESS_ID")
private Integer addressId;
#Column(name ="NAME")
private String name;
#OneToOne(mappedBy= "person", cascade= CascadeType.ALL)
private Address address;
...
}
second class is mapped via #IdClass annotation
#Entity
#Table ( name = "ADDRESS" )
#IdClass(AddressKeys.class)
public class Address implements Serializable {
#Id
#Column ( name = "ID")
private Integer id;
#Id
#Column ( name = "PERSON_ID")
private Integer idPerson;
#Column ( name = "CITY" )
private String city;
#OneToOne(cascade= CascadeType.ALL)
#JoinColumn(name="PERSON_ID")
private Person person;
...
}
and the id class
class AddressKeys implements Serializable{
private Integer id;
private Integer idPerson;
//getters and setters
#Override
public int hashCode() {
...
return result;
}
#Override
public boolean equals(Object obj) {
...
}
}
So when i try to create and save a record i have a next error
Could not open sessionRepeated column in mapping for entity:
hibernateMappedModels.base1.mappedClasses.oneToOne.Address column:
PERSON_ID (should be mapped with insert="false" update="false")
java.lang.NullPointerException at
hibernateMappedModels.base1.Main.run(Main.java:45) at
hibernateMappedModels.base1.Main.main(Main.java:24
I tryed to make an Id fields unInsertable and unUpdatable, and it was working, but i need them to be insertable and updatable; Is there any possibility to do it?
I am confused by your mappings and not sure what is required other then the simple mappings below: if I am missing something then you will need to expand on your question. You are getting the error as you have mapped the column twice - once via the one-to-one and once as a simple property. Additionally, I am not sure why you require a composite key on address.
#Entity
#Table(name = "PERSONS")
public class Person implements Serializable{
private static final long serialVersionUID = -3451407520028311143L;
#Id
#Column(name = "ID")
private Integer id;
#Column(name ="NAME")
private String name;
#OneToOne(mappedBy= "person", cascade= CascadeType.ALL)
private Address address;
}
#Entity
#Table ( name = "ADDRESS" )
public class Address implements Serializable {
#Id
#Column ( name = "ID")
private Integer id;
#Column ( name = "CITY" )
private String city;
#OneToOne(cascade= CascadeType.ALL)
#JoinColumn(name="PERSON_ID")
private Person person;
}

OpenJPA OneToMany and composite key in parent and child table

I have tables with composited primary key.
Server(key=ServerId)
ServerId|Name
1 |server1
2 |server2
ParentObj(key=ServerId+Code)
ServerId|Code |Title
1 |code1|value1
1 |code2|value2
2 |code1|Value2b
ChildObj(key=ServerId+Code+Name)
ServerId|Code |Name |Value
1 |code1|prop1|val1
1 |code1|prop2|val2
1 |code2|prop1|val1b
2 |code1|prop3|val3
This is Java beans I have.
#Entity #Table(name="ParentObj") #Access(AccessType.FIELD)
#IdClass(value=ParentObj.PK.class)
#XmlAccessorType(XmlAccessType.PUBLIC_MEMBER)
#XmlRootElement
public class ParentObj {
#Id private long serverId;
#Id private String code;
private String title;
public long getServerId() { return serverId; }
public String getCode() { return code; }
public String getTitle() { return title; }
public static class PK implements Serializable {
private static final long serialVersionUID = 1L;
private long serverId;
private String code;
public long getServerId() { return serverId; }
public void setServerId(long id) { serverId=id; }
public String getCode() { return code; }
public void setCode(String code) { this.code=code; }
}
}
#Entity #Table(name="ChildObj") #Access(AccessType.FIELD)
#IdClass(value=ChildObj.PK.class)
#XmlAccessorType(XmlAccessType.PUBLIC_MEMBER)
#XmlRootElement
public class ChildObj {
#Id private long serverId;
#Id private String code;
#Id private String name;
private String value;
// public getter+setters for each field
public static class PK implements Serializable {
private static final long serialVersionUID = 1L;
private long serverId;
private String code;
private String name;
public long getServerId() { return serverId; }
public void setServerId(long id) { serverId=id; }
public String getCode() { return code; }
public void setCode(String code) { this.code=code; }
public String getName() { return name; }
public void setName(String name) { this.name=name; }
}
}
I have been trying "everything" to create OneToMany mapping(ParentObj->ChildObj) but nothing seem to work. I don't need ManyToOne(ParentObj<-ChildObj) link but that's ok if one must be defined.
This is a legacy database so I cannot insert an auto_increment identity column or create extra join table between parent and childs.
This annotation is conceptually what I want but multiple join columns is not accepted by OpenJPA2.x library.
// from parent to zero or more childs
#OneToMany(fetch=FetchType.LAZY)
#JoinColumns({
#JoinColumn(name="server_id", referencedColumnName="server_id"),
#JoinColumn(name="code", referencedColumnName="code")
})
private List<ChildObj> properties;
Edit, answer
OneToMany, ManyToOne and EmbeddedId annotations works. I have only tried reading existing rows but its fine for now. Later I try update+insert+delete tasks.
public class ParentObj {
#EmbeddedId ParentObj.PK pk;
#OneToMany(fetch=FetchType.LAZY, cascade=CascadeType.ALL, mappedBy="parent", orphanRemoval=true)
private List<ChildObj> childs;
public PK getPK() { return pk; }
public void setPK(PK pk) { this.pk=pk; }
public List<ChildObj> getChilds() { return childs; }
...
#Embeddable #Access(AccessType.FIELD)
public static class PK implements Serializable {
private static final long serialVersionUID = 1L;
#Column(nullable=false) private long serverId;
#Column(nullable=false) private String code;
..getters+setters+hashCode+equals functions
}
}
public class ChildObj {
#EmbeddedId ChildObj.PK pk;
#ManyToOne(fetch=FetchType.LAZY, cascade=CascadeType.PERSIST, optional=false)
#JoinColumns({
#JoinColumn(name="serverid", referencedColumnName="serverid", nullable=false),
#JoinColumn(name="code", referencedColumnName="code", nullable=false)
})
private ParentObj parent;
public PK getPK() { return pk; }
public void setPK(PK pk) { this.pk=pk; }
public long getServerId() { return pk.getServerId(); }
public String getCode() { return pk.getCode(); }
public String getName() { return pk.getName(); }
...
#Embeddable #Access(AccessType.FIELD)
public static class PK implements Serializable {
private static final long serialVersionUID = 1L;
#Column(nullable=false) private long serverId;
#Column(nullable=false) private String code;
#Column(nullable=false) private String name;
..getters+setters+hashCode+equals functions
}
}
The easiest way to do this is to create an association from ChildObj to ParentObj similar to the following:
#ManyToOne(fetch = FetchType.LAZY, optional = true)
#JoinColumns({
#JoinColumn(name = "serverId", referencedColumnName = "serverId"),
#JoinColumn(name = "code", referencedColumnName = "code")})
private ParentObj parentObj;
and then define the #OneToMany association in ParentObj like this:
#OneToMany(mappedBy = "parentObj", fetch=FetchType.LAZY)
private List<ChildObj> children;
I would also recommend that you define your composite keys as #Embeddable classes, used as #EmbeddedId references in the Entities. These embeddable PK classes should be separate classes (not inner classes), as you will use them separately to query the related Entities, and serialisation of inner classes can cause problems

JPA, Eclipselink - Relations are deleted on new transaction

I'm building a small eclipse rcp with a little bit of JPA. Now something strange happens:
I create some TopCategories with some SubCategories, this works as intended. The inserts are printed in the log. I close my application and now the problem raises up:
The Categories have a relation to books
Book.java
#Entity
public class Book implements Serializable, PropertyChangeListener {
private static final long serialVersionUID = 4646743297687986216L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
private String name;
private boolean active = true;
#Temporal(TemporalType.TIMESTAMP)
private Date updated;
#Lob
private Set<Group> allowedGroups;
#Column(columnDefinition = "TEXT")
private String text;
private BookType type;
#ManyToOne
private TopCategory topCategory;
#ManyToOne
private SubCategory subCategory;
#OneToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
private BookAttachment attachment;
#Transient
private PropertyChangeSupport propertyChangeSupport = new PropertyChangeSupport(this);
// ordinary getter/setter
#PrePersist
#PreUpdate
private void updateUpdated() {
this.updated = new Date();
}
}
After restart and querying Book with this select b from Book all SubCategories which aren't used getting deleted. If a SubCategory has a relation to Book it stays in my DB. Why this occures?
Category.java
#MappedSuperclass
public class Category implements Serializable {
private static final long serialVersionUID = 6091963773161164543L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
private String name;
#Temporal(TemporalType.TIMESTAMP)
private Date updated;
#Enumerated(EnumType.STRING)
private CategoryType type;
#Transient
private List<Snippet> snippets = new LinkedList<Snippet>();
// ordinary getter/setter
#PrePersist
#PreUpdate
public void updateUpdated() {
this.updated = new Date();
}
}
TopCategory.java
#Entity
public class TopCategory extends Category {
#OneToMany(cascade = CascadeType.ALL)
private List<SubCategory> subCategories;
public TopCategory() {
setName("");
setSubCategories(new ArrayList<SubCategory>());
}
public List<SubCategory> getSubCategories() {
return subCategories;
}
public void setSubCategories(List<SubCategory> subCategories) {
this.subCategories = subCategories;
}
#Override
public void setType(CategoryType type) {
super.setType(CategoryType.topCategory);
}
SubCategory.java
#Entity
public class SubCategory extends Category {
#ManyToOne(cascade = CascadeType.ALL)
private TopCategory topCategory;
public TopCategory getTopCategory() {
return topCategory;
}
public void setTopCategory(TopCategory topCategory) {
this.topCategory = topCategory;
}
#Override
public void setType(CategoryType type) {
super.setType(CategoryType.subCategory);
}
}
I'm using Eclipselink 2.1.1.
Regards
Alright - I've found the problem: One of the result lists from my model is modified from a contentprovider - this is were not used SubCategories are removed, in case that the entity objects weren't detached, it caused the entitymanger to update.

Categories