I have an application with a composite table holding one extra column. It all works fine, until we add Hibernate Envers (#Audited).
org.hibernate.MappingException: Unable to read the mapped by attribute for responseDomainCodes in no.pack.response.ResponseDomainCode
I am happy to provide more detailed information if necessary, however, at this time I am not sure what would be relevant.
The tables look like this, and is a pretty standard composite key table, with one extra column.
Database schema
+-----------+---------+
| CODE | TYPE |
+-----------+---------+
| category | VARCHAR |
| code | VARCHAR |
+-----------+---------+
|
|
+----------------------+---------+
| RESPONSE_DOMAIN_CODE | TYPE |
+----------------------+---------+
| response_domain_id | KEY |
| code_id | KEY |
| rank | VARCHAR |
+----------------------+---------+
|
|
+--------------------+------+
| RESPONSE_DOMAIN | TYPE |
+--------------------+------+
| response_domain_id | PK |
| response_kind_id | FK |
+--------------------+------+
ResponseDomain.java
#Entity
#Table(name = "responseDomain")
public class ResponseDomain implements Serializable {
#Id
#Column(name = "responseDomain_id")
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#ManyToOne
#JoinColumn(name = "respons_kind_id")
private ResponseKind responseKind;
#OneToMany(fetch = FetchType.EAGER, mappedBy = "pk.responseDomain", cascade = CascadeType.ALL)
private Set<ResponseDomainCode> responseDomainCodes = new HashSet<>();
//Omitted rest.
}
Code.java
#Entity
#Table(name = "code")
public class Code implements Serializable {
#Id
#Column(name = "code_id")
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String category;
private String code;
#OneToMany(fetch = FetchType.EAGER, mappedBy = "pk.code", cascade = CascadeType.ALL)
private Set<ResponseDomainCode> responseDomainCodes = new HashSet<>();
//Omitted rest
}
ResponseDomainCode.java
#Entity
#Table(name = "responseDomain_code")
#AssociationOverrides(value = {
#AssociationOverride(name = "pk.responseDomain",
joinColumns = #JoinColumn(name = "responseDomain_id")),
#AssociationOverride(name = "pk.code",
joinColumns = #JoinColumn(name = "code_id"))
})
public class ResponseDomainCode implements Serializable {
#EmbeddedId
private ResponseDomainCodeId pk = new ResponseDomainCodeId();
#Column(name = "rank")
private String rank;
public ResponseDomainCodeId getPk() {
return pk;
}
public void setPk(ResponseDomainCodeId pk) {
this.pk = pk;
}
public String getRank() {
return rank;
}
public void setRank(String rank) {
this.rank = rank;
}
#Transient
public ResponseDomain getResponseDomain() {
return getPk().getResponseDomain();
}
public void setResponseDomain(ResponseDomain responseDomain) {
this.getPk().setResponseDomain(responseDomain);
}
#Transient
public Code getCode() {
return getPk().getCode();
}
public void setCode(Code code) {
this.getPk().setCode(code);
}
//Omitted rest
}
ResponseDomainCodeId.java
#Embeddable
public class ResponseDomainCodeId implements Serializable {
#ManyToOne
private ResponseDomain responseDomain;
#ManyToOne
private Code code;
public ResponseDomainCodeId() {
}
public ResponseDomain getResponseDomain() {
return responseDomain;
}
public void setResponseDomain(ResponseDomain responseDomain) {
this.responseDomain = responseDomain;
}
public Code getCode() {
return code;
}
public void setCode(Code code) {
this.code = code;
}
//Omitted rest
}
With the help of #adamw I managed to solve this, by changing my mapping.
Instead of using a composite key, a table with its own unique ID was generated.
+----------------------+------------+
| RESPONSE_DOMAIN_CODE | TYPE |
+----------------------+------------+
| id | PK(BIGINT) |
| response_domain_id | BIGINT |
| code_id | BIGINT |
| rank | VARCHAR |
+----------------------+------------+
Now instead of using #Embeddable and #EmbeddedId I have a #ManyToOne and #OneToMany annotation on either side, and query based on ResponseDomain.
This enable full version audit control using Hibernate Envers also in relations like this.
I hope this will be helpful for someone at some point.
Related
Preamble An Oracle DB read-only(I don't have access) has the following two tables:
person
person table
| id | name | gender |
| -- | ------ | ------ |
| 2001 | Moses | M |
| 2002 | Luke | M |
| 2003 | Maryam | F |
PK(id)
reference
reference table
| sep | guid | table_name |
| --- | -------- | ---------- |
| 2001 | EA48-... | person |
| 2002 | 047F-... | person |
| 2003 | B23F-... | person |
| 2003 | 3E3H-... | address |
| 2001 | H2E0-... | address |
| 2001 | 92E4-... | report |
No PK, it is generated by some triggers
The person table is a straight forward table with a primary key. The reference table are generated via a trigger that stores the id(PK) in sep column of any table and the table name that is store in table_name column (Note: Since no primary key, the reference table stores duplicate values in the sep column but distinct value into guid.)
Requirement
I need to use JPA to get the record from the reference table and map to the person record (person.id and other table.id are stored in reference.sep column) using Jackson as follows
{
"id": 2001,
"name": "Moses",
"gender": "M",
"reference": {
"sep": 2001,
"guid": "EA48-...",
"tableName": "person"
}
}
Entity (Person)
#Entity
#Table(name="person")
public class Person implements Serializable {
#Id
private Long id;
private String name;
private String gender;
#OneToOne
#JoinColumn(name = "id", referencedColumnName = "sep", insertable = false, updatable = false)
private Reference reference;
// Getters & Setters
}
Entity (Reference)
#Entity
#Table(name="reference")
public class Reference implements Serializable {
private Long sep;
private String guid;
private String tableName;
//Getters & Setters
}
Problem 1
JPA throws error of no #Id annotation on Reference table.
Problem 2
If I add the #Id annotation on the sep field, JPA throws error of duplicate values for that column.
Problem 3
If I add the #Id annotation on the guid field (it is unique field), JPA throws error of mapping a Long to a String field (org.hibernate.TypeMismatchException: Provided id of the wrong type for class)
Question
How can I structure the entities (Person.java and Reference.java) in order to come up with the output below:
{
"id": 2001,
"name": "Moses",
"gender": "M",
"reference": {
"sep": 2001,
"guid": "EA48-...",
"tableName": "person"
}
}
Reference is the owner of the relationship and needs to be specified as such in either a unidirectional or bidirectional relationship
// Unidirection relationship
#Entity
public class Person implements Serializable {
#Id
private Long id;
private String name;
private String gender;
// Getters & Setters
}
#Entity
public class Reference implements Serializable {
#Id
private String guid;
private String tableName;
#OneToOne
#JoinColumn(name = "sep", insertable = false, updatable = false)
private Person person;
//Getters & Setters
}
// Bidirection relationship
#Entity
public class Person implements Serializable {
#Id
private Long id;
private String name;
private String gender;
#OneToOne(mappedBy = "person")
private Reference reference;
// Getters & Setters
}
#Entity
public class Reference implements Serializable {
#Id
private String guid;
private String tableName;
#OneToOne
#JoinColumn(name = "sep", insertable = false, updatable = false)
private Person person;
//Getters & Setters
}
Same example for read any kind records from table reference:
#Entity
#Table(name = "reference")
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
#DiscriminatorColumn(name = "table_name")
public abstract class AbstractReferenceEntity {
#Id
private UUID guid;
public UUID getGuid() {
return guid;
}
public void setGuid(UUID guid) {
this.guid = guid;
}
}
#Entity
#DiscriminatorValue("person")
public class PersonReferenceEntity extends AbstractReferenceEntity {
#OneToOne
#JoinColumn(name = "sep")
private Person person;
public Person getPerson() {
return person;
}
public void setPerson(Person person) {
this.person = person;
}
}
// Read all types of records.
AbstractReferenceEntity e = this.em.find(AbstractReferenceEntity.class, sameUuid));
// Read only person type of records.
AbstractReferenceEntity e = this.em.find(PersonReferenceEntity, sameUuid);
For the benefit of anyone looking to solve this kind of issue, I will be posting the solution that works for me following #XtremeBaumer suggestion in the comment.
Step 1: For the REFERENCE table, I made the JPA entity to have two ids (sep & table_name) by creating an extra composite Id class and using it in the Reference Entity.
public class RefId {
private Long sep;
private String tableName;
//All args constructor
//No args constructor
//Setters & Getters
//Override the equals() and hashCode() !very important
}
Step 2: Add the above class as a composite id to the Reference entity by using the #IdClass annotation. We must also declare and annotate the two fields with #Id in the Reference class.
#Entity
#Table(name="reference")
#IdClass(RefId.class) // Important if not using embeddable type
public class Reference implements Serializable {
#Id
private Long sep;
private String guid;
#Id
private String tableName;
//Getters & Setters
}
Step 3: In the Person entity, declare #OneToOne on the Reference entity and annotate it with #JoinColumnsOrFormulas as shown below:
#Entity
#Table(name="person")
public class Person implements Serializable {
#Id
private Long id;
private String name;
private String gender;
#OneToOne
#JoinColumnsOrFormulas(value = {
#JoinColumnOrFormula(column = #JoinColumn(name = "id", referencedColumnName = "sep", insertable = false, updatable = false)),
#JoinColumnOrFormula(formula = #JoinFormula(value = "'person'", referencedColumnName = "tableName"))
})
private Reference reference;
// Getters & Setters
}
This works fine in the scenario above. Note in the formula = #JoinFormula, it is like we are declaring the 'WHERE' clause i.e. WHERE table_name = 'person' (Don't miss the single quotes)
Lastly, by using the Jackson object mapper, I was able to get
{
"id": 2001,
"name": "Moses",
"gender": "M",
"reference": {
"sep": 2001,
"guid": "EA48-...",
"tableName": "person"
}
}
Thanks for your insight (#XtremeBaumer)
I have created four entity classes as:
#Entity
#Table(name = "DashboardRegionCountry")
public class DashboardRegionCountry implements Serializable {
#Id
#Column(name = "id")
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#Column(name = "dashboardId")
private long dashboardId;
#OneToOne(targetEntity = Country.class)
#JoinColumn(name="countryId")
private Country country;
#OneToOne(targetEntity = Region.class)
#JoinColumn(name="regionId")
private Region region;
#ManyToOne()
#JoinColumn(name="dashboardId")
private Dashboard dashboard;
}
#Entity
#Table(name = "Dashboard")
public class Dashboard implements Serializable {
#Id
#Column(name = "dashboardId")
#GeneratedValue(strategy = GenerationType.AUTO)
private Long dashboardId;
#Column(name = "dashboardName")
private long dashboardName;
#OneToMany(mappedBy= dashboard)
private List<DashboardRegionCountry> dashboardRegionCountry;
}
#Entity
#Table(name = "Country")
public class Country implements Serializable {
#Id
#Column(name = "countryId")
#GeneratedValue(strategy = GenerationType.AUTO)
private Long countryId;
#Column(name = "shortName")
private String shortName;
#Column(name = "longName")
private String longName;
}
#Entity
#Table(name = "Region")
public class Region implements Serializable {
#Id
#Column(name = "regionId")
#GeneratedValue(strategy = GenerationType.AUTO)
private Long regionId;
#Column(name = "shortName")
private String shortName;
#Column(name = "longName")
private String longName;
}
And the table schemas for the respective entities are as follows:
DashboardRegionCountry:
+----------------+---------------+
| Field | Type |
+----------------+---------------+
| id(PK) | Number(11) |
| dashboardId(FK)| Number(11) |
| countryId | Number(11) |
| regionId | Number(11) |
+-------------+------------------+
Dashboard:
+----------------+---------------+
| Field | Type |
+----------------+---------------+
| dashboardId(PK)| Number(11) |
| dashboardName | varchar(11) |
+-------------+------------------+
Country:
+-------------+---------------+
| Field | Type |
+-------------+---------------+
| countryId(PK)| Number(11) |
| shortName | Varchar2(10) |
| longName | Varchar2(10) |
+-------------+---------------+
Region:
+-------------+---------------+
| Field | Type |
+-------------+---------------+
| regionId(PK)| Number(11) |
| shortName | Varchar2(10 |
| longName | Varchar2(10) |
+-------------+---------------+
Basically, when user enters the dashboardId then we want to fetch, dashboardDetails along with the Region and respective countries presnt in that region. As stated above, I only have region and country Ids in my table and their names are present in other tables.
I want to display my sample output something like:
{
"dashboardId":20,
"DashboardRegionCountry": [{
"Region":"ASIA",
"dashboardId":["India","China"]
},
{
"Region":"NAM",
"dashboardId":["USA","Canada"]
}
]
}
I am trying to write JPA repository but was wondering is it possible to write something like:
#Repository
public interface DashboardRegionCountryRepository extends JpaRepository<DashboardRegionCountry, Long>{
List<Map<Long,Country>> findRegionBy_RegionId(Long dashboardId);
}
I am trying to fetch all the data in one query, any suggestion will be really helpful
Just get the corresponding DashboardRegionCountry using getById (check the reference documentation) and it will contain both associated Country and Region. If you then don't want to expose all Country and Region information in your entities I suggest you map them to a DTO that would be the model that you want to use while returning something on your controllers.
findByRegion_regionId try this this will work.
I'm trying to find a solution for a this problem:
We have a table (code_list) in the database which contains all enum -like data.
Let us say we have an Affiliate that can have an AffiliateType and a LanguageCode.
We put all this in the code_list table where id_code_list field tells us if we are talking about AffiliateType or Languagecode and we can have a String or Integer identifier that tells us about which AffiliateType we are talking.
Example of the data in table:
| id_code_list | val_num | val_string | label |
| :----------: |:------: |:---------: | :-----:|
| TYP_AFF | 3 | 3 | Other |
| TYP_AFF | 1 | 1 | Divers |
| COD_LAN | 1 | 1 | French |
I tried to map this that way:
Code List Parent
#Entity
#Table(name = "CODE_LIST")
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
#DiscriminatorColumn(name = "ID_CODE_LIST")
public abstract class CodeListString {
#Id
#Column(name = "VAL_STRING")
protected String value;
#Embedded
protected Label label;
...
Affiliate Type:
#Entity
#DiscriminatorValue("TYP_AFF")
public class AffiliateType extends CodeListString{
public static final AffiliateType SOCIAL_SECRETARIAT = new AffiliateType("1");
public static final AffiliateType VARIOUS_SERVICES = new AffiliateType("2");
public static final AffiliateType OTHERS = new AffiliateType("3");
public static final AffiliateType SOPA = new AffiliateType("9");
public AffiliateType() {}
private AffiliateType(String value) {
super(value);
}
}
And my Affiliate entity:
#Entity
#Table(name = "AFF")
public class Affiliate {
#ManyToOne
#JoinColumn(name = "TYP_AFF")
private AffiliateType type;
But I'm getting this error:
org.hibernate.MappingException: Foreign key (FK7re97tvvbbo2km961gy9b5jw6:aff [typ_aff])) must have same number of columns as the referenced primary key (code_list [val_string,id_code_list])
So, is there a way to make this work or do you have other solution for this problem ?
PS: I'm using Hibernate and the solution like
#ManyToOne(targetEntity = AffiliateType.class)
#JoinColumn(name = "TYP_AFF")
#Where(clause = "ID_CODE_LIST='TYP_AFF'")
private AffiliateType type;
Doesn't work...
I've removed a dependency to a custom library and this worked.
I could not reproduce this error on a clean project.
Anyway this solution is really slow !
At first I thought this solution might solve my problem:
#Entity
public class User {
#JoinTable(name = "user_permission",
joinColumns = #JoinColumn(name = "user_id"),
inverseJoinColumns = #JoinColumn(name = "permission_id"))
#MapKeyJoinColumn(name = "project_id")
#ElementCollection
private Map<Project, Permission> permissions = new HashMap<>();
}
#Entity
public class Project {
...
}
#Entity
public class Permission {
...
}
But in this implementation there can only be one Permission set per Project. I'd like to accomplish the ability to set multiple permissions for a project such that the following could be true:
| user_id | project_id | permission_id |
|---------|------------|---------------|
| 1 | 1 | 1 |
|---------|------------|---------------|
| 1 | 1 | 2 |
|---------|------------|---------------|
| 1 | 2 | 1 |
|---------|------------|---------------|
| 1 | 2 | 2 |
|---------|------------|---------------|
| 2 | 1 | 1 |
|---------|------------|---------------|
| 2 | 1 | 2 |
|---------|------------|---------------|
| 2 | 2 | 1 |
|---------|------------|---------------|
| 2 | 2 | 2 |
You can use an entity dedicated to your relation table. It's the way we declare relations with their own attributes for instance.
This would result in the following implementation:
#Entity
#IdClass(PermissionAssignation.class)
public class PermissionAssignation {
#Id
#ManyToOne
#JoinColumn(name="user_id")
private User user;
#Id
#ManyToOne
#JoinColumn(name="project_id")
private Project project;
#Id
#ManyToOne
#JoinColumn(name="permission_id")
private Permission permission;
...
}
I used the solution found in this post: Hibernate and no PK
It explains how to create the PK with field (I did not test it).
If it does not work, you'd better use a EmbeddedId class.
And if you want your relation to be bidirectional, you can then use a Set<PermissionAssignation> (or List, as you prefer/need):
#Entity
public class User {
#OneToMany(mappedBy="user")
private Set<PermissionAssignation> permissions;
}
Since I recently ran into this and still struggled, I wanted to share a complete code example. This example uses a separate #EmbeddedId class which will still create a table with 3 PK/FK columns. My example makes use of Lombok to fill in a bunch of boiler-plate code such as getters/setters, constructors, etc. It was also necessary to override the equals and hashcode methods. This was written using Spring framework, which wires up the repos & tests. Hopefully someone finds this a useful guide.
/* ENTITY CLASSES */
#Entity
#Data
#Table(name = "_Who")
public class Who {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
#OneToMany(mappedBy = "who", fetch = FetchType.EAGER)
#JsonManagedReference
List<WhoWhatWhere> storage;
}
#Entity
#Data
#Table(name = "_What")
public class What {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String thing;
}
#Entity
#Data
#Table(name = "_Where")
public class Where {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String place;
}
#Data
#EqualsAndHashCode(onlyExplicitlyIncluded = true)
#Entity
#NoArgsConstructor
#Table(name = "_WhoWhatWhere")
public class WhoWhatWhere {
public WhoWhatWhere(Who who, What what, Where where) {
this.who = who;
this.what = what;
this.where = where;
this.setId(new WhoWhatWhereId(who.getId(), what.getId(), where.getId()));
}
#EmbeddedId
WhoWhatWhereId id;
#ManyToOne(fetch = FetchType.EAGER)
#JsonBackReference
#JoinColumn(name = "who_id", insertable = false, updatable = false)
private Who who;
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "what_id", insertable = false, updatable = false)
private What what;
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "where_id", insertable = false, updatable = false)
private Where where;
}
#Embeddable
#NoArgsConstructor
#AllArgsConstructor
#EqualsAndHashCode(onlyExplicitlyIncluded = true)
public class WhoWhatWhereId implements Serializable {
#Column(name = "who_id")
Long whoId;
#Column(name = "what_id")
Long whatId;
#Column(name = "where_id")
Long whereId;
}
/* REPOSITORIES */
#Repository
public interface WhoRepository extends PagingAndSortingRepository<Who, Long> {
Iterable<Who> findWhoByName (String name);
}
#Repository
public interface WhatRepository extends PagingAndSortingRepository<What, Long> {
}
#Repository
public interface WhereRepository extends PagingAndSortingRepository<Where, Long> {
}
#Repository
public interface WhoWhatWhereRepository extends PagingAndSortingRepository<WhoWhatWhere, WhoWhatWhereId> {
}
/* TEST CLASS */
#SpringBootTest
#Slf4j
public class ThreeWayAssocTest {
private final WhoRepository whoRepository;
private final WhatRepository whatRepository;
private final WhereRepository whereRepository;
private final WhoWhatWhereRepository whoWhatWhereRepository;
#Autowired
public ThreeWayAssocTest(WhoRepository whoRepository, WhatRepository whatRepository, WhereRepository whereRepository, WhoWhatWhereRepository whoWhatWhereRepository) {
this.whoRepository = whoRepository;
this.whatRepository = whatRepository;
this.whereRepository = whereRepository;
this.whoWhatWhereRepository = whoWhatWhereRepository;
}
#Test
public void attemptPersistence() {
/*
* the commented pieces can be used to do the initial inserts. Later, fetch existing values so as not to fill
* up the database
*/
Who who =
/* new Who();
who.setName("Carl");
whoRepository.save(who);*/
whoRepository.findById(1L).get();
What what =
/* new What();
what.setThing("strawberry");
whatRepository.save(what);
what.setThing("salad");
whatRepository.save(what);*/
whatRepository.findById(2L).get();
Where where =
/* new Where();
where.setPlace("plate");
whereRepository.save(where);*/
whereRepository.findById(1L).get();
WhoWhatWhere whoWhatWhere = new WhoWhatWhere(who, what, where);
whoWhatWhereRepository.save(whoWhatWhere);
LOGGER.debug("finished");
}
#Test
public void testSerializing() throws JsonProcessingException {
Iterable<Who> examples = whoRepository.findWhoByName("Carl");
Who carl = examples.iterator().next();
LOGGER.debug("Carl: {}", carl);
LOGGER.debug("found some: \n {}", new ObjectMapper().writeValueAsString(examples));
}
}
I am running into the following problem: I must map my Employee class to the following database schema (where id1 and id2 is a composite primary key):
--------------------------
| id1 | id2 | ManagerId2 |
--------------------------
| 1 | 1 | NULL | <--- Have no manager / Can be manager of many
--------------------------
| 1 | 2 | 1 | <--- Is manager / Has manager 1-1
--------------------------
| 1 | 3 | 1 | <--- Is manager / Has manager 1-1
--------------------------
I am aware that a foreign key must have the same columns as the primary key it references (same number of columns). The point is that once an Employee is inserted with id1 = 1 it must only reference managers with id1 = 1. A way to keep integrity and avoid scenarios like the following:
---------------------------------------
| id1 | id2 | ManagerId1 | ManagerId2 |
---------------------------------------
| 1 | 1 | NULL | NULL | <--- Have no manager / Can be manager of many
---------------------------------------
| 2 | 1 | NULL | NULL | <--- Have no manager / Can be manager of many
---------------------------------------
| 1 | 2 | 2 | 1 | <--- THIS IS NOT ALLOWED
---------------------------------------
| 1 | 3 | 2 | 1 | <--- NOR THIS
---------------------------------------
So far the best that I got is the following mapping (although it creates the table as expected it simply doesn't populates the ManagerId2 field):
#Entity
#Table(name="Employee")
public class Employee {
public Employee(){
}
#EmbeddedId
private EmployeeId id;
public void setId(EmployeeId id) {
this.id = id;
}
public EmployeeId getId() {
return id;
}
#ManyToOne(cascade={CascadeType.ALL})
#JoinColumns({
#JoinColumn(name = "id1", referencedColumnName = "id1", insertable=false, updatable=false), //its fine to have this attribute not insertable nor updatable
#JoinColumn(name = "id2_manager", referencedColumnName = "id2", insertable=false, updatable=false) //but I must be able to update this one!
})
private Employee manager;
public Employee getManager() {
return manager;
}
public void setManager(Employee manager) {
this.manager = manager;
}
}
#Embeddable
public class EmployeeId implements Serializable{
public EmployeeId() {
}
public EmployeeId(int id1, int id2) {
this.id1 = id1;
this.id2 = id2;
}
private int id1;
private int id2;
public int getId1() {
return id;
}
public void setId(int id1) {
this.id1 = id1;
}
public int getId2() {
return id2;
}
public void setId2(int id2) {
this.id2 = id2;
}
//hashCode and equals properly overriden
}
After goggling the entire day it seems that I am not able to find anything!
Can someone kindly show me what I am doing wrong, or point to any good resources?
PS.: I can't change the db schema, it is not an option
Im not sure how to do this with annotations, but ive found a workaround that might help you.
#Entity
#Table(name = "Employee")
public class Employee {
#EmbeddedId
private EmployeeId id;
private Integer id2_manager;
#PrePersist
#PreUpdate
public void prePersistUpdate() {
if (manager != null)
id2_manager = manager.getId().getId2();
}
public Employee() {
}
public void setId(EmployeeId id) {
this.id = id;
}
public EmployeeId getId() {
return id;
}
#ManyToOne(cascade = { CascadeType.ALL })
#JoinColumns({ #JoinColumn(name = "id1", referencedColumnName = "id1", insertable = false, updatable = false),
#JoinColumn(name = "id2_manager", referencedColumnName = "id2", insertable = false, updatable = false, nullable = true) })
private Employee manager;
public Employee getManager() {
return manager;
}
public void setManager(Employee manager) {
this.manager = manager;
}
}
to test
static EntityManagerFactory emf;
static EntityManager em;
public static void main(String[] args) {
emf = Persistence.createEntityManagerFactory("unit");
em = emf.createEntityManager();
Employee manager = new Employee();
manager.setId(new EmployeeId(1, 1));
Employee e1 = new Employee();
e1.setId(new EmployeeId(1,2));
e1.setManager(manager);
em.getTransaction().begin();
em.persist(manager);
em.persist(e1);
em.getTransaction().commit();
Employee e2 = em.find(Employee.class, new EmployeeId(1, 2));
System.out.println(e2.getManager().getId().getId1() + "-" + e2.getManager().getId().getId2());
// prints 1-1
}