Spring JPA entity mapping LocalDate and LocalDateTime field to Sql Server - java

I am using Spring JPA and have mapped the following entity to H2 Database fields:
#Entity
#Table(name = "REQUEST")
public class Request implements Serializable {
#Id
#Column(name = "ID")
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
#Column(name = "CORR_ID")
private String corrId;
#Column(name = "CB_DATE")
private LocalDate cbDate;
#Column(name = "ACTIVE")
private boolean active;
#Column(name = "CREATED_DATE")
private LocalDate createdDate;
#Column(name = "LAST_UPDATED")
private LocalDateTime lastUpdated;
}
-- H2 DB Script
CREATE TABLE REQUEST
(
ID BIGINT AUTO_INCREMENT PRIMARY KEY,
CORR_ID CHAR(36) NOT NULL,
CB_DATE DATE NOT NULL,
ACTIVE BOOLEAN DEFAULT FALSE NOT NULL,
CREATED_DATE DATE NOT NULL,
LAST_UPDATED DATETIME NOT NULL
);
I am unsure if the following SQL Server column definitions will map correctly to Java. I have seen that BIT maps to boolean java type, However, I am unsure about Date & DateTime will be mapped correctly to LocalDate & LocalDateTime respectively.
--MS SQL SEVER SCRIPT
CREATE TABLE REQUEST
(
ID BIGINT AUTO_INCREMENT PRIMARY KEY,
CORR_ID CHAR(36) NOT NULL,
CB_DATE DATE NOT NULL,
ACTIVE BIT DEFAULT FALSE NOT NULL,
CREATED_DATE DATE NOT NULL,
LAST_UPDATED DATETIME NOT NULL
);
Some clarity around this will be appreciated. Do I need to make any adjust to my Java code? Are my SQL server definitions ok?
I am doing conversion before saving with JPA:
re.setIsActive(tt.isActive());
re.setCbDate(LocalDate.parse(tt.getCobDate()));
re.setCreatedDate(LocalDate.now());

I guess the version of the SQL Server itself, the SQL Server JDBC Driver and the used Hibernate Dialect play here a role to determine what types are correct or acceptable on the SQL Server side.
That's too many variables to say something. I would let Hibernate create the table and see what it generates. You need to determine which SQL Server version(s) you have to support. Based on that i would choose the best dialect and JDBC Driver and then again see what Hibernate will generate with this setup and take this as the correct types.

Related

findByDate in spring boot jpa gives empty list

I'm using spring boot JPA for CRUD operations. I'm querying the database table with the field name created_at which is of type date.There are some rows in the table with the given date but JPA is giving zero result set. I'm using Oracle 11g
Here is my entity
import java.sql.Date;
#Entity
#Table(name="veev_json")
public class VeevJson {
#Id
#Column(name="ID")
private int id;
#Column(name="CREATED_AT")
private Date createdDate;
}
My JPA Repository
import java.util.Date;
#Repository
public interface VeevJsonRepository extends JpaRepository<VeevJson, Integer> {
public List<VeevJson> findByCreatedDate(Date date);
}
Calling the function in the service layer
Date date = new Date(); //taking current date of type java.util.Date
List<VeevJson> documents = veevJsonRepository.findByCreatedDate(date);
My DB table structure
ID NUMBER(10,0)
CREATED_AT DATE
SQL query generated by the hibernate:
select veevjson0_.ID as ID1_1_, veevjson0_.CREATED_AT as CREATED_AT2_1_, veevjson0_.JSON as JSON3_1_, veevjson0_.STATUS as STATUS4_1_ from veev_json veevjson0_ where veevjson0_.CREATED_AT=?
When using a field with type Date, you should also use the #Temporal annotation. The default value of #Temporal is TemporalType.TIMESTAMP, and your JPA implementation may get confused about dealing with a field of type java.util.Date, passing as argument of query the timestamp instead of date.
Please annotate your field as
import java.util.Date;
#Entity
#Table(name = "veev_json")
public class VeevJson {
#Id
#Column(name = "ID")
private int id;
#Temporal(TemporalType.DATE)
#Column(name = "CREATED_AT")
public Date createdDate;
...
}
Doing so will allow JPA implementation to send as queried value only the date (probably in 'YYYY-MM-dd' format) instead of timestamp or any other value.
If you prefer and your JDBC supports 4.1 version, you may exchange the java.util.Date for java8 time API types, but I guess this is out of scope here.

Implementing complex MAX function in Spring Boot/JPA query language

Spring Boot here using JPA/Hibernate and CrudRepository impls for managing persistence to my DB tables.
I have the following MySQL table:
CREATE TABLE IF NOT EXISTS price_scarcity_configs (
price_scarcity_config_id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
price_scarcity_config_ref_id VARCHAR(36) NOT NULL,
price_scarcity_config_version BIGINT NOT NULL,
price_scarcity_config_updated_on DATETIME NOT NULL,
price_scarcity_config_fizz INTEGER NOT NULL,
CONSTRAINT pk_price_scarcity_configs PRIMARY KEY (price_scarcity_config_id),
CONSTRAINT uc_price_scarcity_configs_ref_id_and_version UNIQUE (price_scarcity_config_ref_id, price_scarcity_config_version)
);
These records will be versioned and different versions of the "same" record will all share the same price_scarcity_config_ref_id. Hence 2+ records can have the same price_scarcity_config_ref_id but will have two distinct different versions.
I'm also using the following JPA/Hibernate entity to model it:
// Uses Lombok annotations to generate getters/setters, etc.
#MappedSuperclass
#Data
#EqualsAndHashCode(callSuper=false)
public abstract class BaseEntity {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String refId;
}
#Entity
#Table(name = "price_scarcity_configs")
#AttributeOverrides({
#AttributeOverride(name = "id", column = #Column(name = "price_scarcity_config_id")),
#AttributeOverride(name = "refId", column = #Column(name = "price_scarcity_config_ref_id"))
})
#Data
#EqualsAndHashCode(callSuper=false)
public class PriceScarcityConfiguration extends BaseEntity {
#Column(name = "price_scarcity_config_version")
private Long version;
#Column(name = "price_scarcity_config_updated_on")
private Date updatedOn;
#Column(name = "price_scarcity_config_fizz")
private Integer fizz;
}
I am now trying to write the PriceScarcityConfigurationRepository and need a fairly sophisticated query. Given a refId, I need to find the record who matches that ref id and has the highest/max version number. The raw SQL query to perform this is:
select
*
from
price_scarcity_configs pcs
inner join
(
SELECT
price_scarcity_config_ref_id,
MAX(price_scarcity_config_version) as max_ver
FROM
price_scarcity_configs
group by
price_scarcity_config_ref_id
) t
on
t.price_scarcity_config_ref_id = pcs.price_scarcity_config_ref_id
and
t.max_ver = pcs.price_scarcity_config_version;
Given my repository and using JPA/Hibernate's built-in query language/annos, how do I implement this query?
public interface PriceScarcityConfigurationRepository extends CrudRepository<PriceScarcityConfiguration,Long> {
#Query("FROM PriceScarcityConfiguration WHERE ??? HOW TO IMPLEMENT THE ABOVE QUERY HERE ???")
PriceSheetConfiguration fetchLatestVersionByRefId(#Param("refId") String refId);
}
You could use the following query instead and use setMaxResults(1)
FROM PriceScarcityConfiguration p WHERE p.refId = :refId ORDER BY p.version DESC
Or simply use the Spring Data notation
List<PriceSheetConfiguration> findFirstByRefIdOrderByVersionDesc(String refId);

Hibernate is getting wrong ID values when using UUID as primary key

My entity:
#Entity
#Table(name = "eh_portal")
public class PortalEntity {
#Id
#Column(name = "id", columnDefinition = "CHAR(36)")
private UUID id; //java.util.UUID;
#Column(name = "name")
private String name;
#Column(name = "url")
private String url;
// -- Constructor for Hibernate --
protected PortalEntity() {
}
// -- Constructor for new entity in service code --
public PortalEntity(final UUID id) {
this.id = id;
}
.... getters and setters ommited
}
Respository is Spring DATA JPA:
public interface PortalRepository extends CrudRepository<PortalEntity, UUID> {
}
MYSQL 5 Database table definition:
CREATE TABLE `eh_portal` (
`id` char(36) NOT NULL COMMENT 'UUID',
`name` varchar(255) NOT NULL,
`url` varchar(255) NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `url_UNIQUE` (`url`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
The problem is, Hibernate is returning obviously wrong data - see screenshots below
Mysql workbench:
Actual web page where I get entities thru Spring Data JPA:
You can see that UUIDs are obviously differrent, while other columns are correct.
What is wrong here? (Spring 4, Hibernate 4, Spring DATA JPA, Mysql 5)
Try using #Type(type="uuid-char").

Postgres array in Hibernate entity

I am using postgres database and spring with hibernate in my project.
I just want to get some datas from DB, where the table having array datatype column in itself.
While I am getting from that table I am getting the following error.
ERROR org.hibernate.util.JDBCExceptionReporter - ERROR: relation "reconcileprocess_bankstmtid" does not exist
Table structure as follows
CREATE TABLE reconcile_process
(
id bigserial NOT NULL,
comments character varying,
fk_last_modified_by bigint NOT NULL,
last_modified_date timestamp with time zone NOT NULL,
fk_remittance_transaction_fkey character varying,
fk_transaction_ref character varying,
process_type character varying,
reconcilled_date date,
fk_bank_stmt_id bigint[]
)
Entity class for that table
#Entity
#Table(name = "reconcile_process")
public class ReconcileProcess implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#Column(name = "id")
Long id;
#Column(name = "comments")
String comments;
#Column(name = "fk_last_modified_by")
Long lastModifiedBy;
#Column(name = "last_modified_date")
Date lastModifiedDate;
#Column(name = "fk_transaction_ref")
String transactionRef;
#Column(name = "fk_remittance_transaction_fkey")
String remitTransactionRef;
#Column(name = "process_type")
String processType;
#Column(name = "reconcilled_date")
Date reconcilledDate;
#ElementCollection
#Column(name = "fk_bank_stmt_id")
List<Long> bankStmtId;
Hibernate does not support database arrays (java.sql.Array).
You get the error because Hibernate expects a separate table for List<Long> bankStmtId (because you didn't explicitly specify the table name, Hibernate assumes the default of <entity name>_<property name>, thus reconcileprocess_bankstmtid).
You can either switch to the supported approach with a separate table, or as explained here, you can try to write custom user type for database arrays.

Setting a JPA timestamp column to be generated by the database?

In my SQL Server 2000 database, I have a timestamp (in function not in data type) column of type DATETIME named lastTouched set to getdate() as its default value/binding.
I am using the Netbeans 6.5 generated JPA entity classes, and have this in my code
#Basic(optional = false)
#Column(name = "LastTouched")
#Temporal(TemporalType.TIMESTAMP)
private Date lastTouched;
However when I try to put the object into the database I get,
javax.persistence.PersistenceException: org.hibernate.PropertyValueException: not-null property references a null or transient value: com.generic.Stuff.lastTouched
I've tried setting the #Basic to (optional = true), but that throws an exception saying the database doesn't allow null values for the TIMESTAMP column, which it doesn't by design.
ERROR JDBCExceptionReporter - Cannot insert the value NULL into column 'LastTouched', table 'DatabaseName.dbo.Stuff'; column does not allow nulls. INSERT fails.
I previously got this to work in pure Hibernate, but I have since switched over to JPA and have no idea how to tell it that this column is supposed to be generated on the database side. Note that I am still using Hibernate as my JPA persistence layer.
I fixed the issue by changing the code to
#Basic(optional = false)
#Column(name = "LastTouched", insertable = false, updatable = false)
#Temporal(TemporalType.TIMESTAMP)
private Date lastTouched;
So the timestamp column is ignored when generating SQL inserts. Not sure if this is the best way to go about this. Feedback is welcome.
I realize this is a bit late, but I've had success with annotating a timestamp column with
#Column(name="timestamp", columnDefinition="TIMESTAMP DEFAULT CURRENT_TIMESTAMP")
This should also work with CURRENT_DATE and CURRENT_TIME. I'm using JPA/Hibernate with Oracle, so YMMV.
#Column(nullable = false, updatable = false)
#CreationTimestamp
private Date created_at;
this worked for me.
more info
Add the #CreationTimestamp annotation:
#CreationTimestamp
#Column(name="timestamp", nullable = false, updatable = false, insertable = false)
private Timestamp timestamp;
If you are doing development in Java 8 and Hibernate 5 Or Spring Boot JPA then use following annotation directly
in your Entity class. Hibernate gets the current timestamp from the VM and will insert date and time in database.
public class YourEntity {
#Id
#GeneratedValue
private Long id;
private String name;
#CreationTimestamp
private LocalDateTime createdDateTime;
#UpdateTimestamp
private LocalDateTime updatedDateTime;
…
}
I do not think that every database has auto-update timestamps (e.g. Postgres). So I've decided to update this field manually everywhere in my code. This will work with every database:
thingy.setLastTouched(new Date());
HibernateUtil.save(thingy);
There are reasons to use triggers, but for most projects, this is not one of them. Triggers dig you even deeper into a specific database implementation.
MySQL 5.6.28 (Ubuntu 15.10, OpenJDK 64-Bit 1.8.0_66) seems to be very forgiving, not requiring anything beyond
#Column(name="LastTouched")
MySQL 5.7.9 (CentOS 6, OpenJDK 64-Bit 1.8.0_72) only works with
#Column(name="LastTouched", insertable=false, updatable=false)
not:
FAILED: removing #Temporal
FAILED: #Column(name="LastTouched", nullable=true)
FAILED: #Column(name="LastTouched", columnDefinition="TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP")
My other system info (identical in both environments)
hibernate-entitymanager 5.0.2
hibernate-validator 5.2.2
mysql-connector-java 5.1.38
I have this working well using JPA2.0 and MySQL 5.5.10, for cases where I only care about the last time the row was modified. MySQL will create a timestamp on first insertion, and every time UPDATE is called on the row. (NOTE: this will be problematic if I cared whether or not the UPDATE actually made a change).
The "timestamp" column in this example is like a "last-touched" column.x`
The code below uses a separate column "version" for optimistic locking.
private long version;
private Date timeStamp
#Version
public long getVersion() {
return version;
}
public void setVersion(long version) {
this.version = version;
}
// columnDefinition could simply be = "TIMESTAMP", as the other settings are the MySQL default
#Column(name="timeStamp", columnDefinition="TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP")
#Temporal(TemporalType.TIMESTAMP)
public Date getTimeStamp() {
return timeStamp;
}
public void setTimeStamp(Date timeStamp) {
this.timeStamp = timeStamp;
}
(NOTE: #Version doesn't work on a MySQL "DATETIME" column, where the attribute type is "Date" in the Entity class. This was because Date was generating a value down to the millisecond, however MySQL was not storing the millisecond, so when it did a comparison between what was in the database, and the "attached" entity, it thought they had different version numbers)
From the MySQL manual regarding TIMESTAMP :
With neither DEFAULT nor ON UPDATE clauses, it is the same as DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP.
If you mark your entity with #DynamicInsert e.g.
#Entity
#DynamicInsert
#Table(name = "TABLE_NAME")
public class ClassName implements Serializable {
Hibernate will generate SQL without null values. Then the database will insert its own default value. This does have performance implications See [Dynamic Insert][1].
This also works for me:-
#Temporal(TemporalType.TIMESTAMP)
#Column(name = "CREATE_DATE_TIME", nullable = false, updatable = false, insertable = false, columnDefinition = "TIMESTAMP DEFAULT CURRENT_TIMESTAMP")
public Date getCreateDateTime() {
return createDateTime;
}
public void setCreateDateTime(Date createDateTime) {
this.createDateTime = createDateTime;
}
I'm posting this for people searching for an answer when using MySQL and Java Spring Boot JPA, like #immanuelRocha says, only have too #CreationTimeStamp to the #Column in Spring, and in MySQL set the default value to "CURRENT_TIMESTAMP".
In Spring add just the line :
#Column(name = "insert_date")
#CreationTimestamp
private Timestamp insert_date;
#Column(name = "LastTouched", insertable = false, updatable = false, columnDefinition = "TIMESTAMP default getdate()")
#Temporal(TemporalType.TIMESTAMP)
private Date LastTouched;`enter code here`
This worked for me:
#Column(name = "transactionCreatedDate", nullable = false, updatable = false, insertable = false, columnDefinition = "TIMESTAMP DEFAULT CURRENT_TIMESTAMP")

Categories