ID from parent to child in hibernate - java

Here is my code. I would like to generate an automatic ID based on parent class. I'm using a method to create Airport, so my ID it's coming with is a null value. ID in AirportModel will be generated, but I don't know how to make it in child class.
#Entity(name = "Airport")
#Table(name = "ai_airport")
public class AirportModel {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id", nullable = false)
private Long id;
#OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
#JoinColumn(name = "airport_id")
private List<AirportTranslatedModel> translations;
Second class(child):
#Entity(name = "AirportTranslated")
#IdClass(AirportTranslatedModelKey.class)
#Table(name = "ai_translated_airport")
public class AirportTranslatedModel
#Id
#Column(name="airport_id")
private Long airportId;
#Id
#Column(name="language_code", length=2)
private String languageCode;
Third one(keys):
#Embeddable
public class AirportTranslatedModelKey implements Serializable {
#Column(name="airport_id")
private Long airportId;
#Column(name="language_code", length=2)
private String languageCode;
I still got the same errors; log:
Hibernate: insert into ai_airport (active, airport_code, city_code, country_code, externa
l_id, is_default, latitude, longitude, market_code, min_connection_time_DD, min_connection_time_DI, min_connection_time_id, min_connection_time_II, time_diff, VERSION) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
Hibernate: insert into ai_translated_airport (airport_long_name, airport_short_name, airp
ort_id, language_code) values (?, ?, ?, ?)
ERROR org.hibernate.engine.jdbc.spi.SqlExceptionHelper - Column 'airport_id' cannot be null

Your current setup has the AirportTranslatedModel airport_id field mapped through a Long- you will need to set the airportId manually to have it set the id in the database. This will likely require that you persist AirportModel and possibly flush to have its PK assigned and available prior to making the AirportModel->AirportTranslatedModel association, so that you can then set the AirportTranslatedModel.airportId.
JPA 2 though allows derived Ids. If you want AirportTranslatedModel to have its ID assigned from AirportModel, it needs to have a relationship to it. There is a simple example at http://wiki.eclipse.org/EclipseLink/Examples/JPA/2.0/DerivedIdentifiers
If you were to model your classes in a similar fashion, it might look like:
public class AirportModel {
..
#OneToMany(mappedby="airportModel", cascade = CascadeType.ALL, orphanRemoval = true)
private List<AirportTranslatedModel> translations;
..
}
public class AirportTranslatedModel {
#Id
#JoinColumn(name="airport_id")
private AirportModel airportModel;
#Id
#Column(name="language_code", length=2)
private String languageCode;
..
}
public class AirportTranslatedModelKey implements Serializable {
private Long airportModel;
private String languageCode;
}
Notice that there is no need to make the AirportTranslatedModelKey and embeddable if you are just using it as a pk class. Also note that the AirportTranslatedModelKey contains a Long airportModel - this must match the type of the pk in the AirportModel, and the name of the relationship property in AirportTranslatedModel.
This will allow AirportTranslatedModel to pull the airport_id value from AirportModel and use it as its PK even though it might not have been generated yet when both entities are still new.

Related

Hibernate uploads data with #OneToMany and #ManyToOne but the foreign key remains Null

Code for Module:
#Data
#NoArgsConstructor
#ToString
#Entity(name = "modules")
#Table(name = "modules")
public class Module {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "module_id")
private int moduleId;
#Column(name = "module_name")
private String moduleName;
#Column(name = "module_code")
private String moduleCode;
#Column(name = "moderator_lecturer")
private String moderatorLecturerId;
#Column(name = "secondary_lecturer")
private String secondaryLecturerId;
#OneToMany(mappedBy = "foreignModuleId", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
private List<Assessment> assessments;
public void addAssessment(Assessment assessment) {
assessments.add(assessment);
}
}
Code for Assignment
#Data
#NoArgsConstructor
#ToString
#Entity(name = "assessments")
#Table(name = "assessments")
public class Assessment {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "assessment_id")
private int assessmentId;
#Enumerated(EnumType.STRING)
#Column(name = "assessment_type")
private AssessmentType assessmentType;
#Column(name = "assessment_weight")
private int assessmentWeight;
#Column(name = "assessment_weeks")
private String weeks;
#Column(name = "assessment_upload_date")
private LocalDate uploadDate;
#Column(name = "assessment_deadline_date")
private LocalDate deadlineDate;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "assessment_belongsTo_module", referencedColumnName = "module_id")
private Module foreignModuleId;
}
A Module can have many Assessments hence why I chose these annotations.
I firstly extract those data from an excel file and combine them in a list (that is passed later on as an argument called "modules"). The list is of the form:
Module(moduleId=0, moduleName=Programming Principles and Algorithms , moduleCode= CCS1110, moderatorLecturerId=Dr Stamatopoulou, secondaryLecturerId= Dr Efremidis, assessments=[Assessment(assessmentId=0, assessmentType=ASSESSED_LAB, assessmentWeight=35, weeks=00001000000000000, uploadDate=null, deadlineDate=null, foreignModuleId=null), Assessment(assessmentId=0, assessmentType=ASSESSED_LAB, assessmentWeight=65, weeks=00000000000000001, uploadDate=null, deadlineDate=null, foreignModuleId=null)])
Module(moduleId=0, moduleName=Programming Methodology and Design, moduleCode= CCS1115, moderatorLecturerId=Dr Stamatopoulou, secondaryLecturerId= Dr Efremidis, assessments=[Assessment(assessmentId=0, assessmentType=PROJECT, assessmentWeight=35, weeks=00000000000000100, uploadDate=null, deadlineDate=null, foreignModuleId=null), Assessment(assessmentId=0, assessmentType=ASSESSED_LAB, assessmentWeight=65, weeks=00000000000000001, uploadDate=null, deadlineDate=null, foreignModuleId=null)])
Then I upload the list on the database:
#NoArgsConstructor
#Data
public class AppDAOImpl implements AppDAO{
private SessionFactory factory;
public void upload(List<com.project.model.Module> modules) {
Session currentSession = factory.getCurrentSession();
try {
currentSession.beginTransaction();
for(Module module : modules) {
currentSession.save(module);
}
currentSession.getTransaction().commit();
}
finally {
currentSession.close();
factory.close();
}
}
}
When I execute Hibernate create queries of form:
Hibernate: insert into modules (moderator_lecturer, module_code, module_name, secondary_lecturer) values (?, ?, ?, ?)
Hibernate: insert into assessments (assessment_type, assessment_weight, assessment_deadline_date, assessment_belongsTo_module, assessment_upload_date, assessment_weeks) values (?, ?, ?, ?, ?, ?)
Hibernate: insert into assessments (assessment_type, assessment_weight, assessment_deadline_date, assessment_belongsTo_module, assessment_upload_date, assessment_weeks) values (?, ?, ?, ?, ?, ?)
But in the database on the table for Assessments, the field assessment_belongsTo_module is null.
My database has this form:
I have tried a lot of things and cannot fix the problem. I have also read similar threads and still nothing. Maybe there is a problem in the way I have created the fields on each table in the db (e.g. the foreign key)?
Do not use the name Module for a class, say MModule. It is confused with the java.lang.Module.
Create JpaRepository MModuleRepository. Then in a controller, write simply something like
for(MModule module :modules){
moduleRepository.save(module);
}
The problem that was causing this was related to the fact that I did not initialize the attribute "foreignModuleId" in the Assessment class. So when I extract the data from my excel at some point I have the lines:
assessment.setForeignModuleId(module); // I add the module in which the assessment belongs.
module.addAssessment(assessment); // I then add that assessment to the #OneToMany assessments List
I also fixed a stackOverFlow exception that was caused whenever I did operations like retrieving those data from the db, by including this in the Assessment class:
#ToString.Exclude
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "assessment_belongsTo_module", referencedColumnName = "module_id")
private Module foreignModuleId = new Module();
It is very important because Lombok's toString causes a recursive call which then results in the previously mentioned exception.

Storing superclass with inheritance JOINED strategy (Hibernate migration)

I have migrated from Hibernate 4.2.2 to 5.1.2 and now I have a problem storing superclass with #Inheritance(strategy = InheritanceType.JOINED).
My Promotion entity is:
#Entity
#Table(name = "promotion")
#Inheritance(strategy = InheritanceType.JOINED)
#DiscriminatorColumn(name = "promotion_type", discriminatorType = DiscriminatorType.STRING)
#XmlRootElement
public class Promotion implements Serializable {…
…
…
#Basic(optional = false)
#Column(name = "promotion_type", nullable = false, length = 17)
protected String promotionType;
…
…
}
One of the extended classes is PromotionEvent:
#Entity
#Table(name = "promotion_event")
#PrimaryKeyJoinColumn(name = "promotion_id")
#DiscriminatorValue("event")
#XmlRootElement
public class PromotionEvent extends Promotion {
…
…
}
In Hibernate 4.2.2 all was working, but now with Hibernate 5.1.2 when a promotion is stored I get the following exception:
ERROR pool-2-thread-2 SqlExceptionHelper.logExceptions - Parameter index out of range (16 > number of parameters, which is 15).
With the insert:
insert into promotion (amount, client_id, description, end_datetime, event_end_datetime, event_start_datetime, hide_amount_at_ticket, name, percentage, promotion_type, requires_pda_alert, short_name, show_amount_original_at_ticket, start_datetime, state, status) values (?, ?, ?, ?, ?, ?, ?, ?, ?, 'event', ?, ?, ?, ?, ?, ?)
So, seems that is trying to insert de #DisciminatorValue of PromotionEvent in the assigned #DiscriminatorColumn, but #DisicriminatorColumn is #Basic and then persisted. So, here is the problem.
But, why in Hibernate 4.x this was not happening?.
I can see that it works if I make
#Basic(optional = false)
#Column(name = "promotion_type", nullable = false, insertable=false, length = 17)
protected String promotionType;
That’s to say, insertable=false, then it works and #DiscriminatorValue is inserted in promotion_type.
Moreover, if I remove #DiscriminatorColumn from Promotion, it seems that all is working, but in this case I do not know how Hibernate knows the class type (with JOINED strategy I guess).
Should it be recommended to put insertable=false?.
Should it be better remove #DiscriminatorColumn if all it’s working?.
Can not be #DiscriminatorColumn a #Basic and insertable attribute?,
so why in H4.2.2 there was no problem?
Thank you.

jpa unidirectional One-to-Many with a Collection of Abstract type

I'm having an issue with JPA. Basically, what I have is an entity with a list of an abstract type and I need each element of the list to be persisted in its corresponding table with a foreign key (to relate to the entity). Here is the code:
#Entity(name = "entity")
public class Entity{
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id")
private BigInteger id;
#OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
#JoinColumn(name="entity_id", referencedColumnName="id")
private List<AbstractType> types;
}
Abstract type:
#Entity
#Inheritance(strategy=InheritanceType.TABLE_PER_CLASS)
public abstract class AbstractType{
#Id
#GeneratedValue(strategy = GenerationType.TABLE)
private BigInteger id;
private String someProp;
#Column(name="entity_id")
private BigInteger entityId;
}
Type A:
#Entity(name = "type_a")
#Transactional
#Inheritance(strategy=InheritanceType.TABLE_PER_CLASS)
public class TypeA extends AbstractType{
private String prop1;
private String prop2;
}
Type B:
#Entity(name = "type_b")
#Transactional
#Inheritance(strategy=InheritanceType.TABLE_PER_CLASS)
public class TypeB extends AbstractType{
private String prop3;
private String prop4;
}
I'm having a SQL error. The generated query tries to update the table of the abstract type (which shouldnt exist). This is part of the query:
update hibernate_sequences set sequence_next_hi_value = ? where
sequence_next_hi_value = ? and sequence_name = 'abstract_type'
insert into type_a (some_prop, entity_id, prop1, prop2) values (?, ?, ?, ?)
insert into type_b (some_prop, entity_id, prop3, prop4) values (?, ?, ?, ?)
update abstract_type set entity_id=? where id=?
As you can see, it's trying to update a table which doesn't (and shouldnt) exist. 'abstract_type' table.
Thanks in advance.
Of course you can't name a class Entity without causing problems.
You can make this much simpler by minimizing the annotations and let JPA do it's work. A Container class holds a collection of AbstractTypes:
#Entity
public class Container {
#Id #GeneratedValue
private Long id;
#OneToMany(cascade=CascadeType.ALL)
private List<AbstractType> types;
public List<AbstractType> getTypes() { return types; }
public void setTypes(List<AbstractType> types) { this.types = types; }
}
The AbstractType is just that:
#Entity
public abstract class AbstractType {
#Id #GeneratedValue
private Long id;
}
And a couple concrete types to extend and inherit from the abstract superclass:
EDIT: A FK back to the Container class can be added with a ManyToOne association.
#Entity
public class TypeA extends AbstractType {
#ManyToOne
private Container container;
}
And:
#Entity
public class TypeB extends AbstractType {
#ManyToOne
private Container container;
}
When I run this Minimal, Complete, Verifiable Example I get the following output:
Hibernate: create table AbstractType (DTYPE varchar(31) not null, id bigint not null, container_id bigint, primary key (id))
Hibernate: create table Container (id bigint not null, primary key (id))
Hibernate: create table Container_AbstractType (Container_id bigint not null, types_id bigint not null)
Hibernate: insert into Container (id) values (?)
Hibernate: insert into AbstractType (container_id, DTYPE, id) values (?, 'TypeA', ?)
Hibernate: insert into AbstractType (container_id, DTYPE, id) values (?, 'TypeB', ?)
Hibernate: insert into Container_AbstractType (Container_id, types_id) values (?, ?)
Hibernate: insert into Container_AbstractType (Container_id, types_id) values (?, ?)
Hibernate: select container0_.id as id1_1_ from Container container0_
model.Container#40591559

Hibernate (on postgres) dropping Schema from #JoinTable

I'm using Hibernate 4.3.10.Final (with SpringData JPA) running on a Postgres 4 database and have run into a very strange bug. Our app utilizes a database outside of the default "public" schema, and when we try to insert data Hibernate drops the correct schema.
Our model consists of an abstract "Log" class that uses single class inheritance to allow many different object types to insert a associated log message. See code below.
The schema already exists (hibernate doesn't create it) and booting validation runs fine, but when try to insert a new record we get the error relation "booking_log" does not exist -- which is missing the schema modifier (say customapp for our purposes). See the first line from the logs below to get an idea of what other insert statements look like.
I've dug through the mapping phase and verified Hibernate is indeed picking up the schema from the #JoinTable annotation, but not sure how we're losing it.
Any help debugging or possible solutions would be appreciated.
Thanks!
Log - Abstract super class
#MappedSuperclass
#Table(name="log", schema=Constants.DB_SCHEMA)
#Inheritance(strategy=InheritanceType.SINGLE_TABLE)
#DiscriminatorColumn(name="log_type_id", discriminatorType = DiscriminatorType.INTEGER)
public abstract class Log {
#Id
#GeneratedValue(strategy=GenerationType.SEQUENCE, generator="log_seq_gen")
#SequenceGenerator(allocationSize = 1, name="log_seq_gen", sequenceName=Constants.DB_SCHEMA + ".log_id_seq")
private Long id;
// ...
}
BookingLog
#Entity
#DiscriminatorValue("2")
public class BookingLog extends Log implements TenantResource<Company,Long> {
#JoinTable(name="booking_log",
schema = Constants.DB_SCHEMA,
joinColumns = { #JoinColumn(name="log_id", referencedColumnName="id", nullable=false, updatable=false)},
inverseJoinColumns = { #JoinColumn(name="booking_id", referencedColumnName="id", nullable=false, updatable=false)})
#ManyToOne(cascade=CascadeType.ALL)
private Booking booking;
///...
}
** Logs **
2015-07-20_18:14:09.055 DEBUG org.hibernate.SQL - insert into customapp.booking_product (created_dt, created_by, modified_dt, modified_by, include_in_payroll, include_in_revenue, booking_id, description, payroll_percent, price, product_id, qty, id) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
2015-07-20_18:14:09.072 DEBUG org.hibernate.SQL - insert into booking_log (log_date, details, log_time, user_id, booking_id, id) values (?, ?, ?, ?, ?, ?)
2015-07-20_18:14:09.176 DEBUG o.h.e.jdbc.spi.SqlExceptionHelper - could not execute statement [n/a]
org.postgresql.util.PSQLException: ERROR: relation "booking_log" does not exist
Based on your #JoinTable configuration and the insert statement that hibernate generates looks like the problem is the way you are triyng to add extra fields/data to the booking_log table.
I would need more details about your model to be sure but I think you are using a join-table and something else instead of create a class that models the join-table.
I mean, you have this
BookingLog (*) --------------------------------------> (1) Booking
but I think you really need this
BookingLog (1) ---> (1) BookingLogAssociation (*) ---> (1) Booking
Then the mapping will be like this,
#Entity
#DiscriminatorValue("2")
public class BookingLog extends Log implements TenantResource<Company,Long> {
#OneToOne(mappedBy="bookingLog")
private BookingLogAssociation booking;
}
Note the attributes of BookingLogAssociation, they are the extra field/data you want to add in booking_log table.
#Entity
#Table(name="booking_log")
#IdClass(BookingLogAssociationId.class)
public class BookingLogAssociation {
#Id
private long log_id;
#Id
private long booking_id;
#OneToOne
#PrimaryKeyJoinColumn(name="log_id", referencedColumnName="id")
private BookingLog bookingLog;
#ManyToOne
#PrimaryKeyJoinColumn(name="booking_id", referencedColumnName="id")
private Booking booking;
#Column(name="log_date")
#Temporal(TemporalType.DATE)
private Calendar logDate;
#Column(name="log_time")
#Temporal(TemporalType.TIME)
private Calendar logTime;
#ManyToOne
#JoinColumn(name="user_id")
private User user;
// Could be just an attribute too
//#Column(name="user_id")
//private long userId;
...
}
The BookingLogAssociationId class that represents the BookingLogAssociation's composite key.
public class BookingLogAssociationId implements Serializable {
private long log_id;
private long booking_id;
public int hashCode() {
return (int)(log_id + booking_id);
}
public boolean equals(Object object) {
if (object instanceof BookingLogAssociationId) {
BookingLogAssociationId otherId = (BookingLogAssociationId) object;
return (otherId.log_id == this.log_id) && (otherId.booking_id == this.booking_id);
}
return false;
}
You can read more about this option here

One to many map in Hibernate - error

can anyone tell me where is the error in this example
#Entity
#Table(name = "ITEM")
public class Item implements Serializable
{
#Id
#GeneratedValue
#Column(name = "ID")
private Long id;
#Column(name = "NAME")
private String name;
#OneToMany(cascade = CascadeType.ALL)
#JoinColumn(name = "ID_ITEM",referencedColumnName="ID")
private List<ItemDetail> itemDetails;
second class
#Entity
#Table(name = "ITEM_DETAIL")
public class ItemDetail implements Serializable
{
#Id
#GeneratedValue
#Column(name = "ID")
private Long id;
#Column(name = "NAME")
private String name;
#Column(name = "ID_ITEM")
private Long itemId;
and the db
COMMIT;
CREATE TABLE item(
id serial PRIMARY KEY,
name VARCHAR(16)
);
CREATE TABLE item_detail(
ID serial PRIMARY KEY,
NAME VARCHAR(16),
ID_ITEM serial REFERENCES item (id)
);
COMMIT;
The error i got is
Hibernate: select nextval ('hibernate_sequence')
Hibernate: select nextval ('hibernate_sequence')
Hibernate: select nextval ('hibernate_sequence')
Hibernate: insert into ITEM (NAME, ID) values (?, ?)
Hibernate: insert into ITEM_DETAIL (ITEM_ID, NAME, ID) values (?, ?, ?)
Hibernate: insert into ITEM_DETAIL (ITEM_ID, NAME, ID) values (?, ?, ?)
Exception in thread "main" org.hibernate.exception.SQLGrammarException: Could not execute JDBC batch update
at org.hibernate.exception.SQLStateConverter.convert(SQLStateConverter.java:90)
at org.hibernate.exception.JDBCExceptionHelper.convert(JDBCExceptionHelper.java:66)
at org.hibernate.jdbc.AbstractBatcher.executeBatch(AbstractBatcher.java:275)
at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:266)
at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:167)
at org.hibernate.event.def.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:321)
at org.hibernate.event.def.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:50)
at org.hibernate.impl.SessionImpl.flush(SessionImpl.java:1027)
at org.hibernate.impl.SessionImpl.managedFlush(SessionImpl.java:365)
at org.hibernate.transaction.JDBCTransaction.commit(JDBCTransaction.java:137)
at com.mkyong.common.App.main(App.java:51)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:115)
Caused by: java.sql.BatchUpdateException: Batch entry 0 insert into ITEM_DETAIL (ITEM_ID, NAME, ID) values (NULL, id1, 161) was aborted. Call getNextException to see the cause.
at org.postgresql.jdbc2.AbstractJdbc2Statement$BatchResultHandler.handleError(AbstractJdbc2Statement.java:2530)
at org.postgresql.core.v3.QueryExecutorImpl.processResults(QueryExecutorImpl.java:1317)
at org.postgresql.core.v3.QueryExecutorImpl.execute(QueryExecutorImpl.java:350)
at org.postgresql.jdbc2.AbstractJdbc2Statement.executeBatch(AbstractJdbc2Statement.java:2592)
at org.hibernate.jdbc.BatchingBatcher.doExecuteBatch(BatchingBatcher.java:70)
at org.hibernate.jdbc.AbstractBatcher.executeBatch(AbstractBatcher.java:268)
... 13 more
Process finished with exit code 1
It's obviously that item_id is null but why??
Thanks
Regards
#backebg: can you check your db script and let us know that you executed exactly same in Database. If yes then either correct your entities to use "ID_ITEM" or item_detail table to use 'ITEM_ID' instead 'ID_ITEM'. thanks
Having ITEM_ID mapped as a column in ItemDetail is a bit odd, and mapping it that way might might be the source of the problem. Nothing is telling the ItemDetail class that that field should be populated with a proper id for the parent Item, including that it shouldn't be null.
If the detail doesn't need to know about the parent, you might be able to just omit that field in the ItemDetail java code altogether. The field in the table should be populated as a consequence of the relation.
It's more common to map this sort of thing as a bidirectional association, so that you have a #OneToMany relation of Item to ItemDetail and a #ManyToOne relation of ItemDetail to Item, and the relations can be navigated in Java. If the ItemDetail does need to know about the parent item, you should do it this way.
This is described somewhere in the Hibernate Annotations Reference section on mapping associations.
Use nullable = false to tell Hibernate that the join column cannot be null:
#Entity
#Table(name = "ITEM")
public class Item implements Serializable {
// ...
#OneToMany(cascade = CascadeType.ALL)
#JoinColumn(name = "ID_ITEM", referencedColumnName = "ID", nullable = false)
private List<ItemDetail> itemDetails;
// ...
}
and remove the itemId property from ItemDetail as it is already mapped by the #JoinColumn annotation. If you need the itemId, then use a bi-directional relationship (hold a reference to the entire Item object, not just the ID).

Categories