I would like to get millisecond precision in my MariaDB. After some research, I found that I needed to change the columnDefinition - so I did this in my entity:
#NotNull
#Column(name = "createdDate", columnDefinition = "DATETIME(3) NOT NULL")
#Temporal(TemporalType.TIMESTAMP)
private TimeStamp createdDate;
#PrePersist
void onPersist() {
createdDate = new Timestamp(new Date().getTime());
}
The resulting SQL to create the column is:
`createdDate` DATETIME(3) NOT NULL
Now, in the DB the value has indeed 3 decimals:
2016-09-12 16:57:44.000
... but they are always 000
What did I do wrong, or what did I forget ?
Edit: I tried without JAVA:
CREATE TABLE `test` (
`id` BIGINT(20) NOT NULL AUTO_INCREMENT,
`createdDate` DATETIME(3) NOT NULL,
PRIMARY KEY (`id`)
)
COLLATE='latin1_swedish_ci'
ENGINE=InnoDB
;
And then:
INSERT INTO test (createdDate)
VALUES(current_timestamp())
Result:
2016-09-13 13:57:44.000
I had the same problem with MariaDB and date types. I've tried org.joda.DateTime and java.util.time types. Both, the server and the client code supported milliseconds correctly.
The problem was that I was using MySQL Connector instead of MariaDB Connector/J JDBC driver.
Background
In most situations using MariaDB with MySQL Connector works out well, but I would never ever recommend this. When I was searching for the issue I was debugging through Hibernate and Connector code and saw many feature detections that were based on the server version number instead of a real feature detection. The version numbering of course differs between MySQL and MariaDB. So there's a big probability that there are far more compatibility issues that are quietly ignored.
Your problem most probably comes from the fact that you mix Dates and Timestamps. Changing the createdDate type to java.sql.Timestamp should solve your issue.
Also, if your version of MySQL is prior to 5.6.4, DateTime won't let you save time fractions.
EDIT after OP's edit :
you are still mixing the Date Java type with Timestamp when you do this :
createdDate = new Timestamp(new Date().getTime());
Can you try createdDate = new Timestamp(System.currentTimeInMilliseconds()); instead ?
Ideally you should use objects from a library like JodaTime to avoid such issues, but that's beyond the point of your question, just a tip :)
Ultimately, if this way of creating your Timestamp does not work, I would use the Timestamp type in DB instead of Datetime, but that's just trial and error as Datetime should work as well in your example.
Edit :
excerpt from Oracle's Date API :
Date()
Allocates a Date object and initializes it so that it represents the time at which it was allocated, measured to the nearest millisecond.
In which case using System.currentTimeInMilliseconds() shouldn't change the outcome - my bad.
To troubleshoot the problem, I'd start to create a date via SQL (without passing via Java objects) with CURRENT_TIMESTAMP to make sure that the field can indeed contain decimal time precision.. If it is OK, verify the value in the Java object with a debugger.. Might give you a lead. If both contain milliseconds, I'd look at the usage of the annotations or start from a working sample.
To do this using pure JPA :
#Column(name="STMP", columnDefinition = "TIMESTAMP (6)")
private Timestamp timestamp = Timestamp.from(Instant.now());
Related
Hi I would like to generate a Predicate for spring data QueryDSLRepository that would generate following query (or equivalent):
SELECT * FROM USER user JOIN PASSWORD_POLICY policy
ON
user.password_policy_oid = policy.password_policy_oid
WHERE
user.password_expiration_date BETWEEN CURRENT TIMESTAMP - (SELECT EXPIRATION_WARN_PERIOD_IN_DAYS FROM PASSWORD_POLICY subQueryPolicy WHERE subQueryPolicy.password_policy_oid = policy.password_policy_oid) DAYS AND CURRENT TIMESTAMP
The meaning of this query would be:
Get me all users that password is about to expire
and by about to expire I mean - password expiration date is between now and (now - EXPIRATION_WARN_PERIOD_IN_DAYS from PASSWORD_POLICY table)
Is it possible?
This probably it can't be done by using only Spring Data predicate runner. AFAIK jpql does not support such datetime manipulation (add days etc). So what you can do if you still want use Querydsl is to use native JPASQLQuery. Unfortunately joining mapped entities is not easy, another drawback is that datetime manipulation capabilities are also not so nice in Querydsl. But I was able to manage your problem.
Assumptions:
User contains #ManyToOne PassworPolicy field, which is mapped by PASSWORD_POLICY_OID column.
DB2 datatabase
import static model.QPasswordPolicy.passwordPolicy;
import static model.QUser.user;
import static com.mysema.query.sql.SQLExpressions.datediff;
import static com.mysema.query.types.expr.DateTimeExpression.currentTimestamp;
...
NumberPath<Integer> userPasswordPolicyOidPath = new NumberPath<>(Integer.class, QUser.user, "PASSWORD_POLICY_OID");
QPasswordPolicy subQueryPolicy = new QPasswordPolicy("subQueryPolicy");
List<Integer> userIds =
new JPASQLQuery(entityManager, new DB2Templates())
.from(user)
.join(passwordPolicy).on(passwordPolicy.passwordPolicyOid.eq(userPasswordPolicyOidPath))
.where(datediff(DatePart.day, currentTimestamp(DateTime.class), user.passwordExpirationDate)
.lt(new JPASubQuery().from(subQueryPolicy)
.where(passwordPolicy.passwordPolicyOid.eq(subQueryPolicy.passwordPolicyOid))
.unique(passwordPolicy.expirationPeriodInDays)))
.list(user.userOid);
probably some one more condition expirationDate < currentTimeStamp is needed to satisfy the logic, but I will leave it to you :)
PS
userPasswordPolicyOidPath is ugly but I don't have idea how to get rid of this :(
I get some strange behavior, when using WCF Data Services 5.6.
In my case, I have table, with 1 column set with Concurrency=Fixed, and this column hold date time field from database, updated each time when row is edited.
In case I just retrieve entity - this column has correct value with milliseconds. But if I do mapping - milliseconds are removed.
Here is a issue at glance :
====================================================================
void Main()
{
var b = from p in TABLE1 where p.ID == 100 select p;
b.Dump();
}
Request in this case is : http://xxxx/Data.svc/TABLE1(100M)
And data returned from service is :
<d:COL1 m:type="Edm.DateTime">2015-02-16T12:13:52.972</d:COL1>
====================================================================
As you can see , here time is returned with milliseconds - .972
In other case :
void Main()
{
var tmp = from p in TABLE1 where p.ID == 100 select
new TABLE1()
{
ID=p.ID,
COL1=p.COL1
};
var a1 = tmp.ToList();
a1.Dump();
}
Request in this case is : http://xxxx/Data.svc/TABLE1(100M)?$select=ID,COL1
<d:COL1 m:type="Edm.DateTime">2015-02-16T12:13:52</d:COL1>
====================================================================
Time is returned without milliseconds.
Does anybody have same problem? May be its a bug in the WCF Data services or in the model?
Ok, seems like I found an answer to this, or at least way to avoid the problem.
First I traced generated SQL from framework, and I see that in first case, im getting SQL
SELECT ID, COL1 FROM TABLE1
and in second case, I got
SELECT ID, CAST( COL1 AS DATETIME) FROM TABLE1
which cause the problem.
Then I tried to update EF to version 6, WCF Data services to version 5.6.3,
Oracle ODP to latest one, tried to use Oracle managed driver... no any success.
Then I played little bit with table definition, and I saw that my col1 with type TIMESTAMP in database and DateTime in the model was defined as NOT NULL.
If I remove this from database definition, I got right value with milliseconds.
So , may be this is a bug in Oracle driver, but probably this is a bug in the WCF Data Services. At least I found a way to use concurrency in my case with this solution.
I have doubts about which is the best type for a field annotated with #Version for optimistic locking in JPA.
The API javadoc (http://docs.oracle.com/javaee/7/api/javax/persistence/Version.html) says:
"The following types are supported for version properties: int, Integer, short, Short, long, Long, java.sql.Timestamp."
In other page (http://en.wikibooks.org/wiki/Java_Persistence/Locking#Optimistic_Locking) says:
"JPA supports using an optimistic locking version field that gets updated on each update. The field can either be numeric or a timestamp value. A numeric value is recommended as a numeric value is more precise, portable, performant and easier to deal with than a timestamp."
"Timestamp locking is frequently used if the table already has a last updated timestamp column, and is also a convenient way to auto update a last updated column. The timestamp version value can be more useful than a numeric version, as it includes the relevant information on when the object was last updated."
The questions I have are:
Is better a Timestamp type if you are going to have a lastUpdated field or is better to have a numeric version field and the timestamp in other field?
Between numeric types (int, Integer, short, Short, long, Long) which is the best to choose (considering the length of each type)? I mean, I think the best is Long but it requires a lot of space for each row.
What happens when the version field gets to the last number of a numeric type (for example 32,767 in a Short field)? Will it start from 1 again in the next increment?
Just go with Long or Integer.
BUT don't go with int or long.
In opposite to other comment here, null value is expected when entity was never persisted yet.
Having int or long might make Hibernate to think that entity is already persisted and in detached state as version value will be 0 when unset.
Just finished debugging a FK violation where "int" was the cause, so save your time and just go with Long or Integer.
First, know that locking is used to managed concurrent transactions.
1.Separate your concerns. If lastupdated field is business model specific, it should be separate from your versioning field - which is for - versioning.
2.Primitives and objects are usually mapped to your db as the same type. Except for the fact that Boolean by default will be nullable and boolean will be 'not nullable'. However, enforce nullability explicitly. In this case you want to use a primitive as the version field can't be nullable.
Integer or long are better than timestamp. Hibernate recommends numeric versionig and they don't take that much space.
If you use long, you might not live to find out.
Use this and you should be fine.
private long version;
#Version
public long getVersion() {
return version;
}
public void setVersion(long version) {
this.version = version;
}
Don't use a time value like Timestamp (or derivates like Instant or LocalDateTime etc...).
Especially if you have a Java < 15 application and hope to ever migrate to Java >= 15. They changed the precision of timestamps within Java to nano-seconds, but your database probably only stores up to microseconds, so it truncates the value, which will make you run into an OptimisticLockException all the time (1).
Neither use a primitive value, see the answer from #Piotr: The Version field must be null for new entities.
Just go with Long.
I know that PostgreSQL 9+ has module to generate UUID. But after a lot of search I couldn't find any solution to generate UUID in PostgreSQL 8.2. I also need to keep this version because lot of my customers are still using 8.2. I am thinking to use current Timestamp as UUID.
Running select extract(epoch from now()); query and getting a Timestamp like 1384396600.53923. I want to store it as character and use as UUID. Is there any possibility of generating duplicate Timestamp? Also will it be feasible to use Timestamp as UUID?
i am not sure it is feasible.
but i suggest you not to use the Timestamp as UUID.
i don't konw how large concurrent your condition is.
if you are in a very much large concurrent condition,or in a very extreme condition by chance,i think it must will be give you two same Timestamp,so there will be something wrong,right?
so,use some other way to make a "UUID".
Let say you have a class with a java.util.Date field. Maybe a To do class or an Event planner class.
#Temporal(TemporalType.TIMESTAMP)
private Date startTime;
This will map to a datetime column in MySQL.
There are use cases when you want to know precisely when this Event occurs. "What is scheduled for Aug 11, 2011 at 8 PM?"
Sometimes you just want to know which events are planned for a specific date. "What is scheduled for August 11, 2011?"
If your JPAQL is:
SELECT blah blah etc. WHERE ti.startTime = :d1
and the parameter is a java.util.Date instance:
query.setParameter("d1", date, TemporalType.DATE);
your results will be restricted by the date but also the time.
This is such a common use case that I'm surprised that there is no simple way to do this even in JPA 2.0.
I'd rather not use a vendor specific hack nor play around with strings/substrings.
How have you solved this problem?
I know I'm a little late, but I've found a way to do this.
I was using a jpql like this:
select s from S s where date(s.someDate) = :param
And I set the param with:
query.setParameter("param", param, TemporalType.DATE);
The way I've found to do this with Criteria Query was:
builder.equal(
builder.function("date", Date.class, root.get(S_.someDate)),
param
);
At least, with MySql it works.
I hope it can help.
select ... where ti.startTime >= :theDay and ti.startTime < :theNextDay
is a relatively easy solution to implement, and works on any JPA implementation.
Hibernate also allows adding functions to a dialect, in order to generate custom SQL.