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
Related
I am trying to join to Hibernate Entities in a OneToOne Mapping. I am able to fetch the data for a given primary key from the Main Entity, the joining entity, however, returns null. I am new to hibernate and any help will be appreciated.
I have two Tables,
PT_CORE
Primary Key: ptId - Integer;
Foreign Key: stId(ST_AUX) - Integer;
Columns: ptId, ptName
ST_AUX
Primary Key: stId;
Columns: stId, stName
The two tables get populated by other applications and mine is a read-only operation.
Below is my first Entity class(PtCore.java)
#Entity
#Table(name="PT_CORE")
public class PtCore implements Serializable{
#Id
#Column(name="ptId", nullable = false)
private int id;
#Column(nullable=false)
private int stId; //The Foreign key column
#OneToOne
#JoinTable( name = "core_aux", joinColumns = {#JoinColumn(Name="ptId")},
inverseJoinColumns = {#JoinColumn(Name="stId")}
)
private StAux staux;
//Getters, setters and toString() for above
}
StAux is another Entity, defined as below,
#Entity
#Table(name="ST_AUX")
public class StAux implements Serializable {
#Id
#Column(nullable=false)
private Integer stId;
#OneToOne
private PtCore ptcore;
#Column
private String stName;
//Getters, Setters and toString follow.
}
I do below in the Service method:
PtCore obj = (PtCore) session.get(PtCore.class,1);
System.out.println(obj);
In the Results, I get the value of ptName, but the stAux class variables are null, Indicating that the join does not work as expected.
First of all you have the mapping information existing in your PT_CORE. And I assume it is something like FOREIGN KEY (stid) REFERENCES (stid). If you want to use existing schema and existing data I guess there is no mapping table core_aux really existing. At least you did not mention it. However it is visible as #JoinTable annotation but still there is this above mentioned foreign key which seems to be the real mapping (so again not the join table).
I suggest the following
remove this
#Column(nullable=false)
private int stId; //The Foreign key column
from your PtCore. I think it is not needed. Also in PtCore, remove the #JoinTable (because what I told above) and add mapping informaiion to #OneToOne annotation, like:
#OneToOne
#JoinColumn(name = "stid")
private StAux staux;
from your PT_CORE.
Then in StAux alter also a bit:
#Id
#Column(name = "stid") // this might not be needed but if there is like "st_id"...
private Integer stId; // so just for sure
#OneToOne(mappedBy = "staux")
private PtCore ptcore;
Because you have existing tables and constraints there might raise errors if hibernate tries to auto-generate those again by JPA instructions.
Check this for example for more information.
UPDATE: just realized also that in your title is #OneToMany but in your code is #OneToOne.
So you might want to elaborate your question and/or title a bit.
In your relation, the owning side is PtCore, the inverse side is StAux.
In bidirectional OneToOne relations, the inverse side has to have the mappedBy attribute. Actually, the mappedBy attribute contains the name of the association-field on the owning side.
So, you must change your inverse side code (StAux Entity). You have to add mappedBy attribute to #OneToOne in StAux class:
#OneToOne(mappedBy="staux")
private PtCore ptcore;
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
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?
I'm having a problem modelling the following problem in JPA. I have a JPA Entity class 'User', like so:
(accessors/mutators/extraneous fields/extraneous JPA configuration omitted for brevity)
#Entity
class User {
#Id
#Generated Value
long id;
#OneToMany
Report contributorReports; // All the reports where this user is contributor
#OneToMany ownerReports; // All the reports where this user is owner
String username;
}
and a JPA Entity class 'Report'
#Entity
class Report {
#Id
#GeneratedValue
long id;
#OneToOne
User contributor;
#OneToOne
User owner;
SomeData data;
}
I would like to model a relationship such that:
A Report must contain both a contributor and an owner
I can access all of the Reports a User has been a 'contributor' for via the User Entity
I can access all of the Reports a User has been a 'owner' for via the User Entity
I imagined I would end up with a mapping table that looked vaguely like this:
CREATE TABLE user_report {
BIGINT reportId,
BIGINT contributorId,
BIGINT ownerId,
}
I attempted to solve the problem like:
#OneToOne
#JoinTable(name = "user_report",
joinColumns = {
#JoinColumn(name = "reportOwner_ID", referencedColumnName = "ID")}
)
private User owner;
#OneToOne
#JoinTable(name = "user_report",
joinColumns = {
#JoinColumn(name = "reportContributor_ID", referencedColumnName = "ID")}
)
private User contributor;
This generates a table like:
CREATE TABLE user_report (
BIGINT ownerReport_ID, // Report ID
BIGINT reportOwner_ID, // UserID owner
BIGINT contributorReport_ID, // Report ID
BIGINT reportContributor_ID // UserID contributor
)
So when JPA attempts to map to this table, it maps each field separately and fails because only half of the row is being committed, throwing this exception:
Caused by: java.sql.BatchUpdateException: Field 'ownerReport_ID' doesn't have a default value
I was hoping to get some direction on how to best model the relationship I envision. (Or maybe a better way to envision the relationship) If additional information is needed, I'll be glad to provide it.
Kind Regards
Matt
Based on your requirements I believe you accomplish this with 2 1:M from User to Report with a matched M:1 back for each.
#Entity
class User {
#Id
#Generated Value
long id;
// All the reports where this user is contributor
#OneToMany(mappedBy="contributor")
List<Report> contributorReports;
// All the reports where this user is owner
#OneToMany(mappedBy="owner")
List<Report> ownerReports;
String username;
}
Then your Report class would look like:
#Entity
class Report {
#Id
#GeneratedValue
long id;
#ManyToOne
User contributor;
#ManyToOne
User owner;
SomeData data;
}
This situation is also possible with a join table but not required based on your requirements as I understand them.
Doug
I'm using Hibernate 3.5.2-FINAL with annotations to specify my persistence mappings. I'm struggling with modelling a relationship between an Application and a set of Platforms. Each application is available for a set of platforms.
From all the reading and searching I've done, I think I need to have the platform enum class be persisted as an Entity, and to have a join table to represent the many-to-many relationship. I want the relationship to be unidirectional at the object level, that is, I want to be able to get the list of platforms for a given application, but I don't need to find out the list of applications for a given platform.
Here are my simplified model classes:
#Entity
#Table(name = "TBL_PLATFORM")
public enum Platform {
Windows,
Mac,
Linux,
Other;
#Id
#GeneratedValue
#Column(name = "ID")
private Long id = null;
#Column(name = "NAME")
private String name;
private DevicePlatform() {
this.name = toString();
}
// Setters and getters for id and name...
}
#Entity
#Table(name = "TBL_APP")
public class Application extends AbstractEntity implements Serializable {
private static final long serialVersionUID = 1L;
#Column(name = "NAME")
protected String _name;
#ManyToMany(cascade = javax.persistence.CascadeType.ALL)
#Cascade({org.hibernate.annotations.CascadeType.SAVE_UPDATE})
#JoinTable(name = "TBL_APP_PLATFORM",
joinColumns = #JoinColumn(name = "APP_ID"),
inverseJoinColumns = #JoinColumn(name = "PLATFORM_ID"))
#ElementCollection(targetClass=Platform.class)
protected Set<Platform> _platforms;
// Setters and getters...
}
When I run the Hibernate hbm2ddl tool, I see the following (I'm using MySQL):
create table TBL_APP_PLATFORM (
APP_ID bigint not null,
PLATFORM_ID bigint not null,
primary key (APP_ID, PLATFORM_ID)
);
The appropriate foreign keys are also created from this table to the application table and platform table. So far so good.
One problem I'm running into is when I try to persist an application object:
Application newApp = new Application();
newApp.setName("The Test Application");
Set<DevicePlatform> platforms = EnumSet.of(Platform.Windows, Platform.Linux);
newApp.setPlatforms(platforms);
applicationDao.addApplication(newApp);
What I would like to happen is for the appropriate rows in the Platform table to created, i.e. create a row for Windows and Linux, if they don't already exist. Then, a row for the new application should be created, and then the mapping between the new application and the two platforms in the join table.
One issue I'm running into is getting the following runtime exception:
2010-06-30 13:18:09,382 6613126-0 ERROR FlushingEventListener Could not synchronize database state with session org.hibernate.TransientObjectException: object references an unsaved transient instance - save the transient instance before flushing: com.example.model.Platform
Somehow, the platform set is not being persisted when I try to persist the application. The cascade annotations are supposed to take care of that, but I don't know what's wrong.
So my questions are:
Is there a better way to model what I want to do, e.g. is using an Enum appropriate?
If my model is alright, how do I properly persist all of the objects?
I've been struggling with this for hours, and I've tried to recreate all of the code above, but it might not be complete and/or accurate. I'm hoping someone will point out something obvious!
You should decide whether your Platform is an entity or not.
If it's an entity, it can't be an enum, because list of possible platforms is stored in the database, not in the application. It should be a regular class with #Entity annotation and you will have a normal many-to-many relation.
If it isn't an entity, then you don't need TBL_PLATFORM table, and you don't have a many-to-many relation. In this case you can represent a set of Platforms either as an integer field with bit flags, or as a simple one-to-many relation. JPA 2.0 makes the latter case simple with #ElementCollection:
#ElementCollection(targetClass = Platform.class)
#CollectionTable(name = "TBL_APP_PLATFORM",
joinColumns = #JoinColumn(name = "APP_ID"))
#Column(name = "PLATFORM_ID")
protected Set<Platform> _platforms;
-
create table TBL_APP_PLATFORM (
APP_ID bigint not null,
PLATFORM_ID bigint not null, -- the ordinal number of enum value
primary key (APP_ID, PLATFORM_ID)
);
and enum Platform without annotations.
Simple use below mapping on your entity. Suppose that we have:
public enum TestEnum { A, B }
Then in your Entity class:
#ElementCollection(targetClass = TestEnum.class)
#CollectionTable(
name = "yourJoinTable",
joinColumns = #JoinColumn(name = "YourEntityId")
)
#Column(name = "EnumId")
private final Set<TestEnum> enumSet= new HashSet<>();
The following example shows what the situation is when Module is an entity and Langue is an enum.
#Entity
public class Module {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String libelle;
#ElementCollection(targetClass = Langue.class, fetch = FetchType.EAGER)
#CollectionTable(name = "link_module_langue",
joinColumns = #JoinColumn(name = "module_id", referencedColumnName = "id"))
#Enumerated(EnumType.STRING)
#Column(name = "langue")
private Set<Langue> langues;
}
public enum Langue {
FRANCAIS, ANGLAIS, ESPAGNOLE
}
You should create link_module_langue table, please see the following sql code :
CREATE TABLE `link_module_langue` (
`module_id` BIGINT(20) NOT NULL,
`langue` VARCHAR(45) NOT NULL,
PRIMARY KEY (`module_id`, `langue`),
CONSTRAINT `module_fk`
FOREIGN KEY (`module_id`)
REFERENCES `module` (`id`)
ON DELETE CASCADE
ON UPDATE CASCADE);
NB: Langue is not an entity and would not have its own table.