I have a table timestamptest with a single column timestamp of type timestamp without time zone.
I inserted a value to this table :
insert into timestamptest values('2015-09-08 13:11:11')
The timestamp does not contain any millisecond value.
On selecting this data in pgAdmin, it is displayed same as above.
But when I fetch this data using jdbc connection, the value displayed is with milliseconds.
Class.forName("org.postgresql.Driver");
Connection lConnection = null;
lConnection = DriverManager.getConnection(
"jdbc:postgresql://localhost:5432/postgres","postgres", "Password#123");
String lQuery = "select * from timestamptest";
Statement lStatement = lConnection.createStatement();
ResultSet lResultSet = lStatement.executeQuery(lQuery);
while(lResultSet.next()) {
System.out.println(lResultSet.getTimestamp(1));
}
Output : 2015-09-08 13:11:11.0
The desired output is 2015-09-08 13:11:11
It can be achieved by using SimpleDateFormat :
new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(lResultSet.getTimestamp(1).getTime())
Can it be possible without using SimpleDateFormat? Is there any other way by which the result set itself gives me in the desired format?
What I need is that the statement
lResultSet.getTimestamp(1)
directly gives me the output 2015-09-08 13:11:11.
Its not possible. Since ResultSet.getTimestamp(1) return class that extends java.sql.TimeStamp. Returning class based on Database driver. And also we cant change the toString implementation of that.
Yes you can - but you're not going to like it.
class MyTimestamp extends Timestamp {
public MyTimestamp(long time) {
super(time);
}
public MyTimestamp(Timestamp ts) {
this(ts.getTime());
}
#Override
public String toString() {
String s = super.toString();
return s.substring(0, s.lastIndexOf("."));
}
}
public void test() {
System.out.println("Hello");
Timestamp t = new Timestamp(System.currentTimeMillis());
System.out.println(t);
System.out.println(new MyTimestamp(t));
}
Related
I am trying to create a named query for all the stringified arguments. This was how my stringified method was:
public Collection<Companypermcache> findExpiredPerms() {
String date = DateUtils.getSQLDate(DateUtils.getToday());
StringBuffer qbe = new StringBuffer("select cpc from Companypermcache cpc");
qbe.append(" where cpc.expire < '"+date+"'");
return super.findByQuery(qbe.toString());
}
I convert this to named query like below
String date = DateUtils.getSQLDate(DateUtils.getToday());
StringBuffer qbe = new StringBuffer("select cpc from Companypermcache cpc");
qbe.append(" where cpc.expire < :date");
return super.findByQuery(qbe.toString(),"date",date);
}
But this generates
java.lang.ClassCastException: java.lang.String cannot be cast to java.util.Date
at org.hibernate.type.descriptor.java.JdbcTimestampTypeDescriptor.unwrap(JdbcTimestampTypeDescriptor.java:24)
at org.hibernate.type.descriptor.sql.TimestampTypeDescriptor$1.doBind(TimestampTypeDescriptor.java:48)
at org.hibernate.type.descriptor.sql.BasicBinder.bind(BasicBinder.java:74)
at org.hibernate.type.AbstractStandardBasicType.nullSafeSet(AbstractStandardBasicType.java:253)
at org.hibernate.type.AbstractStandardBasicType.nullSafeSet(AbstractStandardBasicType.java:248)
at org.hibernate.param.NamedParameterSpecification.bind(NamedParameterSpecification.java:52)
at org.hibernate.loader.hql.QueryLoader.bindParameterValues(QueryLoader.java:627) ...
Exception.
However If I update the named query like below I don't have any exception and everything looks good.
String date = DateUtils.getSQLDate(DateUtils.getToday());
DateFormat simpleDateFormat=new SimpleDateFormat("yyyy-MM-dd");
Date edate = simpleDateFormat.parse(date);
StringBuffer qbe = new StringBuffer("select cpc from Companypermcache cpc");
qbe.append(" where cpc.expire < :date");
return super.findByQuery(qbe.toString(),"date",edate);
}
However I really did not understand the difference and and the need of parsing.I have many place in my project where I am using getSQLDate(), So I am concerned whether I want to parse all those dates?
In addition to that type for edate in my table is DATETIME
Why we need to parse the Date when we pass a date as argument to named query????
I am using a SQLite database with tables that include DATETIME columns. jOOQ default binds the DATETIME columns to java.sql.Timestamp. Querying tables with DATETIME columns causes a NumberFormatException (handled) for each column.
I am using jOOQ 3.11.9.
The exception is thrown in the org.jooq.impl.DefaultBinding.DefaultTimestampBinding parse method when it first tries to convert the timestamp string value as a number.
private static final long parse(Class < ? extends java.util.Date > type, String date) throws SQLException {
// Try reading a plain number first
try {
return Long.valueOf(date);
}
// If that fails, try reading a formatted date
catch (NumberFormatException e) {
// [#7325] In SQLite dates could be stored in both ISO standard formats:
// With T (default standard), or without T (optional standard, JDBC standard)
date = StringUtils.replace(date, "T", " ");
if (type == Timestamp.class)
return Timestamp.valueOf(date).getTime();
// Dates may come with " 00:00:00". This is safely trimming time information
else if (type == Date.class)
return Date.valueOf(date.split(" ")[0]).getTime();
else if (type == Time.class)
return Time.valueOf(date).getTime();
throw new SQLException("Could not parse date " + date, e);
}
}
Looking at the the get0 and set0 DefaultTimestampBinding methods the Timestamp is always get/set as a String. Is there a reason why for SQLite this is not passed to the JDBC statement/result as a Timestamp? Is there any way to override this behavior or avoid the exception?
Override
final void set0(BindingSetStatementContext < U > ctx, Timestamp value) throws SQLException {
if (ctx.family() == SQLITE)
ctx.statement().setString(ctx.index(), value.toString());
else
ctx.statement().setTimestamp(ctx.index(), value);
}
#Override
final Timestamp get0(BindingGetResultSetContext < U > ctx) throws SQLException {
// SQLite's type affinity needs special care...
if (ctx.family() == SQLDialect.SQLITE) {
String timestamp = ctx.resultSet().getString(ctx.index());
return timestamp == null ? null : new Timestamp(parse(Timestamp.class, timestamp));
} else {
return ctx.resultSet().getTimestamp(ctx.index());
}
}
While you could register a custom binding with the code generator, note that this issue will be addressed in the upcoming jOOQ 3.12 release as well as in the next 3.11 service release. See https://github.com/jOOQ/jOOQ/issues/8736 for details.
I need to perfom a select by date from my DB in my spring boot webapp. What I have so far is a list of sport competitions and there respective informations.
Problem : I can not figure out how my select query convert my String type (dateFrom = '2017-05-02' and dateTo = '2017-05-06') to date like '2017-02-12' in the ?
Alos how to fill my RowMapper with more then one date in some competition which have more then one date.
My data base schema:
CREATE TABLE competition (
competition_id integer PRIMARY KEY,
nom varchar(128) NOT NULL,
);
CREATE TABLE date (
id integer PRIMARY KEY,
date_time timestamptz,
competition_id integer REFERENCES competition (competition_id)
);
Json data:
{
"id": "420",
"name": "SOCCER",
"dates": [
"2016-05-12T03:00:00.000Z"
"2016-05-12T04:00:00.000Z"
"2016-05-12T05:00:00.000Z"
]
},
{
"id": "220",
"name": "BASKETBALL",
"dates": [
"2016-05-12T03:00:00.000Z"
"2016-05-12T04:00:00.000Z"
]
}
My competition Class:
public class Competition{
private int id;
private String name;
private String[] dates;
// setters ... getters
}
My RowMapper Class:
public class RowMapper implements RowMapper
{
public Object mapRow(ResultSet rs, int rowNum) throws SQLException {
Competition competition = new Competition();
competition.setId(rs.getInt("id"));
competition.setName(rs.getString("name"));
competition. // How to fill dates
return competition;
}
}
Function to select data :
private static final String SELECT_STMT =
" select * from competition INNER JOIN date ON
+ " competition.competition_id = date.competition_id"
+ " WHERE date(date.date_time) BETWEEN ? AND ?"
;
public List<Competition> findByOptionsAll(String dateFrom, String dateTo ){
List<Competition> competitions = jdbcTemplate.query(SELECT_STMT, new
RowMapper(), dateFrom, dateTo);
return competitions ;
}
Date converting
Right now you have all dates as a String both in your DB and domain model. To convert strings to date you need a date formatter:
private static final String DATE_FORMAT = "dd-MM-yy";
// parsing date; Note you should handle ParseException
java.util.Date date = new SimpleDateFormat(DATE_FORMAT).parse(dateAsString);
// converting date to string
String dateAsString = new SimpleDateFormat(DATE_FORMAT).format(date);
Note that SimpleDateFormat is not thread-safe so it's a good practice to have static final String DATE_FORMAT instead of static final DateFormatter
Converting date and time is tricky in some cases (what about time zone? java.util.Date vs joda.time vs LocalDate from Java 8) but out of scope. I suggest use LocalDate if possible just because it's a new way without old issues.
Mapping
You have two entities in your DB (Competition and Date-of-competition) and only one class Competition in your domain model. Most probably, later you'll want to add additional info to the Date-of-competition (boolean finished, cancelled, Score etc) so it's a good idea to create CompetitionInstance class right now.
Since you have One-to-Many relationship you have to write some additional stuff to map objects. Normally that's what an ORM like Hibernate do istead of you. First, add a 'GROUP BY competition_id' in your sql statement.
Then use RowSetExtractor instead of RowMapper as described here:
private static final class CompetitionMapExtractor implements ResultSetExtractor<List<Competition>> {
#Override
public List<Competition> extractData(ResultSet rs) throws SQLException {
List<Competition> result = new ArrayList<>(rs.getCount());
int previousCompetitionId = NEVER_EXIST; // normally -1 is good enough
while (rs.next()) {
// we have some dates with the same competition_id
// dates are grouped thanks to GROUP BY clause
if ( rs.getInt("id") != previousCompetitionId) {
Competition currentCompetition = new Competition(rs.getInt("id"),
rs.getString("name");
/* I prefer constructor initializers "o = new O(propertyValue)"
instead of snippet "o = new O(); o.setProperty(value)"
*/
result.add(currentCompetition);
previousCompetitionId = currentCompetition.getid();
} else {
currentCompetition.addDate(new CompetitionInstance(rs.getString("date")));
}
}
return result;
}
I suppose Competition has method public void addDate(String date) which simply add a new CompetitionInstance to a list.
Update:
1.
column name in DB and in MapExtractor is different. I prefer to change the query:
SELECT c.id, c.name, d.date_time as date
from competition c
INNER JOIN date d ON c.competition_id = d.competition_id
WHERE date(d.date_time) BETWEEN ? AND ?"
2. I can't reproduce issues you have with date. Most probably you mixed up java.util.Date, java.sql.Date and java.sql.Timestamp - this is a common mistake. There are many answers already, probably you could find one of them useful.
What i am trying to do:
Do 2 rounds of validation to retrieve the data from MySQL, first is to check the time of the day, second to check if that timing is taken, if the timing is not taken i take the data from that row and add it into an arraylist.
Problems:
My logic for this is that i run the loop while(rs.next()), i will do my first round of checking which is to check the timing, after that when the checking passes it goes into another if, and when that passes the check it will get that data from that row and add it into arraylist. However when i run the code, i believe the checking is working BUT it stores the same data over and over again into the arraylist. and in particular it is storing the last row's data, for example where id 1,2,3,4,5,6 passes the validation it will just store id6 over and over again.
try {
PreparedStatement stmt = so.getPreparedStatementWithKey("SELECT * FROM et_elderly WHERE room = ?");
stmt.setString(1, roomNum);
stmt.executeQuery();
rs = stmt.getResultSet();
while(rs.next()){
if(dosageTime.equalsIgnoreCase("morning")){
if(rs.getInt("morningtaken")==0){
// calculate the age
java.sql.Date reportDate=rs.getDate("dob");
DateFormat df = new SimpleDateFormat("yyyy/MM/dd");
String text = df.format(reportDate);
String year=text.substring(0, 4);
String month=text.substring(5,7);
String day=text.substring(8,10);
// setting the information
data.setElderBed(rs.getInt("bed"));
data.setElderName(rs.getString("name"));
data.setElderAge(ElderData.getAge(year,month,day));
data.setElderGender(rs.getString("gender"));
DosageList.add(data);
numofElder++;
}
}
else if (dosageTime.equalsIgnoreCase("afternoon")){
if(rs.getInt("afternoontaken")==0){
// calculate the age
java.sql.Date reportDate=rs.getDate("dob");
DateFormat df = new SimpleDateFormat("yyyy/MM/dd");
String text = df.format(reportDate);
String year=text.substring(0, 4);
String month=text.substring(5,7);
String day=text.substring(8,10);
// setting the information
data.setElderBed(rs.getInt("bed"));
data.setElderName(rs.getString("name"));
data.setElderAge(ElderData.getAge(year,month,day));
data.setElderGender(rs.getString("gender"));
DosageList.add(data);
numofElder++;
}
}
else if (dosageTime.equalsIgnoreCase("noon")){
if(rs.getInt("noontaken")==0){
// calculate the age
java.sql.Date reportDate=rs.getDate("dob");
DateFormat df = new SimpleDateFormat("yyyy/MM/dd");
String text = df.format(reportDate);
String year=text.substring(0, 4);
String month=text.substring(5,7);
String day=text.substring(8,10);
// setting the information
data.setElderBed(rs.getInt("bed"));
data.setElderName(rs.getString("name"));
data.setElderAge(ElderData.getAge(year,month,day));
data.setElderGender(rs.getString("gender"));
DosageList.add(data);
numofElder++;
}
}
}
} catch (SQLException e1) {
e1.printStackTrace();
}
You're using a single instance of data which is simply updated on each iteration of the ResultSet...
while(rs.next()){
if(dosageTime.equalsIgnoreCase("morning")){
if(rs.getInt("morningtaken")==0){
//...
// You never create a new "data" object...
// setting the information
data.setElderBed(rs.getInt("bed"));
data.setElderName(rs.getString("name"));
data.setElderAge(ElderData.getAge(year,month,day));
data.setElderGender(rs.getString("gender"));
DosageList.add(data);
numofElder++;
}
}
So, basically, you're only going to end up with a List of objects which are all the same object. You could test using a Set of some kind and you'd end up with a single item in your Set.
Instead, you might want to consider using a Factory or Builder pattern to ensure that on each iteration through the ResultSet, you are creating a new instance of data filled with the properties from the database
I'm creating an email using String Template but when I print out a date, it prints out the full date (eg. Wed Apr 28 10:51:37 BST 2010). I'd like to print it out in the format dd/mm/yyyy but don't know how to format this in the .st file.
I can't modify the date individually (using java's simpleDateFormatter) because I iterate over a collection of objects with dates.
Is there a way to format the date in the .st email template?
Use additional renderers like this:
internal class AdvancedDateTimeRenderer : IAttributeRenderer
{
public string ToString(object o)
{
return ToString(o, null);
}
public string ToString(object o, string formatName)
{
if (o == null)
return null;
if (string.IsNullOrEmpty(formatName))
return o.ToString();
DateTime dt = Convert.ToDateTime(o);
return string.Format("{0:" + formatName + "}", dt);
}
}
and then add this to your StringTemplate such as:
var stg = new StringTemplateGroup("Templates", path);
stg.RegisterAttributeRenderer(typeof(DateTime), new AdvancedDateTimeRenderer());
then in st file:
$YourDateVariable; format="dd/mm/yyyy"$
it should work
Here is a basic Java example, see StringTemplate documentation on Object Rendering for more information.
StringTemplate st = new StringTemplate("now = $now$");
st.setAttribute("now", new Date());
st.registerRenderer(Date.class, new AttributeRenderer(){
public String toString(Object date) {
SimpleDateFormat f = new SimpleDateFormat("dd/MM/yyyy");
return f.format((Date) date);
}
});
st.toString();
StringTemplate 4 includes a DateRenderer class.
My example below is a modified version of the NumberRenderer on the documentation on Renderers in Java
String template =
"foo(right_now) ::= << <right_now; format=\"full\"> >>\n";
STGroup g = new STGroupString(template);
g.registerRenderer(Date.class, new DateRenderer());
ST st = group.getInstanceOf("foo");
st.add("right_now", new Date());
String result = st.render();
The provided options for format map as such:
"short" => DateFormat.SHORT (default)
"medium" => DateFormat.MEDIUM
"long" => DateFormat.LONG
"full" => DateFormat.FULL
Or, you can use a custom format like so:
foo(right_now) ::= << <right_now; format="MM/dd/yyyy"> >>
You can see these options and other details on the DateRenderer Java source here
one very important fact while setting date format is to use "MM" instead of "mm" for month. "mm" is meant to be used for minutes. Using "mm" instead of "MM" very generally introduces bugs difficult to find.