Inspection Entity:
#Entity
#Table(name="INSPECTION")
public class Inspection implements Serializable
{
...
#OneToMany(cascade={CascadeType.PERSIST, CascadeType.MERGE, CascadeType.REMOVE}, orphanRemoval=true)
#OrderColumn(name="LIST_INDEX", nullable=false)
#JoinColumn(name="INSPECTION_ID")
private List<RecommendationInstance> recommendations;
...
}
RecommendationInstance Entity
#Entity
#Table(name = "RECOMMENDATION_INSTANCE")
public class RecommendationInstance implements Serializable
{
#SequenceGenerator(name="RECOMMENDATION_INST_SEQ_GEN", sequenceName="RECOMMENDATION_INST_SEQ", allocationSize=1, initialValue=100)
#Id #GeneratedValue(generator="RECOMMENDATION_INST_SEQ_GEN", strategy=GenerationType.SEQUENCE)
private Long id;
#Column(name="INSPECTION_ID")
private Long inspectionId;
#ManyToOne
#JoinColumn(name="RECOMMENDATION_ID")
private Recommendation recommendation;
#Column(name="DESCRIPTION")
private String description;
...
}
And the table is created as follows:
CREATE TABLE "RECOMMENDATION_INSTANCE"
( "ID" NUMBER(19,0) NOT NULL,
"INSPECTION_ID" NUMBER(19,0) NOT NULL,
"RECOMMENDATION_ID" NUMBER(19,0) NOT NULL,
"DESCRIPTION" VARCHAR2(4000 BYTE) NOT NULL,
"LIST_INDEX" NUMBER(4,0) NOT NULL
) ;
When a new RecommendationInstance is created and I attempt to save the InspectionEntity I get the following error:
Caused by: org.eclipse.persistence.exceptions.DatabaseException:
Internal Exception: java.sql.SQLIntegrityConstraintViolationException: integrity constraint violation: NOT NULL check constraint; SYS_CT_10161 table: "RECOMMENDATION_INSTANCE" column: "LIST_INDEX"
Error Code: -10
Call: INSERT INTO RECOMMENDATION_INSTANCE (ID, DESCRIPTION, INSPECTION_ID, RECOMMENDATION_ID) VALUES (?, ?, ?, ?)
bind => [102, Sprinkler System DESCRIPTION, 110, 40]
Am I missing some relationship here? It looks as though the list_index is being ignored completely.
To give further information, if needed, I did have this working using a join table. However I am doing a refactor since the join table is not needed. This moved the LIST_INDEX column from the join table to the RecommendationInstance table.
I have done this before but using the #OrderBy annotation, for instance, an piece of code I wrote recently:
#OneToMany(mappedBy = "product")
#OrderBy("createdDateTime ASC")
private Collection<SkuUpc> skuUpcs;
Where SkuUpc has a fied
#Column(name = "created_dt")
private Date createdDateTime = new Timestamp(new Date().getTime());
I found that when I removed the NOT NULL constraint then everything worked (duh), but I decided I can deal with that for now. Looking at the logs, JPA first inserts the row without the list_index (thus the constraint violation) then immediately after runs an update to set the list_index.
This answer really creates a more specific question as to why it doesn't set the list_index upon insertion of the row, even when I specify nullable=false
I asked the more specific question here: Why does JPA update the OrderColumn instead of setting it on creation?
Related
I am working with intelij, java, spring and hiberante to create tables in my mysql database. My code executes without a problem but for some reason hibernate creates a table I deleted. The table connected two entities with many to many relation with a composite key. One entity was deleted and so the composite key table was also deleted because it was no longer needed. I updated the other table so there is no leftover connections. I tried changing hibernates ddl-auto from update to create. It drops all and creates all with that table that doesn't exist. I also droped the whole schema in mysql workbench multiple times. I am assuming there is some leftover data somewhere in hibernate but I cannot find it especially since the same code works on another computer where it doesn't create the deleted table.
Tis is the hibernate part from my application properties:
# Hibernate properties
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL5Dialect
spring.jpa.properties.hibernate.globally_quoted_identifiers=true
spring.jpa.hibernate.ddl-auto=create
Below is the entity that I didn't delete that was connected to the projection_seat_list which doesn't exist anymore as well as the seat entity:
#Data
#Getter
#Setter
#NoArgsConstructor
#AllArgsConstructor
#Entity
#Table(name = "projections")
public class Projection {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id")
private int projectionId;
#Column(name = "date")
private String date;
#Column(name = "startTime")
private String startTime;
#ManyToOne
#JoinColumn(name = "idHall")
private Hall hall;
#ManyToOne
#JoinColumn(name = "idMovie")
private Movie movie;
#Column(name = "seatList")
#ElementCollection
private List<Integer> seatList;
#OneToMany(fetch = FetchType.LAZY, mappedBy = "projection", cascade = CascadeType.ALL)
private List<Ticket> ticketList;
}
This is the part of the output code regarding the problem when app is executed:
Hibernate: drop table if exists `projection_seat_list`
Hibernate: drop table if exists `projections`
Hibernate: create table `projection_seat_list` (`projection_id` integer not null, `seat_list` integer) engine=MyISAM
Hibernate: create table `projections` (`id` integer not null, `date` varchar(255), `start_time` varchar(255), `id_hall` integer, `id_movie` integer, primary key (`id`)) engine=MyISAM
Hibernate: alter table `projection_seat_list` add constraint `FK8rkfyw0lua4jjaamrw3kl3llo` foreign key (`projection_id`) references `projections` (`id`)
Here is my whole code on github if you'd like to see the structure:
https://github.com/denibakulic/Java.git
It was my mistake because I had a list in my projection model and with that automatically another table is created because you can't have a list. I got confused because the name of the created table of the same of the one that I previously deleted.
I'm relatively new to JPA and Hibernate and am trying to see how the #OneTo One annotation works, let's say I have an entity "Task" with the following relation:
#OneToOne
#JoinColumn(name = "manager_id")
private Manager manager;
And there's the entity "Manager":
#Entity
#Table(name = "manager")
public class Manager {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
private Long id;
private String name;
public Manager() {
}
When I run the test file along with the "hibernate.hbm2ddl.auto" set to "update" I get a Many to One relation in the database (as you can see, there is no unique constraint of any kind that'd make it a one to one relation):
CREATE TABLE IF NOT EXISTS `timesheet`.`task` (
`id` BIGINT(20) NOT NULL AUTO_INCREMENT,
`completed` BIT(1) NOT NULL,
`description` VARCHAR(255) NULL DEFAULT NULL,
`manager_id` BIGINT(20) NULL DEFAULT NULL,
PRIMARY KEY (`id`),
INDEX `FK3635851B178516` (`manager_id` ASC),
CONSTRAINT `FK3635851B178516`
FOREIGN KEY (`manager_id`)
REFERENCES `timesheet`.`manager` (`id`))
ENGINE = InnoDB
DEFAULT CHARACTER SET = utf8;
To be sure of this I tried adding two records with the same manager id and were indeed added, I also tried setting the unique constraint like "#Table(name = "Task",uniqueConstraints = #UniqueConstraint(columnNames =..." but no luck.
So Why is this happening and what's exactly the pros of using #OneToOne annotaion if no application logic is applied to validate this?
Also, Is there any chance that Hibernate is not able to do the DDL generation properly?
(I know that generation of schemas through hibernate is only meant for testing)
In a unidirectional relationship you will get the expected unique constraint if you mark it as "optional=false". You also get it if you set the join column explicitly as unique, of course.
So either
#OneToOne(optional=false)
#JoinColumn(name = "manager_id")
private Manager manager;
or
#OneToOne
#JoinColumn(name = "manager_id", unique=true)
private Manager manager;
So why do you need to mark it as not optional?
My guess is that, when a value is optional, the column can contain many null values, but in many databases this can not be done when a unique constraint is present. You can do it in MySQL though, so maybe the Hibernate generator is not taking the database into account in this case (a bug?).
See a discussion about MySQL handling of nulls here.
I had this issue too and I just needed to add the referenced column so I can get a generated table:
#Entity(name = "news")
public class News extends BaseEntity {
#Column(length = 500)
private String title;
#Column(length = 2000)
private String description;
#OneToOne(optional = false, fetch = FetchType.LAZY, cascade = CascadeType.ALL)
#JoinColumn(name = "file_id", referencedColumnName = "id", unique = true)
private Picture picture;
}
Here is an exception I'm getting:
[EclipseLink-4002] (Eclipse Persistence Services - 2.5.2.v20140319-9ad6abd):
org.eclipse.persistence.exceptions.DatabaseException Internal Exception:
java.sql.SQLSyntaxErrorException: Vergleiche zwischen 'BIGINT' und 'VARCHAR (UCS_BASIC)'
werden nicht unterstützt.
Error Code: 30000
Call: SELECT t1.ID, t1.TEXTINFO FROM COORDINATESLOCATION_INFORMATION t0, TEXTINFORMATION t1
WHERE ((t0.CoordinatesLocation_ID = ?) AND (t1.ID = t0.informationList_ID))
bind => [1 parameter bound]
When I run my application first (without an empty database) everything works. I easily can manage data in all CRUD functions. Later (after an unspecific time or several requests), the exception appears.
This is a tourist information application. There are authors that create tours. One tour contains many locations. One location contains many information. The associations are realised as compositions.
As I changed every association to eager fetch, the exception appears earlier in the workflow.
Here are the code snippets:
#Entity
public class CoordinatesLocation implements Serializable{
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String name;
private String description;
#OneToMany(fetch = FetchType.EAGER)
private List<Information> informationList = new ArrayList<>();
private double lat;
private double lng;
}
#Entity
#Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
public abstract class Information implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
}
#Entity
public class TextInformation extends Information {
private String textInfo;
}
Plus getter & setter!
Thanks for helping!
With the code you have provided, I can recreate the issue in EclipseLink . The generated script is;
CREATE TABLE COORDINATESLOCATION_INFORMATION (CoordinatesLocation_ID BIGINT
NOT NULL, informationList_ID VARCHAR(255) NOT NULL, PRIMARY KEY
(CoordinatesLocation_ID, informationList_ID))
I have tried explicitly defining the columns of the join table;
#OneToMany(fetch = FetchType.EAGER)
#JoinTable (name = "COORDINATESLOCATION_INFORMATION",
joinColumns = #JoinColumn(name = "CoordinatesLocation_ID", referencedColumnName = "ID"),
inverseJoinColumns = #JoinColumn(name = "Information_ID",referencedColumnName = "ID" ))
private List<Information> informationList;
….but still get VARCHAR(255).
I have removed the
#Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
From the Information class (so defaulting to SINGLE_TABLE strategy) and this leads to the desired script:
CREATE TABLE COORDINATESLOCATION_INFORMATION (CoordinatesLocation_ID BIGINT NOT
NULL, informationList_ID BIGINT NOT NULL, PRIMARY KEY (CoordinatesLocation_ID,
informationList_ID)).
So, following that somewhat empirical analysis (I’m not sure why ID was mapped to type VARCHAR(255)) you have a couple of choices.
Modify you CREATE TABLE COORDINATESLOCATION_INFORMATION script to include informationList_ID BIGINT.
Change to #Inheritance(strategy = InheritanceType. SINGLE_TABLE). This requires the addition of a discriminator column and may not be in line with your approach.
Note also that with #OneToMany you do not need a join table if you add a Foreign Key in your Information table and define #ManyToOne on the Information side and #OneToMany on the CoordinatesLocation side with the mappedBy Attribute.
This link will give some idea of that. https://en.wikibooks.org/wiki/Java_Persistence
I can't read the localized message in your exception, however I'll guess that it says you can't compare bigint and varchar. I'll further guess that the error is in this part of the SQL: (t1.ID = t0.informationList_ID). Is one of those columns a bigint and the other a varchar?
basically here t0.CoordinatesLocation_ID = ? at sometime your parameter is of type BIGINT and the reqested type is VARCHAR, you should look at this
I searched on Google and StackOverflow but was unable to find a situation that appears to match mine.
I am using MySQL 5.5 and Java 7 JPA to interface with a database from within Java programs. I have a few tables with a many-to-many relationship and have corresponding join tables defined. I successfully tested adding and removing entries to a join table, but ran into a problem when re-running the "add" test. First, let me provide some of the code for reference:
Member.java:
#Entity
#Table(name = "member")
public class Member implements Serializable {
#Id
#TableGenerator(name = "member_seq_gen", table = "sequence", pkColumnName = "seq_name", valueColumnName = "seq_count", pkColumnValue = "member_seq")
#GeneratedValue(strategy = GenerationType.TABLE, generator = "member_seq_gen")
private long id;
... other fields not shown ...
#ManyToMany
#JoinTable(name = "j_member_strength", joinColumns = { #JoinColumn(name = "member_id", referencedColumnName = "id") }, inverseJoinColumns = { #JoinColumn(name = "program_id", referencedColumnName = "id") })
private Collection<StrengthProgram> programs;
...
public void register(StrengthProgram program) {
programs.add(program);
}
public void unregister(StrengthProgram program) {
programs.remove(program);
}
}
StrengthProgram.java:
#Entity
#Table(name = "strength_program")
public class StrengthProgram implements Serializable {
#Id
#TableGenerator(name = "strength_program_seq_gen", table = "sequence", pkColumnName = "seq_name", valueColumnName = "seq_count", pkColumnValue = "strength_program_seq")
#GeneratedValue(strategy = GenerationType.TABLE, generator = "strength_program_seq_gen")
private long id;
... other fields not shown ...
#ManyToMany(mappedBy = "programs")
private Collection<Member> members;
...
}
SQL for the join table:
DROP TABLE IF EXISTS `cf531alt`.`j_member_strength` ;
CREATE TABLE IF NOT EXISTS `cf531alt`.`j_member_strength` (
`member_id` INT(11) NOT NULL ,
`program_id` INT(11) NOT NULL ,
PRIMARY KEY (`member_id`, `program_id`) ,
CONSTRAINT `fk_member_has_strength_program_member1`
FOREIGN KEY (`member_id` )
REFERENCES `cf531alt`.`member` (`id` )
ON DELETE NO ACTION
ON UPDATE NO ACTION,
CONSTRAINT `fk_member_has_strength_program_strength_program1`
FOREIGN KEY (`program_id` )
REFERENCES `cf531alt`.`strength_program` (`id` )
ON DELETE NO ACTION
ON UPDATE NO ACTION)
ENGINE = InnoDB
DEFAULT CHARACTER SET = latin1;
CREATE INDEX `fk_member_has_strength_program_strength_program1_idx` ON `cf531alt`.`j_member_strength` (`program_id` ASC) ;
CREATE INDEX `fk_member_has_strength_program_member1_idx` ON `cf531alt`.`j_member_strength` (`member_id` ASC) ;
I have logic elsewhere that will "register" or "un-register" a member with a strength program by adding or removing the program object to/from the programs collection in the Member object.
I was able to register a collection of members with a program and un-register a subset of those members successfully, but when I went to re-run the register test with the original list of members, I got a duplicate entry exception. I understand why this is happening. What I don't know is how to prevent it or handle the situation if/when it arises. Following is an excerpt of the exception:
[EL Warning]: 2014-12-29 19:22:25.075--ClientSession(1337625898)--Exception [EclipseLink-4002] (Eclipse Persistence Services - 2.5.2.v20140319-9ad6abd): org.eclipse.persistence.exceptions.DatabaseException
Internal Exception: com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException: Duplicate entry '183-1' for key 'PRIMARY'
Error Code: 1062
Call: INSERT INTO j_member_strength (program_id, member_id) VALUES (?, ?)
bind => [2 parameters bound]
Query: DataModifyQuery(name="programs" sql="INSERT INTO j_member_strength (program_id, member_id) VALUES (?, ?)")
...
I'm not very familiar with how JPA handles one-to-many or many-to-many relationships in the Java code. I wasn't sure if the relationship would be "created" using the method above until I tried it. I ran the test and then queried the database and to my surprise (and satisfaction) found that entries had been "automatically" made in the join table.
Does anyone have ideas about how to avoid or mitigate the "duplicate entry" problem? If I have not been clear enough or if more code is needed, please let me know.
UPDATE:
Do I need to query the join table to see if an entry exists for the given member/program combination and take appropriate action based upon the result? This would be my first thought, but wasn't sure how much JPA did behind the scenes.
there is some tips in the code that I think maybe cause a problem:
you don't need register(program) and unregister(program) methods, you can directly use member.programs.add(program) or member.programs.remove(program).
in many-to-many association the entities are adding to both side I mean: program.members.add(member); member.programs.add(program) after that you can persist the member or program
I have a problem very similar to this: How do I join tables on non-primary key columns in secondary tables?
But I'm not sure if I can apply the same solution.
I have two tables like these:
CREATE TABLE CUSTOMER
(
CUSTOMER_ID INTEGER NOT NULL,
DETAIL_ID INTEGER NOT NULL,
PRIMARY KEY( CUSTOMER_ID ),
CONSTRAINT cust_fk FOREIGN KEY( DETAIL_ID ) REFERENCES DETAILS( DETAIL_ID )
)
CREATE TABLE DETAILS
(
DETAIL_ID INTEGER NOT NULL,
OTHER INTEGER NOT NULL,
PRIMARY KEY( DETAIL_ID )
)
I'd like to map these tables to a single class called Customer, so I have:
#Entity
#Table(name = "CUSTOMERS")
#SecondaryTable(name = "DETAILS", pkJoinColumns=#PrimaryKeyJoinColumn(name="DETAIL_ID"))
public class Customer {
#Id
#GeneratedValue
#Column(name = "CUSTOMER_ID")
private Integer id;
#Column(table = "DETAILS", name = "OTHER")
private Integer notes;
// ...
}
but this works only if DETAIL_ID matches CUSTOMER_ID in the primary table.
So my question is: how can i use a foreign-key field in my primary table to join on the primary-key of the secondary table?
UPDATE
I tried to set:
#SecondaryTable(name = "DETAILS", pkJoinColumns=#PrimaryKeyJoinColumn(name="DETAIL_ID", referencedColumnName="DETAIL_ID"))
but when I run the application I get this exception:
org.hibernate.MappingException: Unable to find column with logical name: DETAIL_ID in org.hibernate.mapping.Table(CUSTOMERS) and its related supertables and secondary tables
For anyone looking for an answer to this, using #SecondaryTable is not the way to join two tables with non-primary key columns, because Hibernate will try to assosiate the two tables by their primary keys by default; you have to use #OneToMany review http://viralpatel.net/blogs/hibernate-one-to-many-annotation-tutorial/ for a solution, here's a code snippet in case that url stops working:
Customer Class:
#Entity
#Table(name="CUSTOMERS")
public class Customer {
#Id
#GeneratedValue
#Column(name="CUSTOMER_ID")
private Integer id;
#ManyToOne
#JoinColumn(name="DETAIL_ID")
private Details details;
// Getter and Setter methods...
}
Details Class:
#Entity
#Table(name="DETAILS")
public class Details {
#Id
#GeneratedValue
#Column(name="DETAIL_ID")
private int detailId;
#Column(name="OTHER")
private String other;
#OneToMany(mappedBy="details")
private Set<Customer> customers;
// Getter and Setter methods...
}
This is easily accessible through hibernate with the following code:
Session session = HibernateUtil.getSessionFactory().openSession();
Query query = session.createQuery("select id, details.other from Customer");
I hope this helps anyone out there spending hours searching for a way to achieve this like I did.
You can use the referenceColumnName attribute of the #PrimaryKeyJoinColumn annotation to define the join column to the referenced table. In fact, by combining use of name/referencedColumnName you can join on arbitrary on both sides, with the constraint that if duplicates are found your ORM provider will throw an exception.