Cannot persist hashmap with JPA - java

I am struggling trying to persist a map to SQLserver with the code below and keep getting the following error:
* Caused by: com.microsoft.sqlserver.jdbc.SQLServerException: Invalid object name 'OnlineReport_availabilities'. *
I use Play framework JPA for this have have managed to persist Maps before with similar code. I tried manually creating the table in the db to see if there was any issue with the name but all seems ok.
I am doing something non conventional or incorrect here?
#Entity
public class OnlineReport extends Model {
#Temporal(TemporalType.DATE)
public Date date;
#ElementCollection
public Map<Date, Double> availabilities;
public OnlineReport(){
this.date = new Date();
this.availabilities = new HashMap<Date, Double>();
}
public void addAvailability(double availability){
TreeSet<Date> set = new TreeSet(availabilities.entrySet());
Date lastEntry = null;
if(!set.isEmpty())
lastEntry = set.last();
Date now = new Date();
if(lastEntry != null){
//Add availibility every 10mn
if(DateUtil.getMinutesBetween(lastEntry, now) >= 10){
availabilities.put(now, availability);
save();
}
} else {
availabilities.put(now, availability);
save();
}
}
}
Update.
Running JPA in debugSQL I have noticed the following errors:
ERROR ~ Unsuccessful: create table OnlineReport_availabilities (OnlineReport_id numeric(19,0) not null, availabilities double precision null, availabilities_KEY datetime null, primary key (OnlineReport_id, availabilities_KEY))
ERROR ~ Cannot define PRIMARY KEY constraint on nullable column in table 'OnlineReport_availabilities'.
I am under the impression I am missing some annotation definition of 'availabilities'.

Likely you do not have such a table or it is not accessible via connection in use. Are object (tables, fields etc) names case sensitive in db?
So, after your update we know why you do not have table. Looks like it fails to create not null -constraint to the column that is part of the key. Dialect fails for SQL Server, because your mapping is minimal, but correct. You can try to force it by adding following annotation:
#MapKeyColumn(columnDefinition = "datetime not null")
Of course having datetime as part of the key will produce problems if it is possible that there is more than one entry per millisecond.

Related

Generate Hibernate Id from database function, for table partitioning

I want to embed date information in the primary key, for a table that will be partitioned (monthly) in a PostgreSQL database. This should in theory speed up the process on finding out in which partition to look for the data. I followed this article to embed the date in a date into the serial.
Now, I am however facing the problem that I can't get the Id been used by Hibernate.
c.f. the sql that should give an idea of the attempted approach.
CREATE SEQUENCE test_serial START 1;
CREATE OR REPLACE FUNCTION gen_test_key() RETURNS BIGINT AS $$
DECLARE
new_id bigint;
BEGIN
new_id = (nextval('public.test_serial'::regclass)::bigint % 10000000000000::bigint
+ ( (EXTRACT(year from now())-2000)::bigint * 10000::bigint
+ EXTRACT(month from now())::bigint * 100::bigint
+ EXTRACT(day from now())::bigint
)::bigint * 10000000000000::bigint
)::bigint;
RETURN new_id;
END;
$$ LANGUAGE plpgsql;
CREATE TABLE test
( id bigint primary key default gen_test_key(),
something text,
tstamp timestamp default now()
) PARTITION BY RANGE (id);
CREATE TABLE test_2022_10 PARTITION OF test
FOR VALUES FROM (2210100000000000000::bigint ) TO (2211010000000000000::bigint);
I came across a similar question, where it was suggested to use a stored procedure. Unfortunately only functions are allowed as default in the table definition and therefore stored procedures, seam not to work for me.
I think what you need here is a subtype of SequenceStyleGenerator that overrides determineBulkInsertionIdentifierGenerationSelectFragment to run the code of this function. You should be able to configure this generator on your entity with #GenericGenerator. I understand the desire to use this concept when you don't want to change your existing queries, but are you sure that partitioning will help you in your use case?
Also, be careful and do not rely on the date information in the primary key, because with pooled optimizers, it might happen that a value is generated way before it actually is used as primary key for a row.
So this is a solution that worked out in the end as suggested #ChristianBeikov here the entity with the annotations pointing to the CustomIdGenerator.
public class Test {
#Id
#GenericGenerator(name = "CustomIdGenerator", strategy = "nl.test.components.CustomIdGenerator")
#GeneratedValue(generator = "CustomIdGenerator")
private Long id;
private String something;
private OffsetDateTime tstamp;
}
As explained by #Mr_Thorynque it is similarly possible to call a stored function as a procedure. Just replace "CALL gen_test_key()" with "SELECT gen_test_key()" and don't pass it to the wrong method for stored procedures connection.prepareCall(CALL_STORE_PROC);, but instead connection.prepareStatement(STORED_FUNCTION); So, this is the CustomIdGenerator.
public class CustomIdGenerator implements IdentifierGenerator {
private static final String STORED_FUNCTION = "select gen_test_key()";
#Override
public Serializable generate(SharedSessionContractImplementor session, Object object) throws HibernateException {
Long result = null;
try {
Connection connection = session.connection();
PreparedStatement pstmt = connection.prepareStatement(STORED_FUNCTION);
ResultSet resultSet = pstmt.executeQuery();
if (resultSet.next()) {
result = resultSet.getLong(1);
System.out.println("Generated Id: " + result);
}
} catch (SQLException sqlException) {
throw new HibernateException(sqlException);
}
return result;
}
}

Spring JPA automatically fetch system time stamp from database

I'd like to fetch the current system timestamp: SELECT CURRENT_TIMESTAMP; automatically every time I run a select operation.
Sample code below:
SomeEntity.java
#Entity
#Table(name = "some_table")
public class SomeEntity {
#Column(name = "name", length = 500)
private String name;
#Column(name = "current_timestamp")
private LocalDate currentTimestamp;
}
The currentTimestamp column is not physically present in the table but I wish to get the CURRENT_TIMESTAMP value from the database to be populated in this field every time I perform a SELECT operation as below:
List<SomeEntity> records = someCrudRepository.findByName("someName")
OR List<SomeEntity> records = someCrudRepository.findAll()
In short, I'd like to run this query via Spring JPA: SELECT name, current_timestamp FROM some_table;
Is there any way to achieve the same except using native or jpql queries?
I don't want to use Java time library for getting the timestamp as that is not the overall purpose of getting the current timestamp.
Hibernate's #Formula is what you are looking for:
import org.hibernate.annotations.Formula;
public class SomeEntity {
...
#Formula("current_timestamp")
private LocalDate currentTimestamp;
}

Groovy Sql Query Returning Date Column as String

I have a groovy class that is responsible for running a query against a sql database table and extracting some data from the results to build a DTO. The database column I am interested in is called 'release_date' is of type (date,null). I was expecting the results to return a java.util.Date object but instead I am getting a java.lang.String. The strange thing is, if I change the type of the column to a datetime it seems to work correctly.
Is this expected behaviour from groovy? Is there a way to get it to return a Date object without having to change the column of the database?
My Groovy Code:
Sql opticsSql=myDb;
def run(Map<Long, OriginalDateDTO> dtoMap) {
def query = """select p.id,p.release_date as planned from releases p; """;
opticsSql.eachRow(query.toString()) { val->
Long id = val.id;
Date planned = val.planned;
Map localMap = dtoMap;
Boolean hasKey = localMap.containsKey(id);
if(hasKey) {
dtoMap.get(id).originalPlannedDate = planned;
}
}
}

Persistence entity with two Date fields, and both are changed on update, how?

I'm having problems where two Date fields are updated to the exact same date when only one should be. I'm trying to figure out why this is happening and how I can update only the one date field I want updated, and leave the other at its original value.
I'm using Hibernate with JPA on a MySQL database, in case that is part of the reason.
I have a persistence entity that looks something like this:
#NamedQueries({
#NamedQuery(name="MyObject.updateItem", query="UPDATE MyObject m SET m.item = :item, m.lastUpdate = :updated WHERE m.id = :id")
})
#Entity
#Table(name="entries")
public class MyObject implements Serializable
{
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
private Long id;
private String item;
#Column(columnDefinition = "TIMESTAMP", nullable = false)
private Date dateCreated = new Date();
#Column(columnDefinition = "TIMESTAMP", nullable = false)
private Date lastUpdate = new Date();
// after here standard constructors, getters, setters, etc.
}
When from my DAO I call the NamedQuery and provide the correct paramters, I find that both lastUpdate and dateCreated are changed. Is there any reason for this and how can I prevent this from happening? Is this caused because I initialize the to date fields in the entity class?
I'm using the TIMESTAMP column definition because I want to be able to perform queries with < or >.
lastUpdate and dataCreated, aftare update have the same value?
I don't know if this will be a solution for you but this is what I commonly do for all of the entities I regularly implement. Add a PrePersist and PreUpdate function to your entity in order to set the created and last modified times. Also try adding #Temporal(TemporalType.TIMESTAMP) to each of your date fields.
#PrePersist
public void prePersist() {
this.dateCreated = new Date();
this.lastUpdated = this.dateCreated;
}
#PreUpdate
public void preUpdate() {
this.lastUpdated = new Date();
}
Beyond that, I'm a little stumped...
So I figured out the problem wasn't to do with my query or how I used persistence but how I built the database itself.
When I created the table to contain the data for the object I didn't specify a specific default for a NOT NULL field.
My original SQL CREATE statement looked something like this.
CREATE TABLE IF NOT EXISTS `entries` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`item` VARCHAR(255) NOT NULL,
`dateCreated` TIMESTAMP NOT NULL,
`lastUpdate` TIMESTAMP NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
When the MySQL Server executed this statement it deferred the first TIMESTAMP field (in this case dateCreated) with the default to fill it with the CURRENT_TIMESTAMP and the attribute on update CURRENT_TIMESTAMP which was unexpected by me.
I corrected this problem by changing default for the field to DEFAULT '0000-00-00 00:00:00' and by changing my CREATE TABLE statement to force this default so my new statement looks like
CREATE TABLE IF NOT EXISTS `entries` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`item` VARCHAR(255) NOT NULL,
`dateCreated` TIMESTAMP NOT NULL DEFAULT '0000-00-00 00:00:00',
`lastUpdate` TIMESTAMP NOT NULL DEFAULT '0000-00-00 00:00:00',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
This apparently allows me to update the fields that I want without causing the other to update automatically.
I'm still not sure why MySQL assumed the defaults that it did. I guess it's probably somewhere in the documentation.

Update record on unique constraint on batch store jooq

I am trying to insert the records using batchStore using jooq. I need to know how we can update the record on unique constraint, currently it is throwing
an exception that the record already exists
SQL Error [23505]: ERROR: duplicate key value violates unique constraint
Below is the code
DSLContext create = getDSLContext();
List<UserRecord> userRecordList = new ArrayList<>();
for (Users user : model.getUsers()) {
User record = create.newRecord(user);
userRecordList.add(record);
}
create.batchStore(userRecordList).execute();
Currently it is inserting the records fine, but when duplicate record found on the basis of unique constraint it should update the record
I have resolved this issue by using a common interface UpdatableRecord, first I have used a fetchOne() query to get the record on the basis of the unique constraint which will give the UpdatableRecord and then set the values with the updated ones which are then added to userRecordlist which will be further passed into batchStore
I had to do the same as JN_newbie. For completeness here's what my solution looked like.
// OpeningtimeRecord is the JOOG generated record for the Openingtime table.
List<OpeningtimeRecord> openingRecords = new ArrayList<>();
// Openingtime is the POJO generated from the database schema for the table.
for (Openingtime opening : openings)
{
// Check to see if this record already exists in the database
OpeningtimeRecord record = dbContext.fetchOne(*TABLE*, *TABLE.ID*.eq(opening.getId()));
// If it doesn't exist then we need to create a new record
if (null == record)
{
record = dbContext.newRecord(*TABLE*, opening);
}
else
{
// Update the record with any new data from the POJO.
record.from(opening);
}
openingRecords.add(record);
}
int[] results = this.dbContext.batchStore(openingRecords).execute();

Categories

Resources