Entity primary key and sequence - oracle

i use oracle and openjpa.
i have an primary key and i want to use a sequence for its value
CREATE TABLE LOG (
ID NUMBER(10) not null,
TIMESTAMP TIMESTAMP DEFAULT (SYSDATE),
constraint PK_ID PRIMARY KEY (ID)
);
CREATE SEQUENCE ID_SEQ
START WITH 1
INCREMENT BY 1
NOCACHE
NOCYCLE;
#Entity
#Table(name="Log")
public class Log implements Serializable {
#Id
#SequenceGenerator(name="SEQ_GEN", sequenceName="ID_SEQ", allocationSize=1)
#GeneratedValue(strategy=GenerationType.SEQUENCE, generator="SEQ_GEN" )
#Column(name="id",nullable=false)
private long id;
#Column(name="timestamp",nullable=false)
private Timestamp timestamp;
public Log(){
}
public Log(Timestamp timestamp){
this.timestamp = timestamp;
}
..
}
#Stateless
public class LogDAO {
#PersistenceContext(unitName="logEntityPU")
private EntityManager em ;
#TransactionAttribute(TransactionAttributeType.REQUIRED)
public boolean insert(Log log){
em.persist(log);
return true;
}
...
...
}
when i check my Log object, id=0 and the timestamp is ok
but i get this error
ORA-01400: integrity constraint violation: NOT NULL check constraint.Insertion of null value not allowed
it's like jpa don't do the link with the sequence.
when i debug more, i see this error: javax.persistence.TransactionRequiredException: No active transaction for PuId=
any idea?

A trigger like this will insert the primary key if one is not provided by the app
CREATE OR REPLACE TRIGGER YOUR_SCHEMA.TRG_YOUR_TABLE
BEFORE INSERT
ON YOUR_SCHEMA.YOUR_TABLE
REFERENCING OLD AS OLD NEW AS NEW
FOR EACH ROW
DECLARE
PrimaryKeyId NUMBER;
BEGIN
IF :NEW.PRIMARY_KEY_ID IS NULL THEN
SELECT your_schema.seq_your_table.nextval
INTO :NEW.PRIMARY_KEY_ID
FROM dual;
END IF;
END YOUR_SCHEMA.TRG_YOUR_TABLE;
Then do a
Select YOUR_SCHEMA.seq_your_table.currval from dual;
to get the value

Related

Strange validation conflict in Spring JPA TableGenerator

I have a legacy database with composite primary key in table project. (BaseEntity contains common properties for lastModifiedDate and lastModifiedBy)
#Entity
#IdClass(ProjectPk.class)
public class Project extends BaseEntity {
#Id
#GeneratedValue(strategy=GenerationType.TABLE, generator="nextProjectId")
#TableGenerator(
name="nextProjectId",
table="projectId",
pkColumnName = "proj_Id",
pkColumnValue="proj_id"
)
private Long projId;
#Id
private int version;
//other properties, getters and setters omitted for clarity
}
PK class
public class ProjectPk implements java.io.Serializable {
private int projId;
private int version;
//both constructoirs, equals, hashcode, getters and setters omitted for clarity
}
I have flyway migration files to simulate production database.
drop table if exists project;
CREATE TABLE project
(
proj_id bigint,
version int,
-- other columns omitted for clarity
PRIMARY KEY (`proj_id`, `version`)
) ENGINE=InnoDB;
drop table if exists project_id;
CREATE TABLE project_id
(
proj_id bigint
) ENGINE=InnoDB;
flyway creates tables as ordered in migration file
Table: project_id
Columns:
proj_id bigint
...
Table: project
Columns:
proj_id bigint PK
version int PK
...
during maven build I'm getting validation error
Schema-validation: wrong column type encountered in column [proj_id] in table [project_id]; found [bigint (Types#BIGINT)], but expecting [varchar(255) (Types#VARCHAR)]
What I did wrong to make hibernate expect [varchar(255) (Types#VARCHAR)]?
This is SpringBoot project 2.6.6 with MySql database
I see the following problems with your code:
Type mismatch between Project.projId (Long type) and ProjectPk.projId (int type).
You use wrong table structure for the project_id table.
You can see a working example below.
Assuming that you have the following tables:
CREATE TABLE test_project
(
proj_id bigint,
version int,
title VARCHAR(50),
PRIMARY KEY (proj_id, version)
);
create table table_identifier (
table_name varchar(255) not null,
product_id bigint,
primary key (table_name)
);
insert into table_identifier values ('test_project', 20);
and the following mapping:
#Entity
#Table(name = "test_project")
#IdClass(ProjectPk.class)
public class Project {
#Id
#GeneratedValue(strategy = GenerationType.TABLE, generator = "nextProjectId")
#TableGenerator(
name="nextProjectId",
table="table_identifier",
pkColumnName = "table_name",
valueColumnName="product_id",
allocationSize = 5
)
#Column(name = "proj_id")
private Long projId;
#Id
private int version;
// other fields, getters, setters ...
}
you will be able to persist the entity like below:
Project project = new Project();
project.setVersion(1);
// ...
entityManager.persist(project);

MyBatis returns some columns as null, 0

I am using MyBatis and Spring Boot. I am trying to extract data from the db using this:
<select id="queryDeviceList" resultType="DeviceList">
SELECT id, mac_address, is_active
FROM ct_device_list dl
where dl.is_active = 1
</select>
and my POJO is
#Data
public class DeviceList {
private int id;
private String mac_address;
private int is_active;
}
and my DAO is
List <DeviceList> queryDeviceList();
and my table CT_DEVICE_LIST in Oracle db has
CREATE TABLE CT_DEVICE_LIST
(
ID NUMBER(10,0) NOT NULL
, MAC_ADDRESS VARCHAR2(17) NOT NULL
, IS_ACTIVE NUMBER(1) NOT NULL
, CREATED_DATE DATE NOT NULL
, CONSTRAINT CT_DEVICE_LIST_PK PRIMARY KEY
(
ID
)
ENABLE
);
But the results I got is:
id=1, mac_address=null, is_active = 0
but my mac_address has value and is_active is not 0 in the database.
Please help. Thanks.
if you set mybatis.configuration.map-underscore-to-camel-case=true, you should use
#Data
public class DeviceList {
private Integer id;
private String macAddress;
private Integer isActive;
}
Remember not to use primitive type int, if you don't want set default to 0.

Cannot get many to many relationship in spring bootstrap

I have a very simple many to many scenario: One ORDER has many PRODUCT, and each product can belong to many orders.
order :
#Entity
#Table(name = "ORDER")
public class OrderEntity {
#Id
#Column(name="ORDER_ID")
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
#Column(name="ORDER_NAME")
private String name;
#Column(name="ORDER_DATE")
private Date date;
#ManyToMany
private List<ProductEntity> selectedProducts = new ArrayList<>();
product:
#Entity
#Table(name = "PRODUCT")
public class ProductEntity {
#Id
#Column(name="PRODUCT_ID")
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
#Column(name="PRODUCT_NAME")
private String name;
#Column(name="PRODUCT_PRICE")
private BigDecimal price;
#ManyToMany
private List<OrderEntity> orders = new ArrayList<>();
(removed getters and setters and constructors for brevity)
However when I startup bootstrap then I get a whole host of errors :
Error executing DDL "drop table order if exists" via JDBC Statement
Syntax error in SQL statement "DROP TABLE ORDER[*] IF EXISTS "; expected "identifier"; SQL statement:
Error executing DDL "create table order (order_id bigint not null, order_date timestamp, order_name varchar(255), primary key (order_id))" via JDBC Statement
Syntax error in SQL statement "CREATE TABLE ORDER[*] (ORDER_ID BIGINT NOT NULL, ORDER_DATE TIMESTAMP, ORDER_NAME VARCHAR(255), PRIMARY KEY (ORDER_ID)) "; expected "identifier"; SQL statement:
create table order (order_id bigint not null, order_date timestamp, order_name varchar(255), primary key (order_id)) [42001-199]
Error executing DDL "alter table order_selected_products add constraint FKrbll8c9ubhjqangdfw2sgkurw foreign key (order_entity_order_id) references order" via JDBC Statement
Syntax error in SQL statement "ALTER TABLE ORDER_SELECTED_PRODUCTS ADD CONSTRAINT FKRBLL8C9UBHJQANGDFW2SGKURW FOREIGN KEY (ORDER_ENTITY_ORDER_ID) REFERENCES ORDER[*] "; expected "identifier"; SQL statement:
alter table order_selected_products add constraint FKrbll8c9ubhjqangdfw2sgkurw foreign key (order_entity_order_id) references order [42001-199]
Error executing DDL "alter table product_orders add constraint FK9pa3r9u6x44jjxrkkhdvhu23k foreign key (orders_order_id) references order" via JDBC Statement
Syntax error in SQL statement "ALTER TABLE PRODUCT_ORDERS ADD CONSTRAINT FK9PA3R9U6X44JJXRKKHDVHU23K FOREIGN KEY (ORDERS_ORDER_ID) REFERENCES ORDER[*] "; expected "identifier"; SQL statement:
alter table product_orders add constraint FK9pa3r9u6x44jjxrkkhdvhu23k foreign key (orders_order_id) references order [42001-199]
I'm not sure why there are these syntax errors. Is this some kind of SQL dialect issue?
ORDER is a very common reserved keyword and that is the root cause of the errors you see.
Change your table name to something else, such as ORDERS, or if you really want to use that name you can try escaping it:
#Entity
#Table(name = "\"ORDERS\"")
public class OrderEntity {
....
}
List of reserved keywords for some common databases:
https://docs.oracle.com/cd/B28359_01/appdev.111/b31231/appb.htm#BABDFFBA
https://learn.microsoft.com/en-us/sql/t-sql/language-elements/reserved-keywords-transact-sql?view=sql-server-2017
https://www.postgresql.org/docs/current/sql-keywords-appendix.html
https://dev.mysql.com/doc/refman/8.0/en/keywords.html

Spring Data JPA + Oracle Trigger increments the ID twice

I use the following tech stack:
spring-boot-starter-data-jpa
HikariCP for connection pooling
Oracle DB
My actual code looks similar to this.
/// My trigger looks like this
CREATE OR REPLACE TRIGGER FILE_BRI
BEFORE INSERT
ON FILE
FOR EACH ROW
BEGIN
SELECT FILE_SEQ.NEXTVAL INTO :NEW.ID FROM DUAL;
END;
///
public class FILE implements Serializable {
#Id
#SequenceGenerator(
name = "FILE_SEQ",
sequenceName = "FILE_SEQ",
allocationSize = 1)
#GeneratedValue(
strategy = GenerationType.SEQUENCE,
generator = "FILE_SEQ"
)
private long id;
}
public class ServiceA () {
#Transactional(propagation = REQUIRES_NEW, isolation = READ_COMMITTED)
public File insertFile() {
// Below line returns the inserted File object with ID as '58496'
return fileRepository.save(file)
}
#Transactional(propagation = REQUIRES_NEW, isolation = READ_COMMITTED)
public AccessControl insertAccessControl() {
// Below line results in 'SQLIntegrityConstraintViolationException' (full error at the bottom of this post)
return accessControlRepository.save(accessControlFile)
}
}
Public class FileProcessor() {
ServiceA serviceA;
public void someMethod() {
// insert the file and get the inserted record
File insertedFile = serviceA.insertFile(file);
// get the ID from the inserted file and make another insert into another table
serviceA.insertAccessControl(insertedFile.getId()); // inserted file ID is '58496'
}
}
This is my investigation:
When I verified the ID of the inserted record in the table "FILE" is '58497', however repository.save() returned a different value.
When I make the second insert on table "ACCESS_CONTROL_FILE" with FILE_ID as '58496' it results in the error below because the FILE with ID as '58496' does not exist.
Caused by: java.sql.SQLIntegrityConstraintViolationException: ORA-01400: cannot insert NULL into ("DB_OWNER"."ACCESS_CONTROL_FILE"."FILE_ID")
I'm puzzled as to why would repository.save() return a different ID(i.e. ID=58496) than what is actually inserted(ID=58497) in the database!
I've investigated all options that I could find on the internet related to 'Propagation and Isolation'.
As mentioned in comments, Looks like a database trigger is causing the issue. Disable the trigger to let JPA to manage the ID generation.

JPA not returning the records which contain empty columns in the EmbeddedId

JpaRepository's findAll() method does not return the rows, if any of the field in the composite key is null.
This is the entity class with the EmbeddedId JobVaccinationPK
/**
* ApplicationParam entity. #author MyEclipse Persistence Tools
*/
#Entity
#Table(name="job_vaccination",schema="cdcis")
#SuppressWarnings("serial")
public class JobVaccination implements java.io.Serializable {
// Fields
#Column(name="default_yn", length=1)
private String defaultYn;
#EmbeddedId
private JobVaccinationPK jobVaccinationPK;
public JobVaccination(){
}
//setters getters
}
This is the Embedded class
#Embeddable
#SuppressWarnings("serial")
public class JobVaccinationPK implements Serializable{
#ManyToOne
#MapsId("job_category_id")
#JoinColumn(name = "job_category_id", nullable=true)
private JobCategoryTypeMast jobCategoryMast;
#ManyToOne
#MapsId("vaccination_id")
#JoinColumn(name = "vaccination_id", nullable=true)
private VaccinationMast vaccinationMast;
#ManyToOne
#MapsId("screening_type_id")
#JoinColumn(name = "screening_type_id", nullable=true)
private ScreeningTypeMast screeningTypeMast;
//getters and setters
}
Service implementation class
#Override
public SearchResult<JobVaccinationDto> getJobVaccination(JobVaccinationDto dto)
throws VaccinationException {
List<JobVaccination> vaccDetails = jobVaccinationRepo.findAll();
if(vaccDetails == null) return null;
List<JobVaccinationDto> jobVaccinationDtos = new ArrayList<JobVaccinationDto>();
jobVaccinationDtos = convertToDto(vaccDetails);
return new SearchResult<>(jobVaccinationDtos.size(), jobVaccinationDtos);
}
Here am able to insert a null value for either jobCategoryId or screeningTypeId, just like below row. But when I'm trying to fetch the rows which have empty values, it returns null. I've tried to debug but I was not able to find the cause.
This is the generated hibernate query:
Hibernate:
select
jobvaccina0_.job_category_id as job_cate4_13_,
jobvaccina0_.screening_type_id as screenin2_13_,
jobvaccina0_.vaccination_id as vaccinat3_13_,
jobvaccina0_.default_yn as default_1_13_
from
cdcis.job_vaccination jobvaccina0_
Hibernate:
select
jobcategor0_.job_category_id as job_cate1_11_0_,
jobcategor0_.job_category_name as job_cate2_11_0_,
jobcategor0_.job_category_name_ar as job_cate3_11_0_,
jobcategor0_.screening_type_id as screenin4_11_0_
from
cdcis.job_category_mast jobcategor0_
where
jobcategor0_.job_category_id=?
Hibernate:
select
screeningt0_.screening_type_id as screenin1_21_0_,
screeningt0_.active_yn as active_y2_21_0_,
screeningt0_.mmpid_required_yn as mmpid_re3_21_0_,
screeningt0_.screening_type as screenin4_21_0_
from
cdcis.screening_type_mast screeningt0_
where
screeningt0_.screening_type_id=?
Hibernate:
select
vaccinatio0_.vaccination_id as vaccinat1_27_0_,
vaccinatio0_.vaccination_name as vaccinat2_27_0_,
vaccinatio0_.vaccination_name_ar as vaccinat3_27_0_
from
cdcis.vaccination_mast vaccinatio0_
where
vaccinatio0_.vaccination_id=?
Going with #Adam Michalik answer. As a work-around I've introduced a new primary key field in the table, as we can't handle a null in the composite key.
Composite IDs cannot contain null values in any of the fields. Since the SQL semantics of NULL is that NULL <> NULL, it cannot be determined that a primary key (1, 2, NULL) is equal to (1, 2, NULL).
NULL means "no value" in SQL and its interpretation is up to you on a case-by-case basis. That's why SQL and JPA do not want to make assumptions that NULL = NULL and that a primary key containing a NULL identifies a single entity only.
You may choose to use a synthetic, generated primary key instead of the composite business primary key to overcome that. Then, you'd always have a non-null, single-column PK and nullable foreign keys.
change the data type of particular row in entity from int to integer

Resources