I have a database partitioned table defined as follows:
CREATE TABLE [integration].[entity_key_map] (
[entity_key_map_id] int /*varchar(64)*/NOT NULL IDENTITY,
[business_entity_alias] CHAR(5) NOT NULL,
[business_key_1] VARCHAR(64) NOT NULL,
[business_key_2] VARCHAR(64) NULL,
[business_key_3] VARCHAR(64) NULL,
[business_key_4] VARCHAR(64) NULL,
[business_key_5] VARCHAR(64) NULL,
[system_alias] CHAR(5) NOT NULL,
[system_entity_alias] CHAR(5) NOT NULL,
[system_key_1] VARCHAR(64) NOT NULL,
[system_key_2] VARCHAR(64) NULL,
[system_key_3] VARCHAR(64) NULL,
[system_key_4] VARCHAR(64) NULL,
[system_key_5] VARCHAR(64) NULL
)
ON entity_key_map_ps ([system_entity_alias])
GO
ALTER TABLE [integration].[entity_key_map]
ADD CONSTRAINT [ekm_pk]
PRIMARY KEY NONCLUSTERED ([entity_key_map_id], [system_entity_alias])
GO
The partition column, system_entity_alias, is required on the primary key, making it a compound PK. This leads to the use of a PK class defining #Embeddable and an entity class using #EmbeddedId.
My JPA entity is defined thus:
#EmbeddedId
private EntityKeyMapPK idClass;
#Column(name = "business_entity_alias", nullable = false, length = 5)
#JsonProperty("businessEntityAlias")
private EntityType businessEntityAlias;
#Column(name = "business_key_1", nullable = false)
#JsonProperty("businessKey1")
private String businessKey1;
#Column(name = "business_key_2")
#JsonProperty("businessKey2")
private String businessKey2;
#Column(name = "business_key_3")
#JsonProperty("businessKey3")
private String businessKey3;
#Column(name = "business_key_4")
#JsonProperty("businessKey4")
private String businessKey4;
#Column(name = "business_key_5")
#JsonProperty("businessKey5")
private String businessKey5;
#Column(name = "system_alias", nullable = false, length = 5)
#JsonProperty("systemAlias")
private SystemType systemAlias;
#Column(name = "system_key_1", nullable = false)
#JsonProperty("systemKey1")
private String systemKey1;
#Column(name = "system_key_2")
#JsonProperty("systemKey2")
private String systemKey2;
#Column(name = "system_key_3")
#JsonProperty("systemKey3")
private String systemKey3;
#Column(name = "system_key_4")
#JsonProperty("systemKey4")
private String systemKey4;
#Column(name = "system_key_5")
#JsonProperty("systemKey5")
private String systemKey5;
And EntityKeyMapPK looks like this:
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "entity_key_map_id", nullable = false, insertable = false, updatable = false)
private Long entityKeyMapId;
#Column(name = "system_entity_alias", nullable = false)
private EntityType systemEntityAlias;
The problem is, that Hibernate still generates an insert statement using entity_key_map_id, passing it a null value, despite using #GeneratedValue(strategy = GenerationType.IDENTITY) and insertable = false, updatable = false. The actual error is:
o.h.engine.jdbc.spi.SqlExceptionHelper : SQL Error: 544, SQLState: S0001
o.h.engine.jdbc.spi.SqlExceptionHelper : Cannot insert explicit value for identity column in table 'entity_key_map' when IDENTITY_INSERT is set to OFF.
o.h.i.ExceptionMapperStandardImpl : HHH000346: Error during managed flush [org.hibernate.exception.SQLGrammarException: could not execute statement]
Hibernate must generate an insert statement that does not reference entity_key_map_id, allowing the database to assign its internally generated ID. The following statements work as expected at the SQL CLI:
BEGIN TRANSACTION
INSERT INTO [integration].[entity_key_map]
([business_entity_alias]
,[business_key_1]
,[system_alias]
,[system_entity_alias]
,[system_key_1]
)
VALUES
('CONT'
,'some.two#email.address'
,'MSTR'
,'PTY'
,'987654321'
)
COMMIT
GO
SELECT * FROM [integration].[entity_key_map]
GO
How do I define the classes to obtain this behaviour?
Kindly note: Using JPA partitioning (multiple DB instances) is not an option--unless I've missed something!--and please simply accept the need for DB partitioning on this table.
Thanks.
Related
I have a legacy app which has an entity relationship that looks like this. I changed the names of the fields to less realistic values and reduced to only the relevant fields.
CREATE TABLE `billing_target` (
`billingTargetID` int(11) NOT NULL,
`targetType` char(5) NOT NULL,
`targetID` int(11) NOT NULL
PRIMARY KEY (`billingTargetID`)
);
CREATE TABLE `Client` (
`clientID` int(11) NOT NULL,
`name` varchar(200),
`color` varchar(200),
`shape` varchar(200)
PRIMARY KEY (`clientID`),
CONSTRAINT `fk_client_billingTarget`
FOREIGN KEY (`clientID`)
REFERENCES `billing_target` (`targetID`)
);
My most current attempt which causes an issue when saving as it gives a null entity key exception.
#Data
#Entity
public class BillingTarget implements Serializable {
#Id
#Column(name = "billingTargetID")
private Integer id;
#Column(name = "targetID", nullable = false)
private Integer targetID;
#Column(name = "targetType", nullable = false)
private String type;
}
#Data
#Entity
public class Client implements Serializable {
#Id
#Column(name = "clientID")
private Integer id;
#OneToOne
#JoinColumn(name = "clientID")
#MapsId("targetID")
private BillingTarget billingTarget;
private String name;
private String color;
private String shape;
}
Here's the PlantUML code if interested
#startuml
hide circle
entity BillingTarget {
* billingTargetID <<generated>>
--
* targetType
* targetID <<unique>>
}
entity Client {
* clientID <<fk>>
--
* name
color
shape
}
BillingTarget ||--o| Client : "targetID:clientID"
#enduml
I was already thinking of using a MappedSuperclass but right now it is only one type (though it could be more). Second the billing target may be zero and not null as there's a NOT NULL constraint already present.
I'm trying to achieve to have an entity called MyEntity along with another entity called MyEntityInfo using Hibernate 5.3.13.Final with annotations under Wildfly 18.
The idea is to have MyEntity store some commonly requested fields, and MyEntityInfo store some rarely requested fields. Both share the same primary key called SID (Long), and there is a FK from Info's SID to Entity's SID. There can be entities without info.
Normally you will not require the additional info. For example, I don't want the info entity to be fetched when I query my entity like this:
MyEntityImpl entity = em.find(MyEntityImpl.class, 1L);
However, when I run this code, I find that there's a second query, fetching the Info entity along the main one, as in an EAGER behaviour.
I'm mapping the relationship using #OneToOne. I've tried several combinations of FetchType, optional and #LazyToOne, but so far without success.
Here is the code for both MyEntity and MyEntityInfo classes (additional getters and setters removed):
MyEntity (ID generator is a custom sequence generator):
#Entity
#Table(name = MyEntityImpl.TABLE_NAME)
public class MyEntityImpl {
public static final String TABLE_NAME = "TMP_MY_ENTITY";
#Id
#GeneratedValue(strategy = GenerationType.TABLE, generator = "GEN_" +
TABLE_NAME)
#GenericGenerator(name = "GEN_" +
TABLE_NAME, strategy = CoreIdGenerator.ID_GENERATOR, parameters = {
#Parameter(name = "tableName", value = TABLE_NAME) })
#Column(name = "sid", nullable = false, unique = true)
private Long sid;
#OneToOne(mappedBy = "myEntity", cascade = CascadeType.ALL, fetch = FetchType.LAZY, optional = true)
#LazyToOne(LazyToOneOption.NO_PROXY)
private MyEntityInfoImpl info;
#Column
private String field;
MyEntityInfo:
#Entity
#Table(name = MyEntityInfoImpl.TABLE_NAME)
public class MyEntityInfoImpl {
public static final String TABLE_NAME = "TMP_MY_ENTITY_INFO";
#Id
#Column(name = "SID", nullable = false, unique = true)
private Long sid;
#OneToOne(fetch = FetchType.EAGER, optional = false)
#JoinColumn(name = "SID", referencedColumnName = "SID", insertable = false, updatable = false, nullable = false)
private MyEntityImpl myEntity;
#Column(name = "INFO_FIELD")
private String infoField;
I've tried this solution, but as I said, it didn't work for me:
Hibernate lazy loading for reverse one to one workaround - how does this work?
I've managed to do something somewhat similar using #OneToMany and managing data manually, but that's not what I'd like to do. However, another alternatives and information on whether this can be achieved or not using #OneToOne, or the right design pattern to do this are also welcome.
PS: Database tables creation for SQL Server, in case you want to try it:
create table TMP_MY_ENTITY (SID NUMERIC(19,0) NOT NULL, FIELD VARCHAR(100));
go
ALTER TABLE TMP_MY_ENTITY ADD CONSTRAINT PK_TMP_MY_ENTITY PRIMARY KEY CLUSTERED (SID);
go
create table TMP_MY_ENTITY_INFO (SID NUMERIC(19,0) NOT NULL, INFO_FIELD VARCHAR(100));
go
ALTER TABLE TMP_MY_ENTITY_INFO ADD CONSTRAINT PK_TMP_MY_ENTITY_INFO PRIMARY KEY CLUSTERED (SID);
go
CREATE SEQUENCE SEQ_TMP_MY_ENTITY START WITH 1 INCREMENT BY 1 MINVALUE 1 CACHE 20;
alter table TMP_MY_ENTITY_INFO add constraint FK_TMP_MY_ENT_INFO_MY_ENT FOREIGN KEY (SID) references TMP_MY_ENTITY(SID);
go
insert into TMP_MY_ENTITY(SID, FIELD) VALUES (NEXT VALUE FOR SEQ_TMP_MY_ENTITY, 'Field 1');
insert into TMP_MY_ENTITY_INFO(SID, INFO_FIELD) VALUES ((SELECT MAX(SID) FROM TMP_MY_ENTITY), 'Info 1');
insert into TMP_MY_ENTITY(SID, FIELD) VALUES (NEXT VALUE FOR SEQ_TMP_MY_ENTITY, 'Field 2');
insert into TMP_MY_ENTITY_INFO(SID, INFO_FIELD) VALUES ((SELECT MAX(SID) FROM TMP_MY_ENTITY), 'Info 2');
insert into TMP_MY_ENTITY(SID, FIELD) VALUES (NEXT VALUE FOR SEQ_TMP_MY_ENTITY, 'Field 3 no info');
-- DELETE ALL
drop table TMP_MY_ENTITY_INFO;
drop table TMP_MY_ENTITY;
drop sequence SEQ_TMP_MY_ENTITY;
After following #SternK link, and upgrading to Wildfly 19 and Hibernate 5.4.14, it finally worked by using #MapsId.
The right mapping to use is this:
MyEntity:
public class MyEntityImpl {
#OneToOne(mappedBy = "myEntity", cascade = CascadeType.REMOVE, fetch = FetchType.LAZY, optional = true)
#JoinColumn(name = "SID")
private MyEntityInfoImpl info;
MyEntityInfo:
public class MyEntityInfoImpl {
#OneToOne(fetch = FetchType.EAGER, optional = false)
#MapsId
#JoinColumn(name = "SID", referencedColumnName = "SID", insertable = false, updatable = false, nullable = false)
private MyEntityImpl myEntity;
I'm stuck on a hard and tricky problem with updating a record from timestamp data.
Technologies :
Spring and Java
Hibernate and JPA
Oracle Databse for version 11g
Create SQL Script for the table :
CREATE TABLE DOSSIER(
NUM_DOSS NUMBER NOT NULL,
TYPE_DOSS CHAR(4) NOT NULL,
DATE_DOSS TIMESTAMP(6) NOT NULL,
DSC_DOSS VARCHAR(25),
NUM_CNFR VARCHAR(25),
TMS_CREA TIMESTAMP(6) NOT NULL,
TMS_MAJ TIMESTAMP(6) NOT NULL,
CONSTRAINT PK_TDECL_RMGV PRIMARY KEY(NUM_DOSS, TYPE_DOSS, DATE_DOSS)
);
Here's my SQL request for updating :
UPDATE DOSSIER
SET
NUM_CNFR='999'
WHERE
NUM_DOSS_='2006103009564900'
and TYP_DOSS='011'
and DATE_DOSS= TO_TIMESTAMP('2017-08-09 16:57:03.786586', 'YYYY-MM-DD HH24:MI:SS.FF6')
You should know that records can have same folder number and type but never the same date because it's a timestamp with milliseconds.
For example :
NUM_DOSS TYP_DOSS DATE_DOSS NUM_CNFR
2006103009564900 | 011 | 2017-08-09 16:57:03.786586 | null
2006103009564900 | 011 | 2017-08-09 16:57:03.786589 | 0125
My DossierEntityPK class :
#Embeddable
public class DossierEntityPK implements Serializable {
private static final long serialVersionUID = -4418929855229352729L;
#Column(name = "NUM_DOSS", nullable = false)
private String numDossier;
#Column(name = "TYPE_DOSS", nullable = false)
private String typeDossier;
#Temporal(TemporalType.TIMESTAMP)
#Column(name = "DATE_DOSS", updatable = false, nullable = false, columnDefinition="TIMESTAMP DEFAULT")
private Date dateDossier;
}
My DossierEntity class :
#Entity
#Table(name = "TDECL_RMGV")
public class OERemiseGouvModele {
#EmbeddedId
private DossierEntityPK dossierEntityPK;
#Column(name = "DSC_DOSS", nullable = true)
private String descriptionDossier;
#Column(name = "NUM_CNFR", nullable = true)
private String numConfirmation;
#Temporal(TemporalType.TIMESTAMP)
#Column(name = "TMS_CREA", nullable = false)
private Date tmsCreation;
#Temporal(TemporalType.TIMESTAMP)
#Column(name = "TMS_MAJ", nullable = false)
private Date tmsMaj;
}
I will show you now my query in JPQL :
#Modifying
#Query("UPDATE DossierEntity d SET d.numConfirmation=:numConfirmation WHERE d.dossierEntityPK.numDossier=:numDossierAND d.dossierEntityPK.typeDossier=:typeDossier AND d.dossierEntityPK.dateDossier= TO_TIMESTAMP(:dateDossier, 'YYYY-MM-DD HH24:MI:SS.FF6')")
int updateFolder(#Param("numConfirmation") String numConfirmation, #Param("numDossier") String numDossier, #Param("typeDossier") String typeDossier, #Param("dateDossier") Date dateDossier);
Then when I execute this method from my web application, there is a empty result from Hibernate. But if I execute this SQL request Update directly on my database, it works well !
I even execute this SQL request with JDBC or with PreparedStatement but I get the same empty result then no record is updated.
I already know this is not a very good idea to use a timestamp in clause WHERE but the workflow works in this way and I can't change that the timestamp is unique and is in a part of composite primary key with folder number and type.
Do you have an idea about how to fix it ?
Executing this SQL request with JDBC or with PreparedStatement, the records are updated and works fine
I'd like to use unidirectional relationship for my JPA instances (Hibernate 4.3.10)
RegionalCountry -> RegionalArea1 -> RegionalArea2
#Entity
public class RegionalCountry {
#Id
#Column(unique = true, nullable = false, updatable = false, length = 36)
private String uuid = UUID.randomUUID().toString();
private String countryName;
private String countryCode;
#OneToMany(fetch = FetchType.EAGER, cascade = {CascadeType.ALL})
private List<RegionalArea1> regionalArea1;
...//getters&setters
}
#Entity
public class RegionalArea1 {
#Id
#Column(unique = true, nullable = false, updatable = false, length = 36)
private String uuid = UUID.randomUUID().toString();
private String area1Name;
private String area1Code;
#OneToMany(fetch = FetchType.EAGER, cascade = {CascadeType.ALL})
private List<RegionalArea2> regionalArea2;
...//getters&setters
}
#Entity
public class RegionalArea2 {
#Id
#Column(unique = true, nullable = false, updatable = false, length = 36)
private String uuid = UUID.randomUUID().toString();
private String area2Name;
private String area2Code;
...//getters&setters
}
Then I want to use Spring JpaRepository to fetch the country and its regional area instances by country code:
public interface RegionalCountryRepository extends JpaRepository<RegionalCountry, UUID> {
public RegionalCountry findOneByCountryCode(String countryCode);
}
When I call
regionalCountryRepository.findOneByCountryCode(countryCode);
SQLException occurs: Unknown column 'regionalar0_.regional_country' in 'field list'
the same exception is for regionalCountryRepository.findAll();
Is there a way to use unidirectional relationship between regional instances and do not get this error?
Surprisingly, unit tests with embedded db work well for the code above, but when we deal with the real MySQL database the exception occurs.
#Test
public void testFindByCountryCode() {
southernArea = new RegionalArea1();
southernArea.setArea1Code("00");
southernArea.setArea1Name("SOUTHERN");
chejuDoArea = new RegionalArea1();
chejuDoArea.setArea1Code("00");
chejuDoArea.setArea1Name("CHEJU-DO");
korea = new RegionalCountry();
korea.setCountryName("Korea, Republic of");
korea.setCountryCode("KOR");
korea.setRegionalArea1s(Arrays.asList(southernArea, chejuDoArea));
repository.save(korea);
RegionalCountry countryFetched = repository.findOneByCountryCode("KOR");
Assert.assertNotNull(countryFetched);
}
Update: the schema is the following
CREATE TABLE REGIONAL_AREA2(
UUID VARCHAR(36) NOT NULL,
AREA2CODE VARCHAR(255),
AREA2NAME VARCHAR(255)
);
CREATE TABLE REGIONAL_AREA1(
UUID VARCHAR(36) NOT NULL,
AREA1CODE VARCHAR(255),
AREA1NAME VARCHAR(255)
);
CREATE TABLE REGIONAL_AREA1_REGIONAL_AREA2(
REGIONAL_AREA1_UUID VARCHAR(36) NOT NULL,
REGIONAL_AREA2_UUID VARCHAR(36) NOT NULL
);
CREATE TABLE REGIONAL_COUNTRY(
UUID VARCHAR(36) NOT NULL,
COUNTRY_CODE VARCHAR(255),
COUNTRY_NAME VARCHAR(255)
);
CREATE TABLE REGIONAL_COUNTRY_REGIONAL_AREA1(
REGIONAL_COUNTRY_UUID VARCHAR(36) NOT NULL,
REGIONAL_AREA1_UUID VARCHAR(36) NOT NULL
);
There may be several reasons:
I do not see any mapping from your class variables to table field regional_country. Please make sure to use annotation #Column to specify the correct table field which the variable maps to. For instance, if variable countryCode maps to table field country_code, you should annotate like this: #Column(name = "country_code") private int countryCode;
For variables private String countryName; private String countryCode; if they have a mapping field in your db schema, please use #Column to annotate them
It would be helpful if you provide more information about your question: e.g., your db schema.
I try to reproduce your problem using just Hibernate Session without any JpaRepository. I have to change mapping for all collection associations from List to Set cause of the "cannot simultaneously fetch multiple bags" error
#OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
private Set<RegionalArea1> regionalArea1;
#OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
private Set<RegionalArea2> regionalArea2;
Everything works fine without any errors. May be, It will help you too. If It will no help:
Hibernate thinks that the REGIONAL_COUNTRY_REGIONAL_AREA1 join table has the regional_country column, but this table has the REGIONAL_COUNTRY_UUID. So try to specify join column names by #JoinTable annotation, or recreate database schema, or change the naming strategy, if you specify it.
SQL generated by Hibernate for search country by country code
select
this_.f_uuid as f_uuid1_3_2_,
this_.f_country_code as f_countr2_3_2_,
this_.f_country_name as f_countr3_3_2_,
regionalar2_.fk_regional_country as fk_regio1_4_4_,
regionalar3_.f_uuid as fk_regio2_4_4_,
regionalar3_.f_uuid as f_uuid1_0_0_,
regionalar3_.f_area1code as f_area2_0_0_,
regionalar3_.f_area1name as f_area3_0_0_,
regionalar4_.fk_regional_area1 as fk_regio1_1_5_,
regionalar5_.f_uuid as fk_regio2_1_5_,
regionalar5_.f_uuid as f_uuid1_2_1_,
regionalar5_.f_area2code as f_area2_2_1_,
regionalar5_.f_area2name as f_area3_2_1_
from
spring_regional_countries this_
left outer join
spring_regional_countries_regional_area1s regionalar2_
on this_.f_uuid=regionalar2_.fk_regional_country
left outer join
spring_regional_area1s regionalar3_
on regionalar2_.fk_regional_area1=regionalar3_.f_uuid
left outer join
spring_regional_area1s_regional_area2s regionalar4_
on regionalar3_.f_uuid=regionalar4_.fk_regional_area1
left outer join
spring_regional_area2s regionalar5_
on regionalar4_.fk_regional_area2=regionalar5_.f_uuid
where
this_.f_country_code=?
Thanks for all answers but the problem was in REGIONAL_COUNTRY_REGIONAL_AREA1 table column names. Postfix _UUID at the end of the column name confuses MySQL.
It's possible to fix this problem renaming columns of intermediate datatables
DROP TABLE IF EXISTS REGIONAL_AREA1_REGIONAL_AREA2;
DROP TABLE IF EXISTS REGIONAL_AREA1_REGIONAL_AREA2;
CREATE TABLE REGIONAL_AREA1_REGIONAL_AREA2(
REGIONAL_AREA1 VARCHAR(36) NOT NULL,
REGIONAL_AREA2 VARCHAR(36) NOT NULL
);
CREATE TABLE REGIONAL_COUNTRY_REGIONAL_AREA1(
REGIONAL_COUNTRY VARCHAR(36) NOT NULL,
REGIONAL_AREA1 VARCHAR(36) NOT NULL
);
I've updated the question so that both tables now use auto-increment. Is perhaps the problem in persisting to the MESSAGES table a problem with the database schema?
In trying to persist a MessageBean as so:
private void persist(MessageBean messageBean) throws Exception {
LOG.info("loading.." + messageBean);
Messages message = new Messages(messageBean);
emf = Persistence.createEntityManagerFactory("USENETPU");
em = emf.createEntityManager();
em.getTransaction().begin();
em.persist(message);
em.getTransaction().commit();
}
Stack trace:
run:
Jul 27, 2012 3:04:06 PM net.bounceme.dur.usenet.controller.CommentsDefaultListModel persist
INFO: loading..floor installer (cultas lake)
[EL Info]: 2012-07-27 15:04:10.006--ServerSession(30409723)--EclipseLink, version: Eclipse Persistence Services - 2.3.0.v20110604-r9504
[EL Info]: 2012-07-27 15:04:11.78--ServerSession(30409723)--file:/home/thufir/NetBeansProjects/USENET/build/classes/_USENETPU login successful
[EL Warning]: 2012-07-27 15:04:12.072--ClientSession(29574192)--Exception [EclipseLink-4002] (Eclipse Persistence Services - 2.3.0.v20110604-r9504): org.eclipse.persistence.exceptions.DatabaseException
Internal Exception: com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: Table 'nntp.SEQUENCE' doesn't exist
Error Code: 1146
Call: UPDATE SEQUENCE SET SEQ_COUNT = SEQ_COUNT + ? WHERE SEQ_NAME = ?
bind => [2 parameters bound]
Query: DataModifyQuery(name="SEQUENCE" sql="UPDATE SEQUENCE SET SEQ_COUNT = SEQ_COUNT + ? WHERE SEQ_NAME = ?")
Jul 27, 2012 3:04:12 PM net.bounceme.dur.usenet.controller.CommentsDefaultListModel <init>
SEVERE: null
Local Exception Stack:
Exception [EclipseLink-4002] (Eclipse Persistence Services - 2.3.0.v20110604-r9504): org.eclipse.persistence.exceptions.DatabaseException
Internal Exception: com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: Table 'nntp.SEQUENCE' doesn't exist
Error Code: 1146
Call: UPDATE SEQUENCE SET SEQ_COUNT = SEQ_COUNT + ? WHERE SEQ_NAME = ?
bind => [2 parameters bound]
Query: DataModifyQuery(name="SEQUENCE" sql="UPDATE SEQUENCE SET SEQ_COUNT = SEQ_COUNT + ? WHERE SEQ_NAME = ?")
at org.eclipse.persistence.exceptions.DatabaseException.sqlException(DatabaseException.java:324)
From MySql:
mysql>
mysql>
mysql> show tables;
+----------------+
| Tables_in_nntp |
+----------------+
| comments |
| messages |
+----------------+
2 rows in set (0.00 sec)
mysql>
mysql> show create table comments;
+----------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Table | Create Table |
+----------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| comments | CREATE TABLE `comments` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`message_id` int(11) NOT NULL,
`comment` text NOT NULL,
`stamp` date NOT NULL,
PRIMARY KEY (`id`),
KEY `message_id` (`message_id`),
CONSTRAINT `comments_ibfk_1` FOREIGN KEY (`message_id`) REFERENCES `messages` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 |
+----------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)
mysql>
mysql> show create table messages;
+----------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Table | Create Table |
+----------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| messages | CREATE TABLE `messages` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`newsgroup` text NOT NULL,
`subject` text NOT NULL,
`content` text NOT NULL,
`number` text NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 |
+----------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)
mysql>
and the fields for Messages:
package net.bounceme.dur.usenet.controller;
import java.io.Serializable;
import java.util.Collection;
import javax.persistence.*;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlTransient;
#Entity
#Table(name = "messages", catalog = "nntp", schema = "")
#XmlRootElement
#NamedQueries({
#NamedQuery(name = "Messages.findAll", query = "SELECT m FROM Messages m"),
#NamedQuery(name = "Messages.findById", query = "SELECT m FROM Messages m WHERE m.id = :id")})
public class Messages implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#Basic(optional = false)
#Column(name = "id", nullable = false)
#GeneratedValue
private Integer id;
#Basic(optional = false)
#Lob
#Column(name = "newsgroup", nullable = false, length = 65535)
private String newsgroup;
#Basic(optional = false)
#Lob
#Column(name = "subject", nullable = false, length = 65535)
private String subject;
#Basic(optional = false)
#Lob
#Column(name = "content", nullable = false, length = 65535)
private String content;
#Basic(optional = false)
#Lob
#Column(name = "number", nullable = false, length = 65535)
private String number;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "messageId")
private Collection<Comments> commentsCollection;
public Messages() {
}
And the Comments fields:
package net.bounceme.dur.usenet.controller;
import java.io.Serializable;
import java.util.Date;
import javax.persistence.*;
import javax.xml.bind.annotation.XmlRootElement;
#Entity
#Table(name = "comments", catalog = "nntp", schema = "")
#XmlRootElement
#NamedQueries({
#NamedQuery(name = "Comments.findAll", query = "SELECT c FROM Comments c"),
#NamedQuery(name = "Comments.findById", query = "SELECT c FROM Comments c WHERE c.id = :id"),
#NamedQuery(name = "Comments.findByStamp", query = "SELECT c FROM Comments c WHERE c.stamp = :stamp")})
public class Comments implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#Basic(optional = false)
#Column(name = "id", nullable = false)
#GeneratedValue
private Integer id;
#Basic(optional = false)
#Lob
#Column(name = "comment", nullable = false, length = 65535)
private String comment;
#Basic(optional = false)
#Column(name = "stamp", nullable = false)
#Temporal(TemporalType.DATE)
private Date stamp;
#JoinColumn(name = "message_id", referencedColumnName = "id", nullable = false)
#ManyToOne(optional = false)
private Messages messageId;
public Comments() {
}
For mysql I would recommend you following:
At you table messages at field id add declaration auto_increment:
create table messages(
...
id int not null auto_increment,
...
primary key (id)
)
At entity declaration use
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
This talks to JPA use auto-increment feature of MySQL
If it is not applicable (for example you may want to create related another entity in the same transaction) use TABLE strategy (for more details see http://www.objectdb.com/java/jpa/entity/generated )
The AUTO strategy is an alias for NATIVE if your database supports it, or SEQUENCE if your database supports it, or TABLE if your database doesn't support any of those.
So if the database doesn't support NATIVE and SEQUENCE, then you need to create the table that EclipseLink uses to generate IDs.
With MySQL, NATIVE should be supported. You need to make the ID column an auto_increment column, though. Make sure to configure the appropriate DatabasePlatform as well.