Hiibernate: #OrderColumn in #OneToMany causes an UPDATE after INSERT - java

I have the following POJOs for my db schema.
#Entity
#Table(name = "Quotes")
public class QuoteRequest
{
public QuoteRequest(){}
#Id
#Column(name = "quote_request_id")
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long QuoteRequestId;
#OneToMany(mappedBy = "quoteRequest", cascade = CascadeType.ALL)
#OrderColumn(name = "accidents_id")
private Accidents[] accidents;
// Getters and setters
}
#Entity
#Table(name = "Accidents")
public class Accidents
{
public Accidents()
{
}
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "accidents_id")
private long AccidentId;
#Column(name = "amount", nullable = false)
private Float amount;
#ManyToOne(optional = false, cascade = CascadeType.ALL)
#JoinColumn(name = "quote_request_id", nullable = false)
private QuoteRequest quoteRequest;
// Getters and setters
}
Because I'm using an array to store Accidents[] Hibernate is requiring me to add #OrderColumn. Adding this causes an update to be generated after insert that zeros out my accidents_id. The only way I found around that is to change Accidents[] to Set.
How can I keep Accidentsas an array and not have Hibernate force this 2nd update after the insert?

Related

In the Entity class there are two references to the same field in the database

I am studying a training project - working with databases.
Here is a class describing the entity
#Entity
#Table(name = "pricelists", schema = "inventories")
public class PriceList {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(name = "id_inventory", insertable = false, updatable = false)
private Long idInventory;
#ManyToOne
#JoinColumn(name = "id_inventory", nullable = false)
private Inventory inventory;
private Integer price;
}
And there are two variables that refer to the same "id_inventory" field in the database table. Is it possible to do this? Is this not a mistake?
You should leave that
#Entity
#Table(name = "pricelists", schema = "inventories")
public class PriceList {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#ManyToOne
#JoinColumn(name = "id_inventory", nullable = false)
private Inventory inventory;
private Integer price;
}
I hope that will work.

Error relationship persistence using Spring Data JPA in a many to one

I have the following code for many to many or many to one relationship persistence using Spring JPA.
This is my repository test https://github.com/Truebu/testJpa.git
This class has three one-to-many relationships, but none work well
#Entity(name = "routine_assignament")
#Table(name = "routine_assignament")
public class RoutineAssignament {
#Id
#GeneratedValue(strategy = IDENTITY)
#Column(name = "id", updatable = false)
private Long id;
#Column(name = "date_start",nullable = true,columnDefinition = "DATE")
private Date date_start = new Date();
#Column(name = "date_end",nullable = true,columnDefinition = "DATE")
private Date date_end;
#ManyToOne
#JoinColumn(name = "id_user")
private User user;
#ManyToOne
#JoinColumn(name = "id_routine")
private Routine routine;
#OneToMany(mappedBy = "routine_assignament")
private Set<Score> scores = new HashSet<>();
#OneToMany(mappedBy = "routine_assignament")
private Set<Statistic> statistics = new HashSet<>();
#OneToMany(mappedBy = "routine_assignament")
private Set<KeepRoutine> keepRoutines = new HashSet<>();
The other classes
#Entity(name = "score")
#Table(name = "score")
public class Score {
#Id
#GeneratedValue(strategy = IDENTITY)
#Column(name = "id", updatable = false)
private Long id;
#Column(name = "commentary",nullable = false,columnDefinition = "TEXT", unique = true)
private String commentary;
#Column(name = "assessment",nullable = false,columnDefinition = "INT", unique = true)
private String assessment;
#ManyToOne
#JoinColumn(name = "id_routine_assignament")
private RoutineAssignament routineAssignament;
}
#Entity(name = "statistic")
#Table(name = "statistic")
public class Statistic {
#Id
#GeneratedValue(strategy = IDENTITY)
#Column(name = "id", updatable = false)
private Long id;
#Column(name = "time",nullable = false,columnDefinition = "TEXT", unique = true)
private String time;
#ManyToOne
#JoinColumn(name = "id_routine_assignament")
private RoutineAssignament routineAssignament;
}
and
#Entity(name = "keep_routine")
#Table(name = "keep_routine")
public class KeepRoutine {
#Id
#GeneratedValue(strategy = IDENTITY)
#Column(name = "id", updatable = false)
private Long id;
#ManyToOne
#JoinColumn(name = "id_routine_assignament")
private RoutineAssignament routineAssignament;
}
The entity relationship diagram is this:
My mistake is that it doesn't detect these relationships correctly.
When I run it it generates this:
Failed to initialize JPA EntityManagerFactory: mappedBy reference an unknown target entity property: com.example.demo.model.entities.KeepRoutine.routine_assignament in com.example.demo.model.entities.RoutineAssignament.keepRoutines
This error is reproduced with all three classes (KeepRoutine, Statistic and Score), I don't know why
Your OneToMany mapping is not appropriate. You need to use routineAssignament the property name instead of the table name routine_assignament as shown below. This property name is defined in the ManyToOne relationship.
#OneToMany(mappedBy = "routineAssignament")
private Set<Score> scores = new HashSet<>();
#OneToMany(mappedBy = "routineAssignament")
private Set<Statistic> statistics = new HashSet<>();
#OneToMany(mappedBy = "routineAssignament")
private Set<KeepRoutine> keepRoutines = new HashSet<>();

Hibernate unexpected deletes when finding entities

We have a Java ee application running on JBoss 6.4 GA using JPA and Hibernate with the following entities:
#Entity
#SequenceGenerator(name = "sequence", sequenceName="SEQ_CAMPAIGNS_ID",allocationSize = 1)
#Table(name = "CAMPAIGN")
public class CampaignEntity implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "sequence")
#Column(name = "ID")
private Long id;
#Column(name = "NAME")
private String name;
#Column(name = "IS_ACTIVE", nullable = false)
private boolean active;
#Column(name = "START_DATE", nullable = false)
private Date startDate;
#Column(name = "END_DATE", nullable = false)
private Date endDate;
#Column(name = "LEGAL_ENTITY_ID", nullable = false)
private Integer legalEntityId;
#Column(name = "DEPARTMENT", nullable = false)
#Enumerated(value = EnumType.STRING)
private Department department;
#Column(name = "CATEGORY", nullable = false)
#Enumerated(value = EnumType.STRING)
private Category category;
#Embedded
CampaignConditionsEntity campaignConditions;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "campaign", orphanRemoval = true)
#OrderBy
private List<CodeEntity> campaignCodes;
public CampaignEntity() {
}
And the following CampaignConditionsEntity:
#Embeddable
public class CampaignConditionsEntity implements Serializable {
private static final String CAMPAIGN_ID = "CAMPAIGN_ID";
#ElementCollection(fetch = FetchType.EAGER)
#CollectionTable(name = "CAMPAIGN_COND_TRIP_TYPE", joinColumns = #JoinColumn(name = CAMPAIGN_ID))
private Set<TripTypeConditionEntity> tripTypeConditions;
And the following CodeEntity:
#Entity
#Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
#SequenceGenerator(name = "sequence", sequenceName = "SEQ_CODES_ID", allocationSize = 1)
public abstract class CodeEntity implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "sequence")
#Column(name = "ID", nullable = false)
private Long id;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "CAMPAIGN_ID")
private CampaignEntity campaign;
#OneToOne(mappedBy = "code", cascade = CascadeType.ALL, fetch = FetchType.LAZY, optional = false, orphanRemoval = true)
private DiscountEntity discount;
#Column(name = "MAX_USAGES", nullable = false)
private Integer maxUsages;
#Column(name = "UNLIMITED_USAGES", nullable = false)
private boolean unlimitedUsages;
#Column(name = "NEGATIVE_SH", nullable = false)
private boolean negativeSH;
#Column(name = "UNIQUE_BUYER", nullable = false)
private boolean uniqueBuyer;
#Column(name = "START_DATE")
private Date startDate;
#Column(name = "END_DATE")
private Date endDate;
#Embedded
private CodeConditionsEntity codeConditions;
public CodeEntity() {
}
This is the CodeConditionsEntity:
#Embeddable
public class CodeConditionsEntity implements Serializable {
private static final String CODE_ID = "CODE_ID";
#ElementCollection(fetch = FetchType.EAGER)
#CollectionTable(name = "CODE_COND_TRIP_TYPE", joinColumns = #JoinColumn(name = CODE_ID))
private Set<TripTypeConditionEntity> tripTypeConditions;
#ElementCollection(fetch = FetchType.EAGER)
#CollectionTable(name = "CODE_COND_CARRIERS", joinColumns = #JoinColumn(name = CODE_ID))
private Set<CarrierConditionEntity> carrierConditions;
This is the CarrierConditionEntity:
#Embeddable
public class CarrierConditionEntity implements Serializable {
#Column(name = "CARRIER", nullable = false, length = 3)
private String carrierCode;
#Column(name = "IS_INCLUDED", nullable = false)
private boolean included;
The problem is that in the logs we are finding unexpected deletes when the only operation that we are doing are finds of particular campaign entities.
In the production logs we find the following deletes
Hibernate: delete from CODE_COND_CARRIERS where CODE_ID=? and CARRIER=? and IS_INCLUDED=?
do you have any suggestion?
thanks
I have some suggestions :)
Be aware of what is a Persistence Context (EntityManager instance in JPA terminology / Session in Hibernate one), the entity lifecycle and transaction scope (unit of work)
Do not mutate entity state if you don't expect the changes to be reflected in database, or at least detach the entity before mutating it.
Mark your transaction as "readOnly" if you only fetch data in the related unit of work. (beware that if you have many "Transactional" methods joining the same physical transaction, the flag is set by the surrounding one and cannot be overridden by inner logical transactions). That way the EntityManager won't be flushed at the end of the transaction and pending changes won't be persisted to the database.
You can track the method triggering the unexpected deletion using an EntityListener on the related entity and printing the current strackTrace (new Throwable().printStackTrace()/ log(new Throwable()) in the PreRemove method
I found where was the problem:
The problem was that the Entities didn't have the equals() and the hashcode() implemented. Also there were entities that have a #PostLoad that modified the entity after loading it from database. Then in this situation Hibernate though that there was a change in those entities that didn't have the equals and the hashcode, and then it delete all of them and inserted again in the database (to have the same entities before the query)
Adding the equals and hashcode methods and deleting postload removed the unexpected deletes and inserts from the logs.
regards

Hibernate - Populate sub entity on insert

Ive added 2 hibernate model objects
First table
#Entity
#Table(name = "ACTIVITIES")
public class ActivityMO extends ModelBase {
#Column(name = "CA_ID", nullable = false, insertable = true,updatable = true, length = 22, precision = 0)
#Id
#GeneratedValue(strategy = GenerationType.AUTO, generator = "G1")
#SequenceGenerator(name = "G1", sequenceName = "CSM_ACTIVITIES_SEQ")
private Long id;
#OneToMany(fetch = FetchType.LAZY, mappedBy = "activityId", cascade = {CascadeType.ALL})
#Fetch(FetchMode.JOIN)
List<ActivitiesProductsMO> relatedProducts;
...getters / setters
}
The other table is
#Entity
#Table(name = "ACTIVITIES_PRODUCTS")
public class ActivitiesProductsMO {
#Column(name = "CAP_ID")
#Id
#GeneratedValue(strategy = GenerationType.AUTO, generator = "G1")
#SequenceGenerator(name = "G1", sequenceName = "ACTIVITIES_PRODUCTS_SEQ")
private Long id;
#Column(name = "CAP_ACTIVITY_ID")
private Long activityId;
#Column(name = "CAP_PRODUCT_ID")
private Long productId;
...getters/setters
}
The point is to populate each db record for ActivitiesProductsMO.activityId with ActivityMO.id value
I.e.
If I create an activity record with id = 555
I'll get another activity_product record with activityId of 555
How can i get this to work?
Thank you!
Instead of manually trying to map the entitiy relations with long values you should use a bidirectional OneToMany relationship from ActivityMO to ActivitiesProductsMO
change ActivitiesProductsMO to:
#Entity
#Table(name = "ACTIVITIES_PRODUCTS")
public class ActivitiesProductsMO {
// cut unimportant code ...
#ManyToOne
#JoinColumn(name = "CAP_ACTIVITY_ID")
private ActivityMO activityId;
// cut unimportant code ...
}
If you then were to persist an ActivityMO that already has ActivitiesProductsMO entries in its relatedProducts List, the Cascade type should actually take care and create those products while filling out the CAP_ACTIVITY_ID database field with the right value.
Another Possible Solution:
Use a Unidirectional OneToMany:
#Entity
#Table(name = "ACTIVITIES")
public class ActivityMO extends ModelBase {
#OneToMany(fetch = FetchType.LAZY, cascade = {CascadeType.ALL})
#Fetch(FetchMode.JOIN)
#JoinColumn(name = "CAP_ACTIVITY_ID")
List<ActivitiesProductsMO> relatedProducts;
}
And remove the
private Long activityId;
from your ActivitiesProductsMO class.
This should both lead to identical database structure. But in the second case you would no longer have the "backlink" inside java from ActivitiesProductsMO to ActivityMO

Using JPA annotations with Hibernate to describe #OneToMany relationship where foreign key is only in child table

I have an existing data model that I'm very happy with:
public class Garden {
private String name; // "Oak Grove"
private List<Plant> plants;
}
public class Plant {
private String name; // "Cherry Tomato"
}
I would like to map this in Hibernate with the following conditions:
The Plant class in Java does not maintain a reference to its parent Garden. This makes things more difficult in the Java tier, IMO.
The PLANT table should have a GARDEN_ID column which is a foreign key to the GARDEN(ID) column.
My initial setup, prior to the #OneToMany addition:
#Entity(name = "GARDEN")
public class Garden {
#Id
#Column(name = "ID", nullable = false)
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
#Column(name = "NAME", nullable = false)
private String name; // "Oak Grove"
// Not yet mapped
private List<Plant> plants;
}
#Entity(name = "PLANT")
public class Plant {
#Id
#Column(name = "ID", nullable = false)
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
#Column(name = "NAME", nullable = false)
private String name; // "Cherry Tomato"
}
How can I define the #OneToMany annotation on the List<Plant> plants; in such a way that the foreign key reference is maintained in the Plant?
If I just add:
#OneToMany(cascade = { CascadeType.ALL })
#JoinColumn(name = "GARDEN_ID")
private List<Plant> plants;
Then saving a garden with a plant fails like so:
Caused by: org.h2.jdbc.JdbcSQLException: NULL not allowed for column "GARDEN_ID"; SQL statement:
insert into PLANT (NAME, ID) values (?, ?) [23502-191]
So it seems Hibernate isn't trying to persist the foreign key. Is there a way to accomplish this without completely mucking up my object model?
Edit: The way I'm testing this is with:
Garden garden = new Garden("Oak Grove");
garden.addPlant(new Plant("Cherry Tomato"));
gardenManager.save(garden);
In which the save() method looks very Hibernate-ey:
public void save(T item) {
try (Session session = factory.openSession()) {
Transaction transaction = session.beginTransaction();
try {
session.saveOrUpdate(item);
transaction.commit();
} catch (Exception ex) {
System.out.println("Error occurred saving item: " + ex.getMessage());
ex.printStackTrace();
transaction.rollback();
}
}
}
Yogesh Sakurikar was close, but the bi-directional #JoinColumn was a bit off. Below you will see how to join bidirectionally or mono-riectionally
#Entity(name = "GARDEN")
public class Garden {
#Id
#Column(name = "ID", nullable = false)
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
#Column(name = "NAME", nullable = false)
private String name; // "Oak Grove"
// use this if you don't want a bi-directional relationship
// #OneToMany
// #JoinColumn(name = "ID", referencedColumnName="GARDEN_ID")
// private List<Plant> plants;
// use this if you want it bi-directional
#OneToMany(fetch = FetchType.LAZY, mappedBy = "garden")
private Set<Plant> plants;
}
#Entity(name = "PLANT")
public class Plant {
#Id
#Column(name = "ID", nullable = false)
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
#Column(name = "NAME", nullable = false)
private String name; // "Cherry Tomato"
// use this if you don't want a bi-directional relationship
// #Column(name="GARDEN_ID")
// private long gardenId;
// use this if you want a bi-directional relationship
#ManyToOne
#JoinColumn(name = "GARDEN_ID", referencedColumnName="ID", nullable = false)
private Garden garden;
}
The code below assumes bi-directional relationship. Otherwise you'd need to know your Garden.id before you could fully describe any child Plant
Garden garden = new Garden("Oak Grove");
Plant plant = new Plant("Cherry Tomato")
plant.setGarden(garden); //don't forget to set the parent on the child
garden.addPlant(plant);
gardenManager.save(garden);
For one to many relationship, if the plant will going to hold the relationship, you need to define it using bi-direction.
Here is I think you should be able to achieve it:
#Entity(name = "GARDEN")
public class Garden {
#Id
#Column(name = "ID", nullable = false)
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
#Column(name = "NAME", nullable = false)
private String name; // "Oak Grove"
#OneToMany(fetch = FetchType.LAZY, mappedBy = "garden")
private List<Plant> plants;
}
#Entity(name = "PLANT")
public class Plant {
#Id
#Column(name = "ID", nullable = false)
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
#Column(name = "NAME", nullable = false)
private String name; // "Cherry Tomato"
#ManyToOne
#JoinColumn(name = "GARDEN_ID", nullable = false)
Garden garden;
}
This bidirectional approach will let entity manager know that there exists a relationship between the two and since on one side it is one to many, for other side it become many to one. Hope this will help.

Categories