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.
Related
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.
I am using Hibernate / JPA 2.1 for persistence in my project. I try to save a FinancialInstrument, which has an embedded field interestRate, which has several PlanSteps The following mapping is set up:
#Entity
#Table(name = "tbl_financial_instrument")
public class FinancialInstrument {
#Embedded
private InterestRate interestRate;
// ...
}
#Embeddable
public class InterestRate {
#OneToMany(fetch = FetchType.LAZY)
#JoinColumn(name = "ir_id")
private Set<InterestPaymentPlanStep> interestPaymentPlanSteps = new LinkedHashSet<>();
// ...
}
The plan steps can be reused by different classes, I use inheritance here (single table type).
#Entity
#DiscriminatorValue("INTEREST_PAYMENT")
public class InterestPaymentPlanStep extends PlanStep {
// ...
}
#Entity
#Table(name = "tbl_plan_step")
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
#DiscriminatorColumn(name = "PLAN_STEP_TYPE", discriminatorType = DiscriminatorType.STRING)
public abstract class PlanStep extends AbstractBaseObject {
// ...
}
I have filled a Financial Instrument with an InterestRate, containing plan steps. Now I'm trying to persist everything to the database I use this code:
private void persistFinancialInstrument(FinancialInstrument financialInstrument) {
financialInstrument = financialInstrumentRepository.save(financialInstrument);
if (financialInstrument.getInterestRate() != null) {
Set<InterestPaymentPlanStep> interestRatePaymentPlanSteps = financialInstrument.getInterestRate().getInterestPaymentPlanSteps();
for (PlanStep planStep : interestRatePaymentPlanSteps) {
planStepRepository.save(planStep);
}
}
}
The strange thing now is that when I turn on the logging of my queries I see that it executed 2 queries for persisting the plan steps:
This is the first one, note that it does not contain the ID of the financial instrument while I already would expect it here:
insert into tbl_plan_step (creation_datetime, modification_datetime, amount, anchor_date, cap, cycle, floor, rate_add, rate_value, plan_step_type, id) values (?, ?, ?, ?, ?, ?, ?, ?, ?, 'INTEREST_PAYMENT', ?)
And the second one:
update tbl_plan_step set ir_id=? where id=?
I have no clue why the saving is being executed in 2 queries. Can anyone find an explanation for this?
This likely is due to PlanStep not having the relation to InterestRate. Make the link bidirectional.
#ManyToOne
private InterestRate interestRate
in PlanStep to perform a single insert.
see hibernate documentation for extensive explanation
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
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.
I have searched on internet and seen ideas about setting default values in entity class using hibernate annotations in mysql and I have done with setting default values to column with datatype integer only as follows.
#Column(name = "COLUMN_NAME", insertable=false, updatable = false, nullable = false, columnDefinition = "int default 1")
protected Integer test_id;
and is working fine.
But I haven't seen any example for varchar/string datatype and I have try with different types in columnDefinition like,
columnDefinition = "TEXT default `TEST`"
columnDefinition = "TEXT default TEST"
columnDefinition = "varchar(255) default `15-JUL-1980`")
columnDefinition = "varchar(255) default 15-JUL-1980")
columnDefinition = "varchar default 15-JUL-1980")
columnDefinition = "varchar default `15-JUL-1980`")
columnDefinition = "CHAR(100) default `15JUL1980`")
columnDefinition = "CHAR default `15JUL1980`")
With length attribute:-
`length=100, columnDefinition = "CHAR default `15JUL1980`")`
etc.
but in this case table is not created by hibernate, that may be due to wrong syntax.
But if I am creating the table with only integer values as above table is created.
Below line of code is working fine
#Column(name = "cname", insertable=false, updatable = false, nullable = false, columnDefinition = "varchar(255) default '15-JUL-1980'")
columnDefinition is works, but it is database dependent.
and there are many other solution(Using #PrePersist annotation, modifying 'getters'), but I recommend you set default value in DBMS, then use #DynamicInsert/#DynamicUpdate annotations in model class.
See example below(Spring data JPA + Hibernate).
// Model class
#Entity
public class Person {
private Integer age;
private String name;
private String phone;
private String address;
// setters and getters
}
// Insert method in service class
#Override
#Transactional
public Person create() {
Person createdPerson = new Person();
createdPerson.setAge(20);
createdPerson.setName("Foo");
//createdPerson.setPhone("Unknown");
//createdPerson.setAddress("Somewhere");
return personRepository.save(createdPerson);
}
Hibernate will generate following insert SQL statement.
INSERT INTO person (age, name, phone, address)
VALUES (?, ?, ?, ?)
you can see Hibernate insert unnecessary columns(phone, address), too.
but, after add #DynamicInsert annotation to Model class like below,
#Entity
#DynamicInsert
public class Person {
...
}
you can see Hibernate insert only necessary columns.
INSERT INTO person (age, name)
VALUES (?, ?)
and uninitilized columns(phone, address) set their value by DBMS default value.
simply set default value in your Java code, just initialize your variable like this - private int myColumn = 100; and String as
#Column(name="order_status" , length=10 )
private String orderStatus = "Pending";
it's work for me !! i know it's too late but i hope this will help someone !!