Auto generated id not cascading on EmbeddedId in child entity in Hibernate - java

When trying to insert an Entity - Awith a set Another Entity B, B should get the Auto generated Id from A but its null.
Tried and failed:
#MapsId("taskPKId.storyId.id") - Same error.
#Embeddable
class StoryId {
#ManyToOne(fetch = FetchType.Lazy)
#JoinColumn(name = "STORY_ID")
Long id;
} //Incomprehensible Null pointer exception
mappedBy("story") - same error
Tried with mappedBy('story') but getting an error with repeated column and so had to map it with insertable=false and updatable=false [Hibernate doesn't recognize insertable=false for #EmbeddedId]
I am getting STORY_ID = null and therefore saveAll fails on storyRepository.saveAll(stories) where storyRepository is a Spring Data repository
#Table(name = "STORY")
#EqualsAndHashCode
class Story {
#Id
#GeneratedValue(stratergy=GenerationType.Auto)
#Column(name="STORY_ID")
Long id;
#Column(name="STORY_NAME")
String name;
//#OneToMany(cascade=ALL, mappedBy="taskPKId.storyId.id", fetch = FetchType.Lazy) // tried this as well
#OneToMany(cascade=ALL, mappedBy="story", fetch = FetchType.Lazy)
Set<Task> task;
}
#Table(name = "TASK_XREF")
#EqualsAndHashCode
Class Task {
#EmbeddedId
TaskPKId taskPKId;
#Column(name = "TASK_NAME")
String name;
#ManyToOne (fetch = FetchType.Lazy, optional = false)
#JoinColumn(name = "STORY_ID", referencedColumnName = "STORY_ID", nullable = false, insertable = false, updatable = false)
Story story;
}
#Embeddable
#EqualsAndHashCode
Class TaskPKId implements Serializable {
TaskId taskId;
TaskTypeId taskTypeId;
StoryId storyId;
}
#Embeddable
#EqualsAndHashCode
class StoryId implements Serializable {
#Column(name = "STORY_ID")
Long id;
}
Tables:
STORY [STORY_ID, STORY_NAME]
TASK_XREF [(TASK_ID(FK), TASK_TYPE_ID(FK), STORY_ID(FK)) PK,TASK_NAME]
Story gets inserted (before commit ofcourse), but fails because STORY_ID is sent as null to TASK_XREF for the next inserts

I'm not quite sure why your configuration does not work. I have a similar configuration in one of my projects that works just fine. I was able to find a solution however, by adding a #MapsId annotation to the ManyToOne in the Task class. (see can someone please explain me #MapsId in hibernate? for an explanation about MapsId) I also removed insertable=false and updatable=false. See below for the code.
I didn't get MapsId to work with the StoryId class, so i changed the type of TaskPKID.storyId from StoryId to long. The StoryId class doesn't seem to add much, so hopefully this isn't to much of a problem. If you find a solution please let me know in the comments though!
By the way, your code has a lot of problems. There's a bunch of typo's, and there is a OneToMany mapping on a property that is not a Collection (which isn't allowed) This made it more difficult for me to debug the problem. Please make sure to post better quality code in your questions next time.
Here is the Task class the way I implemented it:
#Entity
#Table(name = "TASK_XREF")
class Task {
#EmbeddedId
TaskPKId taskPKId;
#Column(name = "TASK_NAME")
String name;
#MapsId("storyId")
#ManyToOne(fetch = FetchType.LAZY, optional = false)
#JoinColumn(name = "STORY_ID")
Story story;
//getters, setters
}
And here is the TaskPKID class:
#Embeddable
class TaskPKId implements Serializable {
long taskId;
long taskTypeId;
#Column(name="STORY_ID")
long storyId;
public long getTaskId() {
return taskId;
}
public void setTaskId(long taskId) {
this.taskId = taskId;
}
public void setTaskTypeId(long taskTypeId) {
this.taskTypeId = taskTypeId;
}
}

I'm not sure what you want to achieve, but it looks like you have combined #OneToMany annotation with #OneToOne-like implementation (which can lead to unexpected behavior like this one).
Possible solutions:
If one story owns multiple tasks
// Story.java
#OneToMany(cascade=ALL, mappedBy="story", fetch = FetchType.Lazy)
Set<Task> task; // basically Set, List or any other collection type
// Task.java
#ManyToOne (fetch = FetchType.Lazy, optional = false)
#JoinColumn(name = "STORY_ID", referencedColumnName = "STORY_ID")
Story story;
If one story owns only one task
// Story.java
#OneToOne(cascade=ALL, mappedBy="story", fetch = FetchType.Lazy)
Task task;
// Task.java
#OneToOne (fetch = FetchType.Lazy, optional = false)
#JoinColumn(name = "STORY_ID", referencedColumnName = "STORY_ID")
Story story;
Further reading:
#OneToOne
#OneToMany

Related

Hibernate Multiple #OneToMany bound to same entity type

I have yet another #OneToMany question. In this case, I'm trying to model a person having a list of excluded people they shouldn't be able to send items to. This is a Spring Boot app using JPA.
In the code below, the exclusions list populates properly but the excludedBy List does not. Because of this, I believe that is causing the deletion of a Person that is excluded by another person to fail because the Exclusion in excludedBy is not mapped on the object properly.
#Entity
#Table(name = "person")
public class Person {
#Id
#GeneratedValue
#Column(nullable = false)
Long id;
...
#OneToMany(mappedBy = "sender", cascade = { CascadeType.ALL })
List<Exclusion> exclusions = new ArrayList<>();
//This is not getting populated
#JsonIgnore
#OneToMany(mappedBy = "receiver", cascade = { CascadeType.ALL })
List<Exclusion> excludedBy = new ArrayList<>();
...
}
#Entity
#Table(name = "exclusions")
public class Exclusion {
#Id
#GeneratedValue
#Column(nullable = false)
Long id;
#ManyToOne
#JsonIgnore
Person sender;
#ManyToOne
Person receiver;
...
}
I would expect that this would have mapped the bidirectional relationship properly and as such the excludedBy List would be populated as well.
Any wisdom on this matter would be great!
1 - An #Id is by default not nullable, not required:
#Column(nullable = false)
2 - There is no need for an #Id in this class. Both sides of the exclusion are together unique. Not needed:
#Id
#GeneratedValue
Long id;
3 - An "Exclusion" requires both an excludedBy and an excluded, give them names that match and they are your #Id. It is a 2 way ManyToMany relationship.
#Entity
#Table(name = "exclusions")
public class Exclusion {
#Id
#ManyToMany // An ID so not optional, so no need for (optional = false)
Person excludedBy;
#Id
#ManyToMany // An ID so not optional, so no need for (optional = false)
Person excluded;
}
Entity Exclusion always knows both sides of the story.
#ManyToMany(mappedBy = "excludedBy", cascade = { CascadeType.ALL })
List<Exclusion> excluded = new ArrayList<>();
#ManyToMany(mappedBy = "excluded", cascade = { CascadeType.ALL })
List<Exclusion> excludedBy = new ArrayList<>();
Tip: JSON DTOs shouldn't be defined in your JPA DTOs, otherwise you can't change your internal data model independently of your external API model.
I had this problem in the past. Your key problem ist that your ORM Mapper hibernate does not know which of your database entries need to be assinged to exclusions and which are assiged to excludedBy. You need a discriminator and add the constraint in your select. I would propose a solution that looks something like this:
#OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
#JoinColumn(name = "PRIMARY_KEX_IN_EXCLUSION_TABLE", referencedColumnName = "id")
#Where(clause = "is_excluded_by = 0")
private Set<Exclusion> exclusions;
#OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
#JoinColumn(name = "PRIMARY_KEX_IN_EXCLUSION_TABLE", referencedColumnName = "id")
#Where(clause = "is_excluded_by = 1")
private Set<Exclusion> excludedBy;
the value isExcludedBy needs to be a database column, part of your Entity and set in your code manually.
I think you also need to use Set instead of List when having multiple collections in one Entity. https://vladmihalcea.com/spring-data-jpa-multiplebagfetchexception/

Entity Mapping with Cascade does not seem to work

I have the following Situation:
OrganisationEntity.java
#Entity
#Table(name = "organisation")
public class OrganisationEntity {
// ...
private PersonEntity contactPerson;
// ...
#OneToOne
#JoinColumn(name = "contact_person_id", referencedColumnName = "id", nullable = false)
public PersonEntity getContactPerson() {
return contactPerson;
}
public void setContactPerson(PersonEntity contactPerson) {
this.contactPerson = contactPerson;
}
// ...
}
ContactPerson.java
#Entity
#Table(name = "person")
public class PersonEntity {
private int id;
// ...
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id", nullable = false)
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
// ...
}
On the database the table Organisation has a non-nullable foreign key to Person. The entity mapping is uni-directional New when I want the persist a new pair of records (one organisation and one person) with merge on the OrganisationEntity I get the following error:
17:10:19.827 WARN [http-nio-8080-exec-2]
[org.hibernate.action.internal.UnresolvedEntityInsertActions] [144]
HHH000437: Attempting to save one or more entities that have a
non-nullable association with an unsaved transient entity. The unsaved
transient entity must be saved in an operation prior to saving these
dependent entities.
Unsaved transient entity:
([ch.freiwilligenarbeit_sempach.entity.PersonEntity#0])
Dependent entities:
([[ch.freiwilligenarbeit_sempach.entity.OrganisationEntity#]])
Non-nullable association(s):
([ch.freiwilligenarbeit_sempach.entity.OrganisationEntity.contactPerson])
This makes perfect sense to me, since it tries to insert the organisation with no reference to the person whatsoever. So I would usually define a cascade behaviour, so that hibernate inserts the person first, sets the reference ond the organisation and then persists the organisation. I tried the following on the organisation entity:
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "contact_person_id", referencedColumnName = "id", nullable = false)
public PersonEntity getContactPerson() {
return contactPerson;
}
and
#OneToOne
#JoinColumn(name = "contact_person_id", referencedColumnName = "id", nullable = false)
#Cascade(org.hibernate.annotations.CascadeType.ALL)
public PersonEntity getContactPerson() {
return contactPerson;
}
But neither of the seem to work. I still get the same error. But I think this should actually work.
Any help is highly appreciated! Thanks in advance.
I don't understand completely, what do you want, but I suppose that you want to save a person entity with a person organization.
If you use different ids, you should add the #OneToOne annotation with attribute mappedBy=contactPerson
In the PersonEntity class
#OneToOne(cascade = CascadeType.ALL, mappedBy = "contactPerson")
private OrganisationEntity orgEntity;
In the OrganisationEntity class
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "contact_person_id", nullable = false)
private PersonEntity contactPerson; // name which is pointed as mappedBy attribute
Then you can save this correct writing something like this
orgEntity.setContactPerson(contactPerson);
orgRepo.save(orgEntity);
p.s. I wrote using field injection, but it's not necessary.
This did the trick.
#Cascade(org.hibernate.annotations.CascadeType.ALL)
Although I believe I stopped tomcat and cleaned redeployed the webapp i did not work yesterday. Today it worked as expected, so that the referenced entities (Person) were inserted before the referencing entity (Organisation).

JPA OneToMany/ManyToOne relationship not working - What am I missing?

I know this has been asked a lot of times before, I know it because I've searched for every related question to my problem to try to find a solution, however, none of the proposed solutions are working for me and I'm pretty sure that I have to be missing something.
Person Class:
#Entity
#Table(name = "person", schema = "test")
public class PersonEntity {
#Id
#Column(name = "id")
private long id;
#Basic
#Column(name = "name")
private String name;
#Basic
#Column(name = "age")
private int age;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "personid")
private List<ProjectEntity> projects;
}
Project Class:
#Entity
#Table(name = "project", schema = "test")
public class ProjectEntity {
#Id
#Column(name = "id")
private long id;
#Basic
#Column(name = "name")
private String name;
#Basic
#Column(name = "budget")
private int budget;
#JoinColumn(name = "personid", referencedColumnName = "id")
#ManyToOne(cascade = CascadeType.ALL)
private PersonEntity personid;
}
I have a bidirectional OneToMany/ManyToOne relationship, I have tried changing
the cascade type to PERSIST, adding 'optional=false' and way more things but nothing seems to work.
I read that I have to 'join' manually the entities before the persist, and that's what I did:
em = JPAUtility.getEntityManager();
em.getTransaction().begin();
PersonEntity personTest = new PersonEntity();
personTest.setName("Test");
personTest.setAge(23);
ProjectEntity projectTest = new ProjectEntity();
projectTest.setName("hello");
projectTest.setBudget(232);
projectTest.setPersonid(personTest);
List<ProjectEntity> projects = new ArrayList<ProjectEntity>();
projects.add(projectTest);
personTest.setProjects(projects);
em.persist(personTest);
em.getTransaction().commit();
em.close();
return personTest;
But I still get this:
Caused by:
com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException:
Cannot add or update a child row: a foreign key constraint fails
(`test`.`project`, CONSTRAINT `FK_Personid` FOREIGN KEY (`personid`) REFERENCES
`person` (`id`) ON DELETE CASCADE ON UPDATE CASCADE)
I honestly don't know what I'm missing, if anyone has any suggestion I'll be more than happy to try it.
Thank you so much!
SOLUTION
I managed to solve the problem thanks to all the suggestions, basically, I was missing the #GeneratedValue(strategy=GenerationType.AUTO) annotation which I removed because I thought it didn't work but, it wasn't working because I was missing a property on the persistence.xml:
<property name="hibernate.id.new_generator_mappings" value="false" />
I found this info here
You also need a method to add the relationship in the objects:
public void addToProjects(ProjectEntity project){
project.setPersonid(this);
this.projects.add(project);
}
To make this work you need to initialize the List when you declare the variable:
private List<ProjectEntity> projects = new ArrayList<ProjectEntity>();
And that's it!
This is the final working code in case anyone can find it useful :):
Person Class:
#Entity
#Table(name = "person", schema = "test")
public class PersonEntity {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
#Column(name = "id")
private long id;
#Basic
#Column(name = "name")
private String name;
#Basic
#Column(name = "age")
private int age;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "personid")
private List<ProjectEntity> projects = new ArrayList<ProjectEntity>();
public void addToProjects(ProjectEntity project) {
project.setPersonid(this);
this.projects.add(project);
}
}
Project Class:
#Entity
#Table(name = "project", schema = "test")
public class ProjectEntity {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
#Column(name = "id")
private long id;
#Basic
#Column(name = "name")
private String name;
#Basic
#Column(name = "budget")
private int budget;
#JoinColumn(name = "personid", referencedColumnName = "id")
#ManyToOne(cascade = CascadeType.PERSIST)
private PersonEntity personid;
public void setPersonid(PersonEntity personid) {
this.personid = personid;
}
}
Make sure you add the Children to their Parent and vice-versa (addToProjects())
em = JPAUtility.getEntityManager();
em.getTransaction().begin();
PersonEntity personTest = new PersonEntity();
personTest.setName("Butters");
personTest.setAge(10);
ProjectEntity projectTest = new ProjectEntity();
projectTest.setName("Hanks");
projectTest.setBudget(10000);
ProjectEntity projectTest2 = new ProjectEntity();
projectTest2.setName("X");
projectTest2.setBudget(100);
personTest.addToProjects(projectTest);
personTest.addToProjects(projectTest2);
em.persist(personTest);
em.getTransaction().commit();
em.close();
Hope it helps! Thank you so much.
The main thing that you will want to watch out for is to define the owning side of the relation correctly. As far as I remember, my takeaway from the (sometimes difficult to understand) official documentation was that the owning side is pretty much the one that will by default trigger cascades and transparent deletions.
For example, in the above, you have defined the owning side as ProjectEntity, so the most important step for cascaded persistence to work is to add the project to PersonEntity.projects.
You will then want to call persist on the owning side of the relation, i.e.
em.persist(projectTest);
If this doesn't help, I would suggest that you enable SQL logging in your JPA provider to find out what statements it is trying to execute, and especially in what order these entities are being inserted.
Also try, as per existing comments, to persist person first.
If you do this, I believe the correct way is to add the persisted entity to the relationship, i.e:
PersonEntity persistedPerson = em.persist(personTest);
projectTest.setPersonId(persistedPerson);
em.persist(projectTest);
A couple of leads I can think of, because I crossed more than once this kind of problems:
Unless you want a cascade operation from Project to update your Person, you should remove
#ManyToOne(cascade = CascadeType.ALL)
from your personid attribute
Try updating your projects collection instead of creating a new one, because if it's already managed by Hibernate (which doesn't need to be , the persist/merge operation will be executed on the old one and the new one.
Person Class:
private List<ProjectEntity> projects = new ArrayList<>();
your code :
personTest.getProjects().addAll(projects);
I usually prefer merge instead of persist, because I find it more 'natural', and sometimes, the output is clearly not the same.
I had the same problem. A #ManyToOne that was not working for no reason and 2 classes. I added #GeneratedValue(strategy=GenerationType.AUTO), but it didn't fix my problem.
I also tried to rename the classes, clean, close project, restart, etc., but none worked. At the end, I deleted the files (made a copy before) and recreated them from new and that fixed my problem. I was on Eclipse 4.8, Spring 2.5, Groovy 2.5, Java 1.8
UPDATE:
Not really sure what was the problem, anyway (for groovy) check your save method: myUserRepo(new MyUser("username")), check as well your xxxxRepo< MyUser, Integer> and also check that your file is .groovy (last one shouldn't be a problem)
Other UPDATE:
If you're creating a relational between 2 tables and use the save result, be sure to use #Transactional on a Service and link the relation field, for example:
#Transactional
UserAccount save(UserAccount userAccount) {
User user = userRepo.save(new User(userAccount))
UserAccount.setUser(user)
userAccountRepo.save(userAccount)
}

JPA: How to handle versioned entities?

I have a versioning on an entity as part of its primary key. The versioning is done via a timestamp of the last modification:
#Entity
#Table(name = "USERS")
#IdClass(CompositeKey.class)
public class User {
#Column(nullable = false)
private String name;
#Id
#Column(name = "ID", nullable = false)
private UUID id;
#Id
#Column(name = "LAST_MODIFIED", nullable = false)
private LocalDateTime lastModified;
// Constructors, Getters, Setters, ...
}
/**
* This class is needed for using the composite key.
*/
public class CompositeKey {
private UUID id;
private LocalDateTime lastModified;
}
The UUID is translated automatically into a String for the database and back for the model. The same goes for the LocalDateTime. It gets automatically translated to a Timestamp and back.
A key requirement of my application is: The data may never update or be deleted, therefore any update will result in a new entry with a younger lastModified. This requirement is satisfied with the above code and works fine until this point.
Now comes the problematic part: I want another object to reference on a User. Due to versioning, that would include the lastModified field, because it is part of the primary key. This yields a problem, because the reference might obsolete pretty fast.
A way to go might be depending on the id of the User. But if I try this, JPA tells me, that I like to access a field, which is not an Entity:
#Entity
#Table(name = "USER_DETAILS")
public class UserDetail {
#Id
#Column(nullable = false)
private UUID id;
#OneToOne(optional = false)
#JoinColumn(name = "USER_ID", referencedColumnName = "ID")
private UUID userId;
#Column(nullable = false)
private boolean married;
// Constructors, Getter, Setter, ...
}
What would be the proper way of solving my dilemma?
Edit
I got a suggestion by JimmyB which I tried and failed too. I added the failing code here:
#Entity
#Table(name = "USER_DETAILS")
public class UserDetail {
#Id
#Column(nullable = false)
private UUID id;
#OneToMany
#JoinColumn(name = "USER_ID", referencedColumnName = "ID")
private List<User> users;
#Column(nullable = false)
private boolean married;
public User getUser() {
return users.stream().reduce((a, b) -> {
if (a.getLastModified().isAfter(b.getLastModified())) {
return a;
}
return b;
}).orElseThrow(() -> new IllegalStateException("User detail is detached from a User."));
}
// Constructors, Getter, Setter, ...
}
What you seem to require seems to be on the lines of a history table, to keep track of the changes. See https://wiki.eclipse.org/EclipseLink/Examples/JPA/History on how EclipseLink can handle this for you while using normal/traditional JPA mappings and usage.
What you have here is a logical 1:1 relationship which, due to versioning, becomes a technical 1:n relationship.
You have basically three options:
Clean JPA way: Declare an 'inverse' #ManyToOne relationship from user to the "other object" and make sure you always handle it whenever a new User record is created.
'Hack-ish' way: Declare a #OneToMany relationship in the "other object" and force it to use a specific set of columns for the join using #JoinColumn. The problem with this is that JPA always expects unique reference over the join columns so that reading the UserDetail plus referenced User records should work, whereas writing UserDetail should not cascade onto User to avoid unwanted/undocumented effects.
Just store the user's UUID in the "other object" and resolve the reference yourself whenever you need it.
The added code in your question is wrong:
#JoinColumn(name = "USER_ID", referencedColumnName = "ID")
private UUID userId;
More correct, albeit not with the result you want, would be
#JoinColumn(name = "USER_ID", referencedColumnName = "ID")
private User user;
This won't work though, because, as I said above, you may have more than one user record per UserDetail, so you'd need a #OneToMany relationship here, represented by a Collection<User>.
Another 'clean' solution is to introduce an artificial entity with a 1:1 cardinality w.r.t. to the logical User to which you can refer, like
#Entity
public class UserId {
#Id
private UUID id;
#OneToMany(mappedBy="userId")
private List<User> users;
#OneToOne(mappedBy="userId")
private UserDetail detail;
}
#Entity
public class User {
#Id
private Long _id;
#ManyToOne
private UserId userId;
}
#Entity
public class UserDetail {
#OneToOne
private UserId userId;
}
This way, you can somewhat easily navigate from users to details and back.
I came to a solution, that is not really satisfying, but works. I created a UUID field userId, which is not bound to an Entity and made sure, it is set only in the constructor.
#Entity
#Table(name = "USER_DETAILS")
public class UserDetail {
#Id
#Column(nullable = false)
private UUID id;
#Column(nullable = false)
// no setter for this field
private UUID userId;
#Column(nullable = false)
private boolean married;
public UserDetail(User user, boolean isMarried) {
this.id = UUID.randomUUID();
this.userId = user.getId();
this.married = isMarried;
}
// Constructors, Getters, Setters, ...
}
I dislike the fact, that I cannot rely on the database, to synchronize the userId, but as long as I stick to the no setter policy, it should work pretty well.

Hibernate database mapping

Firstly, I am somewhat new with Hibernate. To get to know the technology I am using it in a project. I am trying to map the following database:
Campaign
campaignId(+)
name
Promotion
campaignId(+)
discount(+)
product
message
I've indicated the primary key in both cases with a (+). The 'campaignId' in Promotion is a foreign key to Campaign to model the 1:m mapping (A Campaign has many Promotions). Using annotations I am stuck on how to do this.
I do not really want to add a promotionId in the Promotion table as it makes working with the data cumbersome. This of course, makes the bridging table a bit tricky. I also have problems working with a foreign key that is also part of the primary key.
Is a mapping for this possible at all?
Ok, I got it working. Sort of. Have to check if persistence actually work. I did the following:
#Entity
#Table(name = "CAMPAIGNS")
#Audited
public class CampaignEntity {
private int campaignId;
private String name;
private List<PromotionEntity> promotions;
public CampaignEntity(int campaignId, String name) {
this.campaignId = campaignId;
this.name = name;
}
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "cmp_id")
public int getCampaignId() {
return campaignId;
}
public void setCampaignId(int campaignId) {
this.campaignId = campaignId;
}
// Campaign name here... left out to save space
#OneToMany
#JoinColumn(name = "cmp_id")
public List<PromotionEntity> getPromotions() {
return promotions;
}
public void setPromotions(List<PromotionEntity> promotions) {
this.promotions = promotions;
}
}
Promotion is a vanilla mapping (not using embedded after all), with the fields: campaignId, discount, message. (It also does not have a #ManyToOne annotation.)
Does that make sense?
Lastly, and this will be first prize: as you can see I'm using Envers to audit the whole thing. The above creates a rather ugly "CampaignEntity_PromotionEntity_AUD" table. I understand that it is needed, but how can I rename it to CAMPAIGN_PROMOTION_AUD rather?
Thanks guys!
I got an answer on a lonely website deeply hidden away in far-corners of the Hibernate's Jira error tracking website: https://hibernate.onjira.com/browse/HHH-3729.
The answer is to use #AuditJoinTable(name = "CAMPAIGN_PROMOTION_AUD") of course.
This is a basic example of a one-to-many relationship and its inverse.
public class Campaign
{
#OneToMany(mappedBy = "campaign)
private List<Promotion> promotions;
}
public class Promotion
{
#ManyToOne
private Campaign campaign;
}
You can use an EmbeddedId to create a multi-field PK.
Remove the PK fields from Promotion
Create a separate entity, say PromotionPK, without any annotations except for #Column on the PK fields
In Promotion, include that PK class as field, annotating it using #EmbeddedId, with getters and setters
The FK mapping is as Wouter indicated.
This is what I am now using. It works well and Hibernate handles the PKs of the Promotions for me. Thanks again.
#Entity
#Table(name = "CAMPAIGNS")
#Audited
public class CampaignEntity {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id", nullable = false)
private Integer campaignId;
#Column(name = "name", nullable = false, unique = true)
private String campaignName;
#OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
#JoinTable(name = "CAMPAIGN_PROMOTIONS",
joinColumns = { #JoinColumn(name = "campaign_id") },
inverseJoinColumns = { #JoinColumn(name = "promotion_id") })
private Set<PromotionEntity> promotions;
...
}
and then, PromotionEntity:
#Entity
#Table(name = "PROMOTIONS")
#Audited
public class PromotionEntity implements Comparable<PromotionEntity> {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
#Column(name = "discount", nullable = false)
private Integer discount;
#Column(name = "message", nullable = false)
private String message;
...
}
I also prefer annotating the fields rather than the getters as it is more compact and reads easier.

Categories