I am having a problem that hibernate tries to drop foreign keys that dont exist instead of the one that exists. My scenario looks like this.
I want to run a junit tests, before ever test I want to create DB and after ever test I want to drop it. For that I use hibernate create-drop property. However the tricky part is that I want to create my own tables as a way to test newly added sql and verify that it will run fine once I deploy it to the production db server. So what happens is this
Hibernate creates tables automatically
Hibernate creates foreign key relationships
Hibernate runs my drop table scripts (that succeeded since there is no data so no foreign key rule has been broken)
Hibernate runs my create table scripts
Hibernate runs my add foreign constraint scripts
Hibernate runs my insert data scripts
Test is executed
Hibernate tries to remove the foreign key and it fails.
The reason hibernate has not be able to remove it is cause it tried to remove that one that hibernate created and not the one that was created by my scripts.
Any idea how to force hibernate to find out the actual foreign key? Any way to get around this problem?
Thanks everyone
Class for which hibernate creates the table
TodoGroup.java
#Entity
#Table(name = "ToDoGroups")
public class ToDoGroup implements Serializable{
#Id
#GeneratedValue
private Long id;
#Column(name = "Name", length = 50)
private String name;
#ManyToOne
#JoinColumn(name = "UserSettingsId")
#XmlTransient
private UserSettings userSettings;
#OneToMany(mappedBy = "group", cascade = CascadeType.ALL)
private List<ToDoItem> items;
hibernate adding the constraint
alter table ToDoGroups
add constraint FK790BA1FAFE315596
foreign key (UserSettingsId)
references UserSettings
running my own tables that work fine since there is no data so I can remove what hibernate created in order to verify my sql
DROP TABLE IF EXISTS ToDoGroups;
CREATE TABLE ToDoGroups (ID BIGINT NOT NULL IDENTITY, Name VARCHAR(50) NOT NULL, UserSettingsId BIGINT NOT NULL, PRIMARY KEY (ID));
ALTER TABLE ToDoGroups ADD FOREIGN KEY (UserSettingsID) REFERENCES UserSettings (ID);
drop fk it tries to execute
alter table ToDoGroups drop constraint FK790BA1FAFE315596
java.sql.SQLException: Constraint not found FK790BA1FAFE315596 in table: TODOGROUPS in statement [alter table ToDoGroups drop constraint FK790BA1FAFE315596]
tries to remove the table which fails due to the constrain that I have set in my create.sql script
drop table ToDoGroups if exists
java.sql.SQLException: Table is referenced by a constraint in table SYS_REF_SYS_FK_808_810 table: TODOITEMS in statement [drop table ToDoGroups if exists]
Update
I have also noticed that hibernate when it first starts before it creates the tables (so this is way before my scripts are run), tries to remove foreign key in order to drop any table that exists.
So how does hibernate know what foreign key to use? It uses the same key that
first statement it executes
alter table ToDoGroups drop constraint FK790BA1FAFE315596
then it drops all of the tables
drop table ToDoGroups if exists
then it creates table
create table ToDoGroups (
id bigint generated by default as identity (start with 1),
Name varchar(50),
UserSettingsId bigint,
primary key (id)
)
then it adds the same FK
alter table ToDoGroups
add constraint FK790BA1FAFE315596
foreign key (UserSettingsId)
references UserSettings
I think my question here is how does hibernate know what FK to use. It used the same FK in the first drop statement when there was even no table. Later it used that some FK to create the relationship. Shouldn't hibernate first check if the table exists and then tries to determine what is the FK?
As far as I understand, your problem is that your own script and hibernate don't use the same constraint name.
You can specify a constraint name used by hibernate with this annotation on your relationship:
#ForeignKey(name = "fk_UserSettings")
And additionally, in your create.sql:
ALTER TABLE ToDoGroups ADD CONSTRAINT fk_UserSettings FOREIGN KEY (UserSettingsID) REFERENCES UserSettings (ID);
I think my question here is how does hibernate know what FK to use. It used the same FK in the first drop statement when there was even no table. Later it used that some FK to create the relationship. Shouldn't hibernate first check if the table exists and then tries to determine what is the FK?
The foreign key name used by hibernate is the concatenation of
"FK_" + hashcode of referenced entity name + hash code of referenced columns name on that entity.
So it is not a randomly generated key (you will see that it will change if you change your entity name). And that's how hibernate knows the name of the fk to drop (hibernate is expecting that the constraint was created by hibernate with this well known naming strategy).
Hibernate use the name of the constraint to manipulate it. It don't compare the "rule" coded in constraints associated with a table to see if the constraint is already there or not.
Related
How can i create entity class for the below table which has two foreign keys of two different tables.
CREATE TABLE `flights_info` (
`airline_id` bigint(20) NOT NULL,
`flight_infoid` bigint(20) NOT NULL,
UNIQUE INDEX `UK_mnghyk14c0ufcb2gs2k6fab40`(`flight_infoid`) ,
INDEX `FKm5m2579nqtr1wele0bimvme8m`(`airline_id`) ,
CONSTRAINT `FKlda61sltnw69kxw7b0gx6sj5s` FOREIGN KEY (`flight_infoid`) REFERENCES `flight_info` (`flight_infoid`) ON DELETE RESTRICT ON UPDATE RESTRICT,
CONSTRAINT `FKm5m2579nqtr1wele0bimvme8m` FOREIGN KEY (`airline_id`) REFERENCES `airline_info` (`airline_id`) ON DELETE RESTRICT ON UPDATE RESTRICT
);
my entity class:
#Entity
public class FlightsInfo {
#Id
#JoinTable(name="AirlineInfo", joinColumns=#JoinColumn(name="airline_id"))
private AirlineInfo airline_id;
#OneToOne
#JoinColumn(name="flight_infoid")
private FlightInfo flight_infoid;
}
The problem is that your table does not have a primary key. So it's hard to point the #Id annotation at the right column. JPA however accepts tables without PKs as long as you have a unique column: https://en.wikibooks.org/wiki/Java_Persistence/Identity_and_Sequencing#No_Primary_Key
Luckily you have a unique constraint on the flight_infoid column, so there you should try to point your #Id annotation.
I've been trying to deal with some problems regarding Flyway. My situation is the following: I have two Java classes, which I'd like to migrate as two schemas. Let's name them Table and CustomTable. My java classes look like:
#Entity
public class xtable{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
//getters, setters, constructors
#Entity
public class CustomTable{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String a;
private String b;
private String c;
//getters, setters, constructors
My application.properties:
spring.flyway.url=${env.var1}
spring.flyway.user=${env.var2}
spring.flyway.password=${env.var3}
spring.jpa.hibernate.ddl-auto=validate
//If I use create-drop, hibernate creates it, but after that the validation fails
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL57Dialect
spring.logging.level.org.hibernate.SQL=debug
spring.jpa.show-sql=true
hibernate.temp.use_jdbc_metadata_defaults=true
spring.flyway.enabled=true
My build.gradle:
plugins {
id "org.flywaydb.flyway" version "5.2.4"
}
dependencies {
implementation 'org.flywaydb:flyway-core'
}
The situation is so weird, because it does not even work with the auto-generated SQL code, which I let the program create without flyway.
It looks like this:
create table custom_table (
id bigint not null,
a varchar(255),
b varchar(255),
c varchar(255),
xtable_id bigint,
primary key (id)
)
engine = InnoDB;
create table xtable (
id bigint not null,
name varchar(255),
xtable_id bigint,
primary key (id)
)
engine = InnoDB;
alter table custom_table
add constraint FKep6vooglihwraille12muox9 foreign key (xtable_id) references xtable (id);
alter table xtable
add constraint FK426q765pr4gv5wux6jaktafqk foreign key (custom_table_id) references custom_table (id);
I also don't understand why Hibernate creates one-one foreign key into each class, but the bigger problem is that I still get the error message
Schema-validation: missing table [custom_table]
I tried renaming custom_table to customtable (and also renaming the class in Java), but the error message was the same.
Have you ever met the same problem? Have you got any suggestions? I've been working on this problem for - at least - 2 days.
I looked for relevant - or seemingly identical - topics here, but I couldn't find a good solution.
Thank you.
Finally I got the problem. The problem was with inserting multiple foreign keys. (So these two lines):
alter table custom_table
add constraint FKep6vooglihwraille12muox9 foreign key (xtable_id) references xtable (id);
alter table xtable
add constraint FK426q765pr4gv5wux6jaktafqk foreign key (custom_table_id) references custom_table (id);
I couldn't figure out, though, the reason why Flyway couldn't handle this, but when I recreated the whole structure with the two tables and another one containing the proper ID's, doing exactly the same thing in the whole project, it worked.
I am trying to set a JPA mapping with JoinTable, and it seems to be ignored when Hibernate (my JPA implementation) is doing a query.
To explain the use case
Each time a user gets a page of my app, I insert a line in the USAGE_LOG table (with the id of the user and the id of the page).
Each page is related to a category (for instance: settings, orders, items, news...) and a type (for instance create, update, display, delete).
So, I have some kind of middle entity table, that links a page to: a category + a type. Like a triplet: (page, category, type)
My table structure
table USAGE_LOG (for information only, this one works well)
ID PrimaryKey
USER_ID Foreign key to column ID of table USER
USAGE_LOG_PAGE_ID Foreign key to column ID of table USER_LOG_PAGE
table USAGE_LOG_PAGE
ID PrimaryKey
URL VARCHAR
USER_ACTION_ID Foreign key to column ID of table USER_ACTION
table USER_ACTION
ID PrimaryKey
ACTION_CATEGORY_ID Foreign key to column ID of table ACTION_CATEGORY
ACTION_TYPE_ID Foreign key to column ID of table ACTION_CATEGORY
table ACTION_CATEGORY
ID PrimaryKey
NAME VARCHAR
table ACTION_TYPE
ID PrimaryKey
NAME VARCHAR
So the USER_ACTION table is a join table with the particularity that it links a USAGE_LOG_PAGE to a ACTION_CATEGORY and a ACTION_TYPE at the same time.
Also, I can have several USAGE_LOG_PAGE that are linked to the same ACTION_CATEGORY and ACTION_TYPE.
Unfortunately, I cannot change the database structure (it is legacy code).
I have tried the following Mappping on the Entity "UsageLogPage"
#ManyToOne
#JoinTable(name="action",
joinColumns=#JoinColumn(name="ID", referencedColumnName="USER_ACTION_ID"),
inverseJoinColumns=#JoinColumn(name="ACTION_CATEGORY_ID", referencedColumnName="ID"))
#Getter #Setter
private ActionCategory actionCategory;
#ManyToOne
#JoinTable(name="action",
joinColumns=#JoinColumn(name="ID", referencedColumnName="USER_ACTION_ID"),
inverseJoinColumns=#JoinColumn(name="ACTION_TYPE_ID", referencedColumnName="ID"))
#Getter #Setter
private ActionType actionType;
(I use Lombok for #Getter and #Setter)
This mapping compiles, but when I try to get data, I have the following exception:
com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: Unknown column 'usagelogpa0_.actionCategory' in 'field list'
Indeed, the Hibernate query is:
select usagelogpa0_.ID as ID1_80_0_,
usagelogpa0_.actionCategory as actionCa2_80_0_,
usagelogpa0_.actionType as actionTy3_80_0_,
usagelogpa0_.URL as URL5_80_0_
from usage_log_page usagelogpa0_
where usagelogpa0_.ID=?
(the key part is the "actionCategory" and "actionType" in the select)
This is not what I expect, Hibernate should do a join.
Have you any idea of what I did wrong?
Thanks !
After lots of investigations, I have found that:
it wasn't working as expected because I put the #ManyToOne and the #JoinTable annotations at the attribute level. I created a getter by hand and put the annotations on it, and they were taken into account
it still wasn't working correctly, because Hibernate didn't find the column "USER_ACTION_ID" on the USAGE_LOG_PAGE table, at run time. This column wasn't in the available fields, for a reason (that I coudn't find). When adding a field "usage_action_id" in the entity "UsageLogPage", it found the attribute, but refused to create the mapping because USAGE_ACTION_ID isn't a primary key.
At the end, even if I couldn't change the database, I could change the object model.
So I created the middle entity "UserAction", binded it with ManyToOne on the UsageLogPage entity, removed the attribute "actionCategory" and "actionType" from the UsageLogPage and added them as ManyToOne in the new UserAction entity.
If you have a table that acts as a middle entity for 2 different ManyToOne relationships, perhaps the best solution is to create the middle entity in your object model.
I have a table created on mysql with following sql
CREATE TABLE `ssk_cms_category_transaction_type_relation` (
`categoryId` int(11) NOT NULL,
`typeId` int(11) NOT NULL,
`createdTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`categoryId`,`typeId`),
KEY `FK_ssk_cms_category_transaction_type_relation1` (`typeId`),
CONSTRAINT `FK_ssk_cms_category_transaction_type_relation` FOREIGN KEY (`categoryId`) REFERENCES `ssk_cms_content_category` (`contentCategoryId`) ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT `FK_ssk_cms_category_transaction_type_relation1` FOREIGN KEY (`typeId`) REFERENCES `ssk_transaction_type` (`typeId`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=latin1
While trying to generate its model using hibernate persistence tool in intellij, if I check on show default relationships I get the following error, can anyone help me out in understanding this. I tried googling but no solution found
The message tells that mentioned relations of the ssk_cms_category_transa... table reference another table which is not included into the model generation. So you should select this another table including it into generation to avoid the error.
Hibernate doesn't let me do a join table with a unique field "docket", no matter if I specify the "referencedColumnName = "docket"" (i thought the idea of this property was to tell Hibernate which field to use, in case it is not the primary key).
Database tables:
student
---------------
id (PK) | docket (UNIQUE)
inscription
---------------
course_id | docket
inscription's PK is (course_id, docket)
course
---------------
id (PK)
The above problems raise with the following configuration:
On Student Entity:
#ManyToMany
#JoinTable(
name="inscription",
joinColumns=#JoinColumn(referencedColumnName = "docket", name="docket"),
inverseJoinColumns=#JoinColumn(name="course_id", referencedColumnName = "id")
)
private List<Course> studentCourses;
On Course Entity:
#ManyToMany(mappedBy = "studentCourses")
private List<Student> students;
What causes the problem is that, when project is deployed, Hibernate executes the statement:
alter table public.inscription add constraint FKp625s5r1hmlggpgeq4x2nju91 foreign key (docket) references public.student
which is (of course) incorrect, as it is not specifying that docket is a unique field.
What it should be doing is:
alter table public.inscription add constraint FKp625s5r1hmlggpgeq4x2nju91 foreign key (docket) references public.student(docket)
but I don't know how can I tell it to do so.
Any help?
Thanks in advance.
Found the answer at the official documentation.
From JPA 2.0 documentation: http://download.oracle.com/otndocs/jcp/persistence-2.0-fr-oth-JSpec/:
11.1.21 JoinColumn Annotation
The JoinColumn annotation is used to specify a column for joining an entity association or element
collection.
...
The name annotation element defines the name of the foreign key column. The remaining annotation
elements (other than referencedColumnName) refer to this column and have the same semantics as
for the Column annotation.
If the referencedColumnName element is missing, the foreign key is assumed to refer to the primary
key of the referenced table.
Support for referenced columns that are not primary key columns of the referenced table is optional.
Applications that use such mappings will not be portable.
So, perhaps, what was going on was that Hibernate does not have this feature implemented, as it is not mandatory.
What I did to fix it was to modify the inscription table, replacing each field by the corresponding primary key.
(i thought the idea of this property was to tell Hibernate which field to use, in case it is not the primary key)
Your assumption is in contradiction with the JPA 2.0 specification provided you are using Hibernate as an implementation of the JPA because the following extract states that you have to join on primary keys. It doesn't say anything about unique fields:
2.10.4 Bidirectional ManyToMany Relationships
Assuming that:
Entity A references a collection of Entity B.
Entity B references a collection of Entity A.
Entity A is the owner of the relationship.
The following mapping defaults apply:
Entity A is mapped to a table named A.
Entity B is mapped to a table named B.
There is a join table that is named A_B (owner name first). This join table has two foreign key columns. One foreign key column refers to table A and has the same type as the primary key of table A. The name of this foreign key column is formed as the concatenation of the following:
the name of the relationship property or field of entity B; "_"; the name of the primary key column in table A.
The other foreign key column refers to table B and has the same type as the primary key of table B. The name of this foreign key column is formed as the concatenation of the following: the name of the relationship property or field of entity A; "_"; the name of the primary key column in table B.
(I added the format; the text is the original quotation from the specification.)
docket is not primary key in your case and therefore you cannot join on it.