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.
Related
I'm using Spring boot, and I Run this model.
package com.example.demo.Models;
import jakarta.persistence.*;
#Entity
#Table(name = "user")
public class UserModel {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(unique = true, nullable = true)
private Long id;
private String name;
private String email;
private Integer priority;
/* Here are all the setters and getters*/
}
application.properties:
spring.datasource.url=jdbc:postgresql://localhost:5432/dbdemo
spring.datasource.username=postgres
spring.datasource.password=password
spring.jpa.hibernate.ddl-auto=update
Everything fine with Java.
Process finished with exit code 0
But in the console of my docker image of Postgres I get the following error:
ERROR: syntax error at or near "user" at character 14
STATEMENT: create table user(id bigserial not null, email varchar(255), name varchar(255), priority integer, primary key (id))
I'm not sure how to solve it, I'd appreciate any help.
create table public.user ( id serial not null,
email varchar(255),
name varchar(255),
priority integer,
primary key (id))
add the schema name before the table name
You need to specify the schema name in table and id fields like this:
#Table(name = "anomaly", schema = "schema_name")
public class Anomaly {
#Id
#SequenceGenerator(name = "id", allocationSize = 1, schema = "schema_name")
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private Long anomalyId;
user is a reserved word in Postgresql thats why it show an error
use a different word like "users" "usersId".
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.
I am playing a little bit with unidirectional and bidirectional mappings using #OneToMany and #ManyToOne annotations, but I cannot break the wall through for unidirectional one when persisting entities and flushing them into the database.
So, two tables delivery_company might have many delivery:
SQL (Oracle):
CREATE TABLE delivery (
delivery_id NUMBER(6) NOT NULL,
price NUMBER(5, 2) NOT NULL,
delivery_time DATE NOT NULL,
delivery_company_id NUMBER(2) NOT NULL
);
ALTER TABLE delivery ADD CONSTRAINT delivery_pk PRIMARY KEY ( delivery_id );
CREATE TABLE delivery_company (
delivery_company_id NUMBER(2) NOT NULL,
delivery_company_name VARCHAR2(15 CHAR) NOT NULL
);
ALTER TABLE delivery_company ADD CONSTRAINT delivery_company_pk PRIMARY KEY ( delivery_company_id );
ALTER TABLE delivery
ADD CONSTRAINT delivery_delivery_company_fk FOREIGN KEY ( delivery_company_id )
REFERENCES delivery_company ( delivery_company_id );
Unidirectional mapping:
#Entity
#Table(name = "Delivery")
class DeliveryUniDirectional
{
#Id
#SequenceGenerator(name = "delivery_id_sequence", sequenceName = "delivery_id_sequence", allocationSize = 1)
#GeneratedValue(generator = "delivery_id_sequence", strategy = GenerationType.SEQUENCE)
#Column(name = "delivery_id")
public Long deliveryId;
public BigDecimal price;
#Temporal(TemporalType.DATE)
public Date deliveryTime;
// setters, getters
}
#Entity
#Table(name = "delivery_company")
class DeliveryCompanyUniDirectional {
#Id
#Column(name = "delivery_company_id")
#SequenceGenerator(name = "delivery_company_id_sequence", sequenceName = "delivery_company_id_sequence", allocationSize = 1)
#GeneratedValue(generator = "delivery_company_id_sequence", strategy = GenerationType.SEQUENCE)
private Long deliveryCompanyId;
#Column(unique = true)
private String deliveryCompanyName;
#OneToMany(cascade = CascadeType.ALL)
#JoinColumn(name = "delivery_id", nullable = false, insertable = false, updatable = false)
private List<DeliveryUniDirectional> deliveries = new LinkedList<>();
// setters getters
}
When I run #DataJpaTest test:
#Test
public void insertDeliveryUniDirectional()
{
DeliveryCompanyUniDirectional deliveryCompany = new DeliveryCompanyUniDirectional();
deliveryCompany.setDeliveryCompanyName("aa");
DeliveryUniDirectional delivery = new DeliveryUniDirectional();
delivery.setPrice(BigDecimal.ONE);
delivery.setDeliveryTime(new Date());
deliveryCompany.getDeliveries().add(delivery);
entityManager.persist(deliveryCompany);
entityManager.flush();
}
I receive
javax.persistence.PersistenceException: org.hibernate.exception.ConstraintViolationException: could not execute batch ...
//
Caused by: java.sql.BatchUpdateException: ORA-01400: cannot insert NULL into ("TESTUSER"."DELIVERY"."DELIVERY_COMPANY_ID")
when entityManager.flush();.
I tried in DeliveryCompanyUniDirectional to use #JoinColumn without insertable and updatable, but in that case hibernate complains:
Error creating bean with name 'entityManagerFactory' defined in class path resource ...
// ...
Caused by: org.hibernate.MappingException: Repeated column in mapping for entity: wieczorek.jakub.shop.business.spring.model.domain.DeliveryUniDirectional column: delivery_id (should be mapped with insert="false" update="false")
Definitely there is a problem with NOT NULL constraint for the foreign key in delivery table. When I try it with bidirectional mapping, persisting and flushing work very good, but I would like to achieve the same using unidirectional.
Thanks for reading
Your #JoinColumn should be delivery_company_id since it's your foreign key
#OneToMany(cascade = CascadeType.ALL)
#JoinColumn(name="delivery_company_id", referencedColumnName="delivery_company_id", nullable = false)
private List<DeliveryUniDirectional> deliveries = new LinkedList<>();
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 need to find all the products that do not contain a specific allergen using Hibernate.
Here is the SQL used to create the database tables:
CREATE TABLE ALLERGEN (id integer IDENTITY PRIMARY KEY, name varchar(20), UNIQUE (id), UNIQUE(name));
CREATE TABLE PRODUCT (id integer IDENTITY PRIMARY KEY, name varchar(20), UNIQUE (id), UNIQUE(name));
CREATE TABLE PRODUCT_ALLERGEN (product_id integer, allergen_id integer, UNIQUE (product_id, allergen_id), FOREIGN KEY (product_id) REFERENCES PRODUCT (id), FOREIGN KEY (allergen_id) REFERENCES ALLERGEN (id));
Here are the Hibernate annotated Java classes:
#Entity
#Table(name = "ALLERGEN")
class Allergen {
#Id
#Column(unique = true, nullable = false)
#GeneratedValue
private Integer id;
private String name;
// ...
}
#Entity
#Table(name = "PRODUCT")
public class Product {
#Id
#Column(unique = true, nullable = false)
#GeneratedValue
private Integer id;
private String name;
#ManyToMany(fetch = FetchType.EAGER)
#JoinTable(inverseJoinColumns = {#JoinColumn(name = "allergen_id")})
private final Set<Allergen> allergens = new HashSet<>();
// ...
}
This SQL appears to give me the result I want, but I don't see how to represent it using Hibernate criteria.
SELECT * FROM PRODUCT WHERE (SELECT COUNT(*) FROM PRODUCT_ALLERGEN WHERE product_id = PRODUCT.id AND allergen_id = 0) = 0;
With the Criteria API you should be able to get all Product without Allergens by creating a left join from Product to Allergen and checking if it is null:
final CriteriaBuilder builder = entityManager.getCriteriaBuilder();
final CriteriaQuery<Product> c = builder.createQuery(Product.class);
final Root<Product> root = c.from(Product.class);
Join<Product, Allergen> allergenJoin = root.join("allergens", JoinType.LEFT);
c.where(builder.isNull(allergenJoin));
c.select(root);
List<Product> = entityManager.createQuery(c).getResultList();
Note: I didn't include where you get the EntityManager from. Usually I use injection for that, but there are other methods like using a factory.
This code uses JPQL to get the products without a specific allergen.
List<Product> results = manager.createQuery(
"SELECT p from Product AS p WHERE (SELECT COUNT(a) FROM p.allergens a WHERE a.name = :an) = 0",
Product.class)
.setParameter("an", "nuts")
.getResultList();