Strange validation conflict in Spring JPA TableGenerator - spring-boot

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);

Related

Error while testing with H2 database with Reactive Spring boot

I am using Reactive Spring boot to create a REST API and while the API works perfectly (I have tested it with postman), I also need to write some Unit tests and for the Unit tests I need to use a seperate in-memory database for it. I am using H2 database for testing and a hosted Postgres DB for the actual application and while my application entities are being loaded successfully in the postgres DB, None of my tests are passing as I receieve the following error:
Failed to execute SQL script statement #1 of class path resource [data.sql]: CREATE TABLE IF NOT EXISTS full_connection ( id BIGINT PRIMARY KEY , aa TEXT NOT NULL, fip TEXT NOT NULL, status TEXT, created_on TIMESTAMP, created_by TEXT, last_updated_on TIMESTAMP, last_updated_by TEXT ); nested exception is java.lang.ClassCastException: class java.lang.Long cannot be cast to class java.lang.Integer (java.lang.Long and java.lang.Integer are in module java.base of loader 'bootstrap')
I am assuming the error is being thrown because of the id paramater which has the datatype BIGINT which the official documentation of H2 says is mapped to java.long.Long.
Here's my entity:
#Getter
#Setter
#NoArgsConstructor
#AllArgsConstructor
#Builder
#ToString
#Table
public class FullConnection {
#Id
#Column(name = "id")
private Long id;
#Column(name = "AA", nullable = false)
private String aa;
#Column(name = "FIP", nullable = false)
private String fip;
private String status;
private LocalDateTime createdOn;
private String createdBy;
private LocalDateTime lastUpdatedOn;
private String lastUpdatedBy;
}
Here's how I am initializing the data:
#Bean
ConnectionFactoryInitializer initializer(#Qualifier("connectionFactory") ConnectionFactory connectionFactory) {
ConnectionFactoryInitializer initializer = new ConnectionFactoryInitializer();
initializer.setConnectionFactory(connectionFactory);
ResourceDatabasePopulator resource =
new ResourceDatabasePopulator(new ClassPathResource(dataInitializerName));
initializer.setDatabasePopulator(resource);
return initializer;
}
where it's reading the dataInitializerName string from application-test.yaml and is being read correctly, the script (for testing)(the script for prod and testing are different) is:
CREATE TABLE IF NOT EXISTS full_connection (
id BIGINT PRIMARY KEY ,
aa TEXT NOT NULL,
fip TEXT NOT NULL,
status TEXT,
created_on TIMESTAMP,
created_by TEXT,
last_updated_on TIMESTAMP,
last_updated_by TEXT
);
Here: https://www.h2database.com/html/datatypes.html#bigint_type it clearly states that BIGINT is mapped to java.long.Long, then why is it trying to cast it to int? Or is it regarding some other field?
I had a similar problem. Explicitly defining the version for the dependency r2dbc-h2 in pom.xml is causing the problem. I removed the version so that springboot will automatically use the compatible version of r2dbc-h2, and then the problem resolved.

Get list of database errors on saveAll()

I have a CountryObject:
#Entity
#Table(name = "country")
class CountryObject(
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id", nullable = false)
val id: Long? = null,
#Column(name = "name")
val name: String,
#Column(name = "ISO2", length = 2)
val ISO2: String,
#Column(name = "ISO3", length = 3)
val ISO3: String
) : Serializable
with the corresponding PostgreSQL create script that has some unique constraints on ISO2 and ISO3:
CREATE TABLE country
(
id BIGINT NOT NULL,
name VARCHAR(255) NOT NULL,
iso2 VARCHAR(2) NOT NULL,
iso3 VARCHAR(3) NOT NULL,
CONSTRAINT pk_country PRIMARY KEY (id)
);
ALTER TABLE country
ADD CONSTRAINT UNIQUE_CONSTRAINT_ISO2 UNIQUE (iso2);
ALTER TABLE country
ADD CONSTRAINT UNIQUE_CONSTRAINT_ISO3 UNIQUE (iso3);
Along with this I have a corresponding repository:
#Repository
interface CountryRepository : JpaRepository<CountryObject, Long>
And out of the box implementation of the CountryRepository has a saveAll() method that tries to insert a list of objects into the country table. So far, everything behaves as expected.
Once I try to insert a list that have multiple objects violating the unique constraints, the exception is thrown on the first and stopped. What I want to have is to get some list with the errors that I can use as a report, which mentions which entries failed saving. Of course nothing should be saved in that case and the transaction is rolled back.
Any hints how something like this can me achieved? Of course one option would be to try to save each object separately and then collect the errors, but that might be not that performant.

jpa generated schema doesn't include property of extended class

I've a very complex database which i will try to resume in here
#Embeddable
open class ChargeableDTO(
#NotBlank var name: String,
#NotBlank var ref: String,
#Min(1) var priceCents: Int,
#NotNull #Min(1) #Max(12) var maxInstallments: Int = 1,
#NotNull var gateway: PaymentGateway) {
#Embeddable
class CreditPackageDTO(name: String,
ref: String,
priceCents: Int,
maxInstallments: Int = 1,
gateway: PaymentGateway,
#Min(1) var creditAmount : Int) : ChargeableDTO(name, ref, priceCents, maxInstallments, gateway) {
#Entity
#Table(name = "credit_packages", uniqueConstraints = [UniqueConstraint(columnNames = ["gateway", "ref"])])
class CreditPackage(dto: CreditPackageDTO) : ChargeableEntity(dto)
#Entity
#Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
abstract class ChargeableEntity(#field:Embedded open var dto: ChargeableDTO) : HydraEntity()
many other classes that are not relevant to this problem....
but when running the schema generation script hibernate generates a code like
create table credit_packages (
id bigint not null,
created_date datetime(6),
last_modified_date datetime(6),
public_id varchar(255),
gateway integer,
max_installments integer not null,
name varchar(255),
price_cents integer not null,
ref varchar(255),
primary key (id)
) engine=InnoDB
the first 4 fields come from a parent class which all my entities inherit from.
but this schema complete ignores the property creditAmount which is defined in the extended dto
also this code doesn't metion the limit 1 to 12 for maxinstallments
am i doing anything wrong, how can i fix it?

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

Resources