Get list of database errors on saveAll() - spring-boot

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.

Related

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?

JPA query after save doesn't return database generated field

I'm trying to save this model to a database, and then retrieve what I just saved. Every field is retrieved except for the database generated UUID field.
#Entity
#Table(name = "usertoken")
public class UserToken implements Serializable
{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
#Column(name = "token", insertable = false, updatable = false, nullable = false)
private UUID token;
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name="usersid", nullable=false)
private User user;
#Column(name = "expiration", updatable = false, nullable = false)
private LocalDateTime expiration;
I save the token from within the service
token = tokenRepository.save(token);
which generates this SQL
Hibernate:
insert
into
usertoken
(expiration, usersid)
values
(?, ?)
The next statement gets the token
token = tokenRepository.findByUser(user);
I see the SQL select includes the token field
Hibernate:
select
usertoken0_.id as id1_8_,
usertoken0_.expiration as expirati2_8_,
usertoken0_.token as token3_8_,
usertoken0_.usersid as usersid4_8_
from
usertoken usertoken0_
where
usertoken0_.usersid=?
...but the returned object has every field populated BUT the token. The database does have a value in the token column.
I'm at a loss as to why it would populate every field but one.
Here's the table in question:
CREATE TABLE public.usertoken
(
id integer NOT NULL GENERATED ALWAYS AS IDENTITY ( INCREMENT 1 START 1 MINVALUE 1 MAXVALUE 2147483647 CACHE 1 ),
usersid integer NOT NULL,
token uuid NOT NULL DEFAULT uuid_generate_v1(),
expiration timestamp without time zone NOT NULL,
CONSTRAINT "usertoken_pkey" PRIMARY KEY (id)
)
I forgot to add that when I query later on the token is found and the UUID field is properly populated. So something weird with JPA caching? Are database DEFAULT column values ignored by hibernate after the insert?
tokenRepository.findByToken(UUID.fromString(userToken));
Hibernate:
select
usertoken0_.id as id1_8_,
usertoken0_.expiration as expirati2_8_,
usertoken0_.token as token3_8_,
usertoken0_.usersid as usersid4_8_
from
usertoken usertoken0_
where
usertoken0_.token=?
You have to signal hibernate that field is being generated by database. So add the following:
#org.hibernate.annotations.Generated(value = GenerationTime.INSERT)
#Column(name = "token", insertable = false,
updatable = false, nullable = false)
private UUID token;
You will also see hibernate issues a select just for that column after insert

Manage String id sequence in spring boot entity

I'm working on oracle database to manage a JPA entity with a String Primary key.
I cannot modify the type on the PK to a Long or int in the database, so i want to know how to configure the pk sequence in my JPA entity,
i've tried this :
#Id
#SequenceGenerator(name="SEQ_ID", sequenceName = "SEQ_ID" )
#GeneratedValue(strategy=GenerationType.SEQUENCE, generator="SEQ_ID")
#Column(name="SEQ_ID",unique=true, nullable = false,updatable = false)
private String id;
but when persisting a new entity i got the error : Unknown integral data type for ids : java.lang.String
someone can help me please ?
Try removing #GeneratedValue and #SequenceGenerator
Also, a remark, #Id will automatically set unique=true, nullable = false,updatable = false so you can remove them from #Column.
Otherwise, you can check this article for more details about creating a custom string generator https://vladmihalcea.com/how-to-implement-a-custom-string-based-sequence-identifier-generator-with-hibernate/

Is #ManyToOne's "optional" param automatically set using Kotlin's nullability

I read that specifying optional = false in the #ManyToOne association annotation could help Spring improve the performance of the queries.
In a Kotlin data class entity, do I actually need to specify the parameter in the annotation, or can Spring figure this out by itself using the nullability of the item field?
For instance, if I have the following declaration:
#Entity
#Table(name = ACCESS_LOGS_ARCHIVES_TABLE, indexes = [
Index(name = "access_logs_archives_item_idx", columnList = "access_item_id")
])
data class AccessLogArchive(
val date: LocalDate,
#ManyToOne(optional = false)
#JoinColumn(name = "access_item_id", nullable = false)
val item: AccessLogItem,
val occurrences: Int
) {
#Id
#GeneratedValue
var id: Long? = null
}
#Entity
#Table(name = ACCESS_ITEMS_TABLE)
data class AccessLogItem(
#Column(length = 3) val code: String,
#Column(columnDefinition = "text") val path: String,
#Column(length = 10) val verb: String
) {
#Id
#GeneratedValue
var id: Long? = null
}
In this case, I would for instance expect Spring to know that the item field is not nullable, and thus the relationship should be understood as optional=false even without specifying it as I did. Is this the case?
Same question goes for the #JoinColumn's nullable = false, by the way.
Consider a simple entity like a Room which has a #ManyToOne relationship to House.
#Entity
class Room(
#ManyToOne(optional = true)
val house: House
) {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
val id: Long = 0
}
JPA will create a room table with a column
`house_id` bigint(20) DEFAULT NULL
If you specify #ManyToOne(optional = false)
the column will look like this:
`house_id` bigint(20) NOT NULL
By specifiying optional you tell JPA how the schema should be generated, whether the column can be NULL or not.
At runtime trying to load a Room without a House will cause an Exception if the house property is not nullable (House instead of House?) even when value of optional is true.
The same applies to #JoinColumn.
Is #ManyToOne's “optional” param automatically set using Kotlin's
nullability?
No it is not. It is independent from that and by default set to true.
Conclusion: In order for you schema to reflect your entities it is a good idea to use optional = true if the house property would be nullable and optional = false if the house property would be non-nullable.

Spring Boot + PostgreSql Sequence not available

I am working in a project developed with Spring Boot and PostgreSql as Database.
I have created a sequence user_seq in PostgreSql for my user table
CREATE SEQUENCE "USER_MGMT"."USER_SEQ"
INCREMENT 1
START 1000
MINVALUE 1000
MAXVALUE 99999999
CACHE 1;
ALTER SEQUENCE "USER_MGMT"."USER_SEQ"
OWNER TO postgres;
Here is my user Table
CREATE TABLE "USER_MGMT"."USER"
(
"USER_ID" bigint NOT NULL DEFAULT nextval('"USER_MGMT"."USER_SEQ"'::regclass),
"FIRST_NAME" character varying(30) COLLATE pg_catalog."default" NOT NULL,
"LAST_NAME" character varying(50) COLLATE pg_catalog."default" NOT NULL,
"EMAIL_ID" character varying(70) COLLATE pg_catalog."default" NOT NULL,
"DESK_NUMBER" bigint,
"MOBILE_NUMBER" bigint,
"IS_ACTIVE" boolean NOT NULL DEFAULT true,
"CREATED_BY" character varying(70) COLLATE pg_catalog."default",
"MODIFIED_BY" character varying(70) COLLATE pg_catalog."default",
"DATE_CREATED" timestamp without time zone,
"DATE_MODIFIED" timestamp without time zone,
CONSTRAINT "USER_ID_PK" PRIMARY KEY ("USER_ID"),
CONSTRAINT "EMAIL_ID_UK" UNIQUE ("EMAIL_ID"),
CONSTRAINT "MOBILE_NUMBER_UK" UNIQUE ("MOBILE_NUMBER")
)
WITH (
OIDS = FALSE
)
TABLESPACE pg_default;
ALTER TABLE "USER_MGMT"."USER"
OWNER to postgres;
I am creating an JPA entity for this user table,
#Entity
#Table(name = "`USER`")
#ToString
#EqualsAndHashCode
public class User extends Auditable<String> {
/**
*
*/
private static final long serialVersionUID = 5351767213835401599L;
#Id
#SequenceGenerator(name = "USER_ID", sequenceName = "USER_SEQ", allocationSize = 1)
//Tried this also but not works
#SequenceGenerator(name = "USER_ID", sequenceName = "`USER_SEQ`", allocationSize = 1)
#GeneratedValue(generator = "USER_ID", strategy = GenerationType.SEQUENCE)
#Column(name = "`USER_ID`")
private Long userId;
#Column(name = "`FIRST_NAME`")
private String firstName;
While inserting data to this user table, I am getting error says user_seq is not available.
ERROR: relation "USER_MGMT.user_seq" does not exist
It tooks as lower case even though I mentioned in Upper case in entity.This is must for upper case for all the tables and sequences. Is there anything I missed in entity?

Resources