Querying Unix Timestamp using JOOQ - mysql

I'm using a MySQL server, and I'm trying to retrieve a timestamp using the JOOQ API. However, I think the default JOOQ behavior returns the time in the local timezone, and not UTC (it's not using UNIX_TIMESTAMP(). Using JOOQ, my query looks like
db.select(USER_TABLE.REGISTERED_ON)
.from(USER_TABLE)
.where(USER_TABLE.EMAIL.equal(email)
.fetchAny()
However, the sql that I want to execute should be something like this
SELECT UNIX_TIMESTAMP(schema.table.timestamp_col)
FROM schema.table
WHERE email="someone#domain.com"
Is it possible using the JOOQ API? If not, what's the best way to run this query, because I really want to be able to use the generated code (USER_TABLE, USER_TABLE.REGISTERED_ON, etc).
EDIT: I'm now doing the following, but is it safe? Basically I'm removing the quotations from JOOQ's generated classes.
String timestamp_field = USER_TABLE.REGISTERED_ON.toString().replace("\"", "");
Field<?> f = DSL.field("UNIX_TIMESTAMP(" + timestamp_field + ")");
Record r = db.select(f)
.from(USER_TABLE)
.where(USER_TABLE.EMAIL.equal(email))
.fetchAny();

There are several ways to tackle this problem:
Use a Converter or Binding to convert the type
You can register a data type Converter or Binding in the source code generator. This way, the code generator will generate a Field<YourType> for every TIMESTAMP field in the database. Possible useful types are:
java.lang.Long
java.time.Instant
java.time.OffsetDateTime (note that jOOQ 3.7 will support this via [#4338])3
Use plain SQL
Use plain SQL every time you want to do an explicit conversion using the UNIX_TIMESTAMP() function. Your solution works:
String timestamp_field = USER_TABLE.REGISTERED_ON.toString().replace("\"", "");
Field<?> f = DSL.field("UNIX_TIMESTAMP(" + timestamp_field + ")");
But it is not recommended because:
You should never rely on any toString() implementation of any Java type.
You should generally try to avoid string concatenation with jOOQ's plain SQL API
A better solution would be:
DSL.field("UNIX_TIMESTAMP({0})", Long.class, USER_TABLE.REGISTERED_ON);
Or even:
public static Field<Long> unixTimestamp(Field<Timestamp> arg) {
return DSL.field("UNIX_TIMESTAMP({0})", Long.class, arg);
}
Use BIGINT in the database
You could of course use BIGINT or BIGINT UNSIGNED in the database instead of TIMESTAMP. This way, you will always automatically have the integer unix timestamp value. This is just a workaround for completeness's sake.

Related

Delphi datatype for SQL Server last_user_update

The SQL Server code below returns the time-date of the last update to a table (any row.)
SELECT OBJECT_NAME(OBJECT_ID) AS DatabaseName, last_user_update
FROM sys.dm_db_index_usage_stats
WHERE OBJECT_ID=OBJECT_ID('TableName')
AND (Index_ID = 1)
If I use this in a TFDQuery, what is the datatype FDQuery1.FieldByName('last_user_update')?
If I want to store and compare this value at different times in a Delphi variable, what Delphi datatype should I assign it to, Double?
You use a TDateTime for this.
Although the TDateTime is represented internally by a double you don't use a double because you'll miss out on all the date/time support.
The code goes like this.
var
LastUpdate: TDateTime;
begin
//Do query etc.
...
LastUpdate:= MyQuery.FieldByName('last_user_update').AsDateTime;
Note that SQL server 7 and before do not have support for TDate. So if you just want the date part this code will fail in SQL server 7.
LastUpdate:= MyQuery.FieldByName('last_user_update').AsDate;
Just get the full DateTime and strip of the Time part later.
However, you're working with 2008 so just extracting the date will work fine for you.
Here's a list of DateTime functions: http://www.delphibasics.co.uk/ByFunction.asp?Main=DatesAndTimes

Storing milliseconds in MySQL timestamp column using Hibernate

I'm trying to store a java Date with milliseconds in MySQL's timestamp column using Hibernate, but the millisecods are stored always as .000.
The definition of the column in hibernate is as follows:
#Type(type="timestamp")``
private Timestamp timestamp;
In DB the column is declared as TIMESTAMP(3)
I've tried different combinations, including Date, but neither helped.
I use MySQL 5.6.25, Connector/J version 5.1.37, Hibernate 4.0.1.
I've been investigating it for a while, but still couldn't find any solution that works form me.
ANy help will be appreciated.
Have you tried using DATETIME(3) or TIMESTAMP(4)? I believe both of these will give you the milliseconds. If you are trying to get the millisecond time where the interatction happens such as the row becomes updated you can use ON UPDATE DATETIME(3)
It seems that Hibernate and MySql interaction removes de milliseconds precision of your Date/Timestamp Java property. I have the same problem.
My solution is to "hack" hibernate telling that the entity property is an string and then serializing/deserializing the value in the setter/getter
#Column(name="time")
private String time
...
public Date getTime(){
return strTodate( this.time );
}
public void setTime(Date value){
this.time = dateToStr( value );
}
When MySQL receives an String for a Datetime(3) column, string is properly converted and milliseconds are not lost :-)
Fortunately, when reading from MySQL, Datetime(3) is propery serialized to string without milliseconds lost
The string date format used is "yyyy-MM-dd hh:mm:ss.SSS"
The idea of this solution is, don't let hibernate deal with dates. Delegate the responsability to MySql.
Unfortunatelly, MySQL doesn't accept an standard ISO string (i.e.: "yyyy-MM-ddThh:mm:ss.SSSZ") and this solution is not compatible with postgres (string date format is not the same)

Timestamp field can only by updated via behavior in Yii

In my model, I have a field named timestamp (MySQL type: timestamp) defined in safe validator and I'm unable to write it manually. Each time I call:
$model->timestmap = time();
$model->save();
model is saved (row created / updated), that is -- it passes validation without errors, but timestamp field is filled with default value of 0000-00-00 00:00:00 (I decided to remove default on update CURRENT_TIMESTAMP attribute, I don't want MySQL to handle this).
However, when I remove above code and attach CTimestampBehavior instead, field is being filled with correct values on both update and create, without any problems.
What can be happening or what am I missing? How can behavior update field without problems, while my manual attempt fails? Is this because column type is timestamp, not datetime or int?
I was always told, that first clue, why some attribute isn't saved, is because it is not listed among safe validator list or because it is listed on unsafe validator list. But this one is listed on safe list.
Your database field is a datetime field (i assume, looking at your default value), but your filling it with a unix timestamp. Try it with an CDbExpression instead:
$model->timestamp = new CDbExpression('NOW()');
$model->save();
I usually use $model->timestamp = date('Y-m-d H:i:s'), it works perfectly
As the other answers already suggested:
The timestamp needs to be converted into a MySQL compatible date format string somehow upon saving and the other way around when loading. Now you already discovered that the CTimestampBehavior does this for you but unfortunately it doesn't care about loading.
IMO the best solution for you is something along the way of:
public function beforeSave()
{
$this->timestamp = date('Y-m-d H:i:s', $this->timestamp);
return parent::beforeSave();
}
public function afterSave()
{
// Turn it back into a unix timestamp in case you want to continue working with the record
$this->timestamp = CDateTimeParser::parse($this->timestamp, 'yyyy-MM-dd hh:mm:ss');
parent::afterSave();
}
public function afterFind()
{
$this->timestamp = CDateTimeParser::parse($this->timestamp, 'yyyy-MM-dd hh:mm:ss');
return parent::afterFind();
}
It's a lot of work for a stupid timestamp and so for myself I have an auto type conversion behaviour that I link to my models. This behaviour uses the table metadata to take care of everything automatically. Might be a good idea to invest in that. I've thought about making mine open source but it's a bit of a mess atm ;)
But the code above will give you unix times to work with during executing and whilst saving it will temporary convert into a mysql datetime string
Hope that helps.

Dynamic Linq failing when using Contains against Int Field

I'm using Entity Frameworks 4.1.0.0 and MySQL.Data.Entity 6.5.4.0 and when I try and generate a dynamic query for a range of integers, I get an error of:
No applicable method 'Contains' exists in type 'Int32'
This seems to work fine when using a similar structure to check against Strings..but I want to expand this to support the other db fields I have in my data.
Code Example:
int[] ids = new int[] { 1, 3, 4 };
IQueryable<entityname> list = db.tablename.Where("Id.Contains(#0)", ids);
I have added in the Dynamic.cs to my project and followed along with
http://blog.walteralmeida.com/2010/05/advanced-linq-dynamic-linq-library-add-support-for-contains-extension-.html
but there has been no difference then using the Dynamic I loaded via Nuget.
Thank you in advance.
The syntax is slightly different:
IQueryable<entityname> list = db.tablename.Where("#0.Contains(outerIt.Id)", ids);
following the link you refer to.
If you need to check if a given (variable) int value is contained within a entity column, you can do the following using Dynamic Linq:
return query.Where(String.Format("{0}.ToString().Contains(#0)", field), value);
Check out this answer for an extension method that can perform such task with strings, integers and booleans column types in a rather seamless way.

How best to retrieve result of SELECT COUNT(*) from SQL query in Java/JDBC - Long? BigInteger?

I'm using Hibernate but doing a simple SQLQuery, so I think this boils down to a basic JDBC question. My production app runs on MySQL but my test cases use an in memory HSQLDB. I find that a SELECT COUNT operation returns BigInteger from MySQL but Long from HSQLDB.
MySQL 5.5.22
HSQLDB 2.2.5
The code I've come up with is:
SQLQuery tq = session.createSQLQuery(
"SELECT COUNT(*) AS count FROM calendar_month WHERE date = :date");
tq.setDate("date", eachDate);
Object countobj = tq.list().get(0);
int count = (countobj instanceof BigInteger) ?
((BigInteger)countobj).intValue() : ((Long)countobj).intValue();
This problem of the return type negates answers to other SO questions such as getting count(*) using createSQLQuery in hibernate? where the advice is to use setResultTransformer to map the return value into a bean. The bean must have a type of either BigInteger or Long, and fails if the type is not correct.
I'm reluctant to use a cast operator on the 'COUNT(*) AS count' portion of my SQL for fear of database interoperability. I realise I'm already using createSQLQuery so I'm already stepping outside the bounds of Hibernates attempts at database neutrality, but having had trouble before with the differences between MySQL and HSQLDB in terms of database constraints
Any advice?
I don't known a clear solution for this problem, but I will suggest you to use H2 database for your tests.
H2 database has a feature that you can connect using a compatibility mode to several different databases.
For example to use MySQL mode you connect to the database using this jdbc:h2:~/test;MODE=MySQL URL.
You can downcast to Number and then call the intValue() method. E.g.
SQLQuery tq = session.createSQLQuery("SELECT COUNT(*) AS count FROM calendar_month WHERE date = :date");
tq.setDate("date", eachDate);
Object countobj = tq.list().get(0);
int count = ((Number) countobj).intValue();
Two ideas:
You can get result value as String and then parse it to Long or BigInteger
Do not use COUNT(*) AS count FROM ..., better use something like COUNT(*) AS cnt ... but in your example code you do not use name of result column but it index, so you can use simply COUNT(*) FROM ...