Constraints in ebean java play framework not reflected in generated sql - java

I am using ebean ORM in my java based play framework and following is my model
package models;
import java.util.*;
import javax.persistence.*;
import com.avaje.ebean.annotation.CreatedTimestamp;
import com.avaje.ebean.annotation.UpdatedTimestamp;
import play.db.ebean.*;
import play.data.validation.*;
#Entity
#Table(name = "coupons")
public class Coupon extends com.avaje.ebean.Model {
private static final long serialVersionUID = 1L;
#Id
private Long id;
#Constraints.Required
#Constraints.MaxLength(80)
#Constraints.MinLength(10)
private String title;
#Constraints.Required
#Constraints.MaxLength(1000)
#Constraints.MinLength(10)
private String description;
#Column(name = "created_at")
#CreatedTimestamp
private Date createdAt;
#Column(name = "updated_at")
#UpdatedTimestamp
private Date updatedAt;
#Column(name = "valid_from")
private Date validFrom = new Date();
#Column(name = "valid_to")
private Date validTo = new Date((long)2147483647*1000);
}
Generated sql file is
create table coupons (
id bigserial not null,
title varchar(255),
description varchar(255),
valid_from timestamp,
valid_to timestamp,
created_at timestamp not null,
updated_at timestamp not null,
constraint pk_coupons primary key (id)
);
drop table if exists coupons cascade;
Does ebean convert the constraints into sql file? How do I make sure my constraints are reflected in sql file also?

Ebean does not read any of the Play constraint annotations.
#Constraints.Required
#Constraints.MaxLength(80)
So the Required and MaxLength are not read and hence don't effect the generated DDL.
Ebean reads the JPA annotations and the javax.validation.constraints annotations #NotNull and #Size. For example, you could use:
#NotNull
#Size(max = 80)

Ebean uses only JPA Annotations.
You can check out the docs for the #Column annotation, which has options such as
#Column(nullable = false)
However, there are no options for minimum or maximum string length, or to ensure that the string is not empty.

Related

Spring Boot Data JDBC field as json value

I have a model which i want to save to database.
#Data
public class Model {
#Id
private UUID id;
private String name;
private ModelSettings settings;
#Data
static class ModelSettings {
boolean fuelEngine;
}
}
create table model
(
id uuid not null,
name varchar(25) not null,
settings jsonb
)
i try to save modelSettings as jsonb object using simple repository method save(), but i got error
ERROR: relation "settings" does not exist
i wrote custom Converter and i see when modelSettings is converted to json, but after prepare statement Spring Data try to save settings field to related table. How to tell Spring Data save field as json only, not row in related table?
Sorry, i forgot #WritingConverter with JdbcValue.
Hi Please use Embedded Annotation:
Embedded entities are used to have value objects in your java data model, even if there is only one table in your database.
#Data
public class Model {
#Id
private UUID id;
private String name;
#Embedded(onEmpty = USE_NULL)
private ModelSettings settings;
#Data
static class ModelSettings {
boolean fuelEngine;
}
}
You can not have an object in your entity and expect JDBC to save it as json for you.
you need to define a String column and write a converter for it for saving in and reading from database.
also some databases like Oracle supports json values but you have not mentioned which database you are using.
in Oracle database you can define a table including a json column as below:
CREATE TABLE "USERS"
(
"ID" NUMBER(16,0) PRIMARY KEY,
"USER_NAME" VARCHAR2(85) UNIQUE NOT NULL,
"PASSWORD" VARCHAR2(48) NOT NULL,
"PROFILE" NCLOB NOT NULL CONSTRAINT profile_json CHECK ("PROFILE" IS JSON),
"SETTINGS" NCLOB NOT NULL CONSTRAINT settings_json CHECK ("SETTINGS" IS JSON),
);
And you need to create your entity class as below:
#Entity
#Table(name = "Users")
public class User {
#Id
#Column(name = "ID")
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "USER_GENERATOR")
#SequenceGenerator(name = "USER_GENERATOR", sequenceName = "USERS_SEQ", allocationSize = 1)
private Long id;
#Column(name = "USER_NAME")
private String userName;
#Column(name = "PASSWORD")
private String password;
#Lob
#Nationalized
#Column(name = "PROFILE",columnDefinition="NCLOB NOT NULL")
private String profile;
#Lob
#Nationalized
#Column(name = "SETTINGS",columnDefinition="NCLOB NOT NULL")
private String settings;
}
as you can see here profile and setting are my json columns.

how to make #Lob annotation compatible for both oracle and postgresql

My code is designed to support both oracle and postgresql, but the #Lob annotation behaved different from oracle to pg, in oracle #Lob with Java String Object type works well, but in pg, I should add annotation #Type(type ="org.hibernate.type.TextType") to make String mapping to pg text format, which makes it not compatible with oracle.
How to make it possible to support oracle and pg in just one JPA entity class?
Here is my entity class.
package hello.world.demo3;
import lombok.Getter;
import lombok.Setter;
import org.hibernate.annotations.Type;
import javax.persistence.*;
#Entity
#Table(name = "my_task")
#Getter
#Setter
public class Task {
#Id
#GeneratedValue
#Column(name = "i_id", nullable = false)
private Long id;
#Lob
#Column(name = "c_lob")
private String lob;
#Lob
#Type(type ="org.hibernate.type.TextType")
#Column(name = "c_text")
private String text;
}
I suggest using the columnDefinition in the #Column annotation to specify that the field should be TEXT type in the underlying SQL table.
#Column(name = "c_text", columnDefinition = "TEXT")
private String text;
Note that specifying #Lob will probably not give you the behavior you want in Postgres. In Postgres, using #Lob will instruct the database to create an auxiliary table with the purpose of storing large binary content.
Do not use LOMBOK for multipurpose database, its not helping you.
Put annotations on methods forces JPA to access properties via methods. It makes sense when internal state of your object differs from the database schema:
private String text;
#Lob
#Basic(fetch = FetchType.LAZY)
#Column(name = "text")
public String getText(text ) {
return text;
}
public void setText(String text) {
text = text;
}
read this : source
I use this method for local Postgres development and Oracle Server both.

DynamicUpdate added extra untouched column in update statement

I am facing a weird behavior using #DynamicUpdate with one of my entity. So my entity is defined like this
#Entity
#DynamicUpdate
#Table(name = "courts")
#Getter
#Setter
#Builder
#AllArgsConstructor // require for #Builder to work correctly
#NoArgsConstructor // required for hibernate mapping
public class CourtDO {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
// name is SQL keyword
#Column(name = "court_name")
private String name;
#Column
private String addressStreet;
#Column
private String addressWard;
#Column
private String addressDistrict;
#Column
private String addressCity;
#Column
private String addressCountry;
#Column
private String phoneNumber;
#Column(nullable = false)
#Convert(converter = DOTimestampConverter.class)
#CreationTimestamp
private ZonedDateTime createdAt;
#Column(nullable = false)
#Convert(converter = DOTimestampConverter.class)
#UpdateTimestamp
private ZonedDateTime updatedAt;
}
DOTimestampConverter is just a simple AttributeConverter to convert ZonedDateTime to ms for me to store in DB as number.
As you can see I marked the entity with #DynamicUpdate.
I have a small jersey REST-API which allow me to update all of the normal field beside the id and the generated date. I always check if the input is null before actually touching the setter for the field.
I see a very weird behavior with the column address_city, it would be included in every update like this even if I only touch other fields, in this case, just the name which translate to court_name because name is a reserved keyword in SQL. UPDATE Actually the problem is not with just the address_city column. Even if I ignore that column, other column would get included in the update statement.
Hibernate:
update
courts
set
address_city=?,
court_name=?,
updated_at=?
where
id=?
Where as if I only edit the address_city only then I would see it correctly
Hibernate:
update
courts
set
address_city=?,
updated_at=?
where
id=?
Did anyone face this problem before?
The stack I am using are
spring-boot 2.1.3.RELEASE
spring-boot-starter-data-jpa
postgresql 11.4
And the data schema
CREATE TABLE COURTS (
id SERIAL PRIMARY KEY,
court_name VARCHAR(20),
address_street VARCHAR(128),
address_ward VARCHAR(20),
address_district VARCHAR(20),
address_city VARCHAR(20),
address_country VARCHAR(10),
phone_number VARCHAR(20),
created_at BIGINT NOT NULL,
updated_at BIGINT NOT NULL
);
This is how update through REST-API
#Override
public Court editCourt(String courtId, CreateCourtRequest createCourtRequest) {
Optional<CourtDO> court = courtRepository.findById(NumberUtils.toLong(courtId));
return court
.map(courtDO -> editCourtInfo(courtDO, createCourtRequest))
.map(courtRepository::save)
.map(courtDOToResponseConverter::convert)
.orElse(null);
}
private CourtDO editCourtInfo(CourtDO courtDO, CreateCourtRequest createCourtRequest) {
if (StringUtils.isNotBlank(createCourtRequest.getName())) {
courtDO.setName(createCourtRequest.getName());
}
if (StringUtils.isNotBlank(createCourtRequest.getAddressStreet())) {
courtDO.setAddressStreet(createCourtRequest.getAddressStreet());
}
if (StringUtils.isNotBlank(createCourtRequest.getAddressWard())) {
courtDO.setAddressWard(createCourtRequest.getAddressWard());
}
if (StringUtils.isNotBlank(createCourtRequest.getAddressDistrict())) {
courtDO.setAddressDistrict(createCourtRequest.getAddressDistrict());
}
if (StringUtils.isNotBlank(createCourtRequest.getAddressCity())) {
courtDO.setAddressCity(createCourtRequest.getAddressCity());
}
if (StringUtils.isNotBlank(createCourtRequest.getPhoneNumber())) {
courtDO.setPhoneNumber(createCourtRequest.getPhoneNumber());
}
return courtDO;
}

Update record based on primary key composite with Timestamp type

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

Mapping Another key as foreign key instead of primary key in sql server

I am using Spring mvc with hibernate and i have a lookup table in my SQL server database. this table has 4 columns
#Entity
#Table(name = "VLE_LOOKUP_DATA")
public class Lookup_Data{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long ID;
#Column(name = "ENTITYNAME")
private String ENTITYNAME;
#Column(name = "CODE")
private String CODE;
#Column(name = "DESCRIPTION")
private String DESCRIPTION;
}
This table has one to one relationship with multiple tables for example with student table.
#Entity
#Table(name = "STUDENT")
public class student{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long STUDENT_ID;
#OneToOne
#JoinColumn(name = "DEPARTMENT_ID")
private Lookup_Data DEPARTMENT_ID;
#Column(name = "Name")
private String Name;
#OneToOne
#JoinColumn(name = "GENDER_ID")
private Lookup_Data GENDER_ID;
}
Here the foreign key came from Lookup Table is not the Primary key(ID) of Lookup data.its column name CODE which is referenced as foreign key in other tables.That is why when i run this example the OneToOne relation gives error as
java.sql.SQLException: Conversion failed when converting the varchar to data type int.
because code is string value.
Is there any way to implement this scenario in Spring Mvc boot application?
Note: In sql server this query can do desired work.
SELECT * FROM STUDENT as e left join VLE_LOOKUP_DATA as di on e.DEPARTMENT_ID=di.CODE
JPA is designed to support wide range of DBMS ( database server ) so it just support SQL standard specification.
In your current case, compare equals between 2 fields is not supported by SQL spec ( may be just supported by sql server only ) so JPA raise this error. So I think instead of using relational mapping, to get the Lookup_data you can do normal JPQL or native query.

Categories

Resources