From 5a3233b9159d3934187fbc397b6404fb6ea7a808 Mon Sep 17 00:00:00 2001 From: Steve Ebersole Date: Thu, 19 Sep 2013 13:19:29 -0500 Subject: [PATCH] HHH-8523 - Incorrect parameter binding for Calendar and TemporalType --- .../AbstractParameterRegistrationImpl.java | 46 ++- .../org/hibernate/type/CalendarTimeType.java | 49 +++ .../java/CalendarTimeTypeDescriptor.java | 133 +++++++ .../test/procedure/DateTimeParameterTest.java | 347 ++++++++++++++++++ .../jpa/test/query/DateTimeParameterTest.java | 116 ++++++ 5 files changed, 678 insertions(+), 13 deletions(-) create mode 100644 hibernate-core/src/main/java/org/hibernate/type/CalendarTimeType.java create mode 100644 hibernate-core/src/main/java/org/hibernate/type/descriptor/java/CalendarTimeTypeDescriptor.java create mode 100644 hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/procedure/DateTimeParameterTest.java create mode 100644 hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/query/DateTimeParameterTest.java diff --git a/hibernate-core/src/main/java/org/hibernate/procedure/internal/AbstractParameterRegistrationImpl.java b/hibernate-core/src/main/java/org/hibernate/procedure/internal/AbstractParameterRegistrationImpl.java index 141895717c..d090a92efa 100644 --- a/hibernate-core/src/main/java/org/hibernate/procedure/internal/AbstractParameterRegistrationImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/procedure/internal/AbstractParameterRegistrationImpl.java @@ -37,6 +37,9 @@ import org.hibernate.engine.jdbc.cursor.spi.RefCursorSupport; import org.hibernate.engine.spi.SessionImplementor; import org.hibernate.procedure.ParameterBind; import org.hibernate.procedure.ParameterMisuseException; +import org.hibernate.type.CalendarDateType; +import org.hibernate.type.CalendarTimeType; +import org.hibernate.type.CalendarType; import org.hibernate.type.DateType; import org.hibernate.type.ProcedureParameterExtractionAware; import org.hibernate.type.Type; @@ -223,10 +226,37 @@ public abstract class AbstractParameterRegistrationImpl implements ParameterR throw new NotYetImplementedException( "Support for REF_CURSOR parameters not yet supported" ); } + // initially set up the Type we will use for binding as the explicit type. + Type typeToUse = hibernateType; + int[] sqlTypesToUse = sqlTypes; + + // however, for Calendar binding with an explicit TemporalType we may need to adjust this... + if ( bind.getExplicitTemporalType() != null ) { + if ( Calendar.class.isInstance( bind.getValue() ) ) { + switch ( bind.getExplicitTemporalType() ) { + case TIMESTAMP: { + typeToUse = CalendarType.INSTANCE; + sqlTypesToUse = typeToUse.sqlTypes( session().getFactory() ); + break; + } + case DATE: { + typeToUse = CalendarDateType.INSTANCE; + sqlTypesToUse = typeToUse.sqlTypes( session().getFactory() ); + break; + } + case TIME: { + typeToUse = CalendarTimeType.INSTANCE; + sqlTypesToUse = typeToUse.sqlTypes( session().getFactory() ); + break; + } + } + } + } + this.startIndex = startIndex; if ( mode == ParameterMode.IN || mode == ParameterMode.INOUT || mode == ParameterMode.OUT ) { if ( mode == ParameterMode.INOUT || mode == ParameterMode.OUT ) { - if ( sqlTypes.length > 1 ) { + if ( sqlTypesToUse.length > 1 ) { // there is more than one column involved; see if the Hibernate Type can handle // multi-param extraction... final boolean canHandleMultiParamExtraction = @@ -239,8 +269,8 @@ public abstract class AbstractParameterRegistrationImpl implements ParameterR ); } } - for ( int i = 0; i < sqlTypes.length; i++ ) { - statement.registerOutParameter( startIndex + i, sqlTypes[i] ); + for ( int i = 0; i < sqlTypesToUse.length; i++ ) { + statement.registerOutParameter( startIndex + i, sqlTypesToUse[i] ); } } @@ -259,16 +289,6 @@ public abstract class AbstractParameterRegistrationImpl implements ParameterR ); } else { - final Type typeToUse; - if ( bind.getExplicitTemporalType() != null && bind.getExplicitTemporalType() == TemporalType.TIMESTAMP ) { - typeToUse = hibernateType; - } - else if ( bind.getExplicitTemporalType() != null && bind.getExplicitTemporalType() == TemporalType.DATE ) { - typeToUse = DateType.INSTANCE; - } - else { - typeToUse = hibernateType; - } typeToUse.nullSafeSet( statement, bind.getValue(), startIndex, session() ); } } diff --git a/hibernate-core/src/main/java/org/hibernate/type/CalendarTimeType.java b/hibernate-core/src/main/java/org/hibernate/type/CalendarTimeType.java new file mode 100644 index 0000000000..0b16ff9fee --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/type/CalendarTimeType.java @@ -0,0 +1,49 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2013, Red Hat Inc. or third-party contributors as + * indicated by the @author tags or express copyright attribution + * statements applied by the authors. All third-party contributions are + * distributed under license by Red Hat Inc. + * + * This copyrighted material is made available to anyone wishing to use, modify, + * copy, or redistribute it subject to the terms and conditions of the GNU + * Lesser General Public License, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this distribution; if not, write to: + * Free Software Foundation, Inc. + * 51 Franklin Street, Fifth Floor + * Boston, MA 02110-1301 USA + */ +package org.hibernate.type; + +import java.util.Calendar; + +import org.hibernate.type.descriptor.java.CalendarTimeTypeDescriptor; +import org.hibernate.type.descriptor.sql.TimeTypeDescriptor; + +/** + * A type mapping {@link java.sql.Types#TIME TIME} and {@link Calendar}. + *

+ * For example, a Calendar attribute annotated with {@link javax.persistence.Temporal} and specifying + * {@link javax.persistence.TemporalType#TIME} + * + * @author Steve Ebersole + */ +public class CalendarTimeType extends AbstractSingleColumnStandardBasicType { + public static final CalendarTimeType INSTANCE = new CalendarTimeType(); + + public CalendarTimeType() { + super( TimeTypeDescriptor.INSTANCE, CalendarTimeTypeDescriptor.INSTANCE ); + } + + public String getName() { + return "calendar_time"; + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/CalendarTimeTypeDescriptor.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/CalendarTimeTypeDescriptor.java new file mode 100644 index 0000000000..c995837a98 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/CalendarTimeTypeDescriptor.java @@ -0,0 +1,133 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2010, Red Hat Inc. or third-party contributors as + * indicated by the @author tags or express copyright attribution + * statements applied by the authors. All third-party contributions are + * distributed under license by Red Hat Inc. + * + * This copyrighted material is made available to anyone wishing to use, modify, + * copy, or redistribute it subject to the terms and conditions of the GNU + * Lesser General Public License, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this distribution; if not, write to: + * Free Software Foundation, Inc. + * 51 Franklin Street, Fifth Floor + * Boston, MA 02110-1301 USA + */ +package org.hibernate.type.descriptor.java; + +import java.util.Calendar; +import java.util.Comparator; +import java.util.Date; +import java.util.GregorianCalendar; + +import org.hibernate.cfg.Environment; +import org.hibernate.internal.util.compare.CalendarComparator; +import org.hibernate.type.descriptor.WrapperOptions; + +/** + * Descriptor for {@link java.util.Calendar} handling, but just for the time portion. + * + * @author Steve Ebersole + */ +public class CalendarTimeTypeDescriptor extends AbstractTypeDescriptor { + public static final CalendarTimeTypeDescriptor INSTANCE = new CalendarTimeTypeDescriptor(); + + protected CalendarTimeTypeDescriptor() { + super( Calendar.class, CalendarTypeDescriptor.CalendarMutabilityPlan.INSTANCE ); + } + + public String toString(Calendar value) { + return DateTypeDescriptor.INSTANCE.toString( value.getTime() ); + } + + public Calendar fromString(String string) { + Calendar result = new GregorianCalendar(); + result.setTime( DateTypeDescriptor.INSTANCE.fromString( string ) ); + return result; + } + + @Override + public boolean areEqual(Calendar one, Calendar another) { + if ( one == another ) { + return true; + } + if ( one == null || another == null ) { + return false; + } + + return one.get(Calendar.DAY_OF_MONTH) == another.get(Calendar.DAY_OF_MONTH) + && one.get(Calendar.MONTH) == another.get(Calendar.MONTH) + && one.get(Calendar.YEAR) == another.get(Calendar.YEAR); + } + + @Override + public int extractHashCode(Calendar value) { + int hashCode = 1; + hashCode = 31 * hashCode + value.get(Calendar.DAY_OF_MONTH); + hashCode = 31 * hashCode + value.get(Calendar.MONTH); + hashCode = 31 * hashCode + value.get(Calendar.YEAR); + return hashCode; + } + + @Override + public Comparator getComparator() { + return CalendarComparator.INSTANCE; + } + + @SuppressWarnings({ "unchecked" }) + public X unwrap(Calendar value, Class type, WrapperOptions options) { + if ( value == null ) { + return null; + } + if ( Calendar.class.isAssignableFrom( type ) ) { + return (X) value; + } + if ( java.sql.Date.class.isAssignableFrom( type ) ) { + return (X) new java.sql.Date( value.getTimeInMillis() ); + } + if ( java.sql.Time.class.isAssignableFrom( type ) ) { + return (X) new java.sql.Time( value.getTimeInMillis() ); + } + if ( java.sql.Timestamp.class.isAssignableFrom( type ) ) { + return (X) new java.sql.Timestamp( value.getTimeInMillis() ); + } + if ( Date.class.isAssignableFrom( type ) ) { + return (X) new Date( value.getTimeInMillis() ); + } + throw unknownUnwrap( type ); + } + + public Calendar wrap(X value, WrapperOptions options) { + if ( value == null ) { + return null; + } + if ( Calendar.class.isInstance( value ) ) { + return (Calendar) value; + } + + if ( ! Date.class.isInstance( value ) ) { + throw unknownWrap( value.getClass() ); + } + + Calendar cal = new GregorianCalendar(); + if ( Environment.jvmHasTimestampBug() ) { + final long milliseconds = ( (Date) value ).getTime(); + final long nanoseconds = java.sql.Timestamp.class.isInstance( value ) + ? ( (java.sql.Timestamp) value ).getNanos() + : 0; + cal.setTime( new Date( milliseconds + nanoseconds / 1000000 ) ); + } + else { + cal.setTime( (Date) value ); + } + return cal; + } +} diff --git a/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/procedure/DateTimeParameterTest.java b/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/procedure/DateTimeParameterTest.java new file mode 100644 index 0000000000..37fea5d05b --- /dev/null +++ b/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/procedure/DateTimeParameterTest.java @@ -0,0 +1,347 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2013, Red Hat Inc. or third-party contributors as + * indicated by the @author tags or express copyright attribution + * statements applied by the authors. All third-party contributions are + * distributed under license by Red Hat Inc. + * + * This copyrighted material is made available to anyone wishing to use, modify, + * copy, or redistribute it subject to the terms and conditions of the GNU + * Lesser General Public License, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this distribution; if not, write to: + * Free Software Foundation, Inc. + * 51 Franklin Street, Fifth Floor + * Boston, MA 02110-1301 USA + */ +package org.hibernate.jpa.test.procedure; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.EntityManager; +import javax.persistence.Id; +import javax.persistence.ParameterMode; +import javax.persistence.StoredProcedureQuery; +import javax.persistence.Table; +import javax.persistence.Temporal; +import javax.persistence.TemporalType; +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.sql.Timestamp; +import java.util.Calendar; +import java.util.Collections; +import java.util.Date; +import java.util.GregorianCalendar; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.hibernate.dialect.DerbyTenSevenDialect; +import org.hibernate.engine.spi.SessionFactoryImplementor; +import org.hibernate.jpa.AvailableSettings; +import org.hibernate.jpa.HibernateEntityManagerFactory; +import org.hibernate.jpa.boot.spi.Bootstrap; +import org.hibernate.jpa.boot.spi.PersistenceUnitDescriptor; +import org.hibernate.jpa.internal.EntityManagerFactoryImpl; +import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import org.hibernate.testing.junit4.BaseUnitTestCase; + +import static org.junit.Assert.assertEquals; + +/** + * @author Steve Ebersole + */ +public class DateTimeParameterTest extends BaseUnitTestCase { + HibernateEntityManagerFactory entityManagerFactory; + + private static GregorianCalendar nowCal = new GregorianCalendar(); + private static Date now = new Date( nowCal.getTime().getTime() ); + + @Test + public void testBindingCalendarAsDate() { + EntityManager em = entityManagerFactory.createEntityManager(); + em.getTransaction().begin(); + + try { + StoredProcedureQuery query = em.createStoredProcedureQuery( "findMessagesByDate" ); + query.registerStoredProcedureParameter( 1, Calendar.class, ParameterMode.IN ); + query.setParameter( 1, nowCal, TemporalType.DATE ); + List list = query.getResultList(); + assertEquals( 1, list.size() ); + } + finally { + em.getTransaction().rollback(); + em.close(); + } + } + + @Test + public void testBindingCalendarAsTime() { + EntityManager em = entityManagerFactory.createEntityManager(); + em.getTransaction().begin(); + + try { + StoredProcedureQuery query = em.createStoredProcedureQuery( "findMessagesByTime" ); + query.registerStoredProcedureParameter( 1, Calendar.class, ParameterMode.IN ); + query.setParameter( 1, nowCal, TemporalType.TIME ); + List list = query.getResultList(); + assertEquals( 1, list.size() ); + } + finally { + em.getTransaction().rollback(); + em.close(); + } + } + + @Before + public void startUp() { + // create the EMF + entityManagerFactory = (EntityManagerFactoryImpl) Bootstrap.getEntityManagerFactoryBuilder( + buildPersistenceUnitDescriptor(), + buildSettingsMap() + ).build(); + + // create the procedures + createTestData( entityManagerFactory ); + createProcedures( entityManagerFactory ); + } + + private PersistenceUnitDescriptor buildPersistenceUnitDescriptor() { + return new BaseEntityManagerFunctionalTestCase.TestingPersistenceUnitDescriptorImpl( getClass().getSimpleName() ); + } + + @SuppressWarnings("unchecked") + private Map buildSettingsMap() { + Map settings = new HashMap(); + + settings.put( AvailableSettings.LOADED_CLASSES, Collections.singletonList( Message.class ) ); + + settings.put( org.hibernate.cfg.AvailableSettings.DIALECT, DerbyTenSevenDialect.class.getName() ); + settings.put( org.hibernate.cfg.AvailableSettings.DRIVER, org.apache.derby.jdbc.EmbeddedDriver.class.getName() ); + settings.put( org.hibernate.cfg.AvailableSettings.URL, "jdbc:derby:memory:hibernate-orm-testing;create=true" ); + settings.put( org.hibernate.cfg.AvailableSettings.USER, "" ); + + settings.put( org.hibernate.cfg.AvailableSettings.HBM2DDL_AUTO, "create-drop" ); + return settings; + } + + @After + public void tearDown() { + if ( entityManagerFactory == null ) { + return; + } + + deleteTestData( entityManagerFactory ); + dropProcedures( entityManagerFactory ); + + entityManagerFactory.close(); + } + + private void createProcedures(HibernateEntityManagerFactory emf) { + final SessionFactoryImplementor sf = emf.unwrap( SessionFactoryImplementor.class ); + final Connection conn; + try { + conn = sf.getConnectionProvider().getConnection(); + conn.setAutoCommit( false ); + + try { + Statement statement = conn.createStatement(); + + // drop them, just to be sure + try { + dropProcedures( statement ); + } + catch (SQLException ignore) { + } + + statement.execute( + "CREATE PROCEDURE findMessagesByDate(IN chkDt DATE) " + + "language java " + + "dynamic result sets 1 " + + "external name 'org.hibernate.jpa.test.procedure.DateTimeParameterTest.findMessagesByDate' " + + "parameter style java" + ); + + statement.execute( + "CREATE PROCEDURE findMessagesByTime(IN chkTime TIME) " + + "language java " + + "dynamic result sets 1 " + + "external name 'org.hibernate.jpa.test.procedure.DateTimeParameterTest.findMessagesByTime' " + + "parameter style java" + ); + + statement.execute( + "CREATE PROCEDURE findMessagesByTimestampRange(IN startDt TIMESTAMP, IN endDt TIMESTAMP) " + + "language java " + + "dynamic result sets 1 " + + "external name 'org.hibernate.jpa.test.procedure.DateTimeParameterTest.findMessagesByTimestampRange' " + + "parameter style java" + ); + + statement.execute( + "CREATE PROCEDURE retrieveTimestamp(IN ts1 TIMESTAMP, OUT ts2 TIMESTAMP) " + + "language java " + + "dynamic result sets 0 " + + "external name 'org.hibernate.jpa.test.procedure.DateTimeParameterTest.retrieveTimestamp' " + + "parameter style java" + ); + + try { + statement.close(); + } + catch (SQLException ignore) { + } + } + finally { + try { + conn.commit(); + } + catch (SQLException e) { + System.out.println( "Unable to commit transaction after creating creating procedures"); + } + + try { + sf.getConnectionProvider().closeConnection( conn ); + } + catch (SQLException ignore) { + } + } + } + catch (SQLException e) { + throw new RuntimeException( "Unable to create stored procedures", e ); + } + } + + private void dropProcedures(Statement statement) throws SQLException { + statement.execute( "DROP PROCEDURE findMessagesByDate" ); + statement.execute( "DROP PROCEDURE findMessagesByTime" ); + statement.execute( "DROP PROCEDURE findMessagesByTimestampRange" ); + statement.execute( "DROP PROCEDURE retrieveTimestamp" ); + } + + public static void findMessagesByDate(java.sql.Date date, ResultSet[] results) throws SQLException { + Connection conn = DriverManager.getConnection( "jdbc:default:connection" ); + PreparedStatement ps = conn.prepareStatement( "select * from msg where post_date=?" ); + ps.setDate( 1, date ); + results[0] = ps.executeQuery(); + conn.close(); + } + + public static void findMessagesByTime(java.sql.Time time, ResultSet[] results) throws SQLException { + Connection conn = DriverManager.getConnection( "jdbc:default:connection" ); + PreparedStatement ps = conn.prepareStatement( "select * from msg where post_time=?" ); + ps.setTime( 1, time ); + results[0] = ps.executeQuery(); + conn.close(); + } + + public static void findMessagesByTimestampRange(Timestamp start, Timestamp end, ResultSet[] results) throws SQLException { + Connection conn = DriverManager.getConnection( "jdbc:default:connection" ); + PreparedStatement ps = conn.prepareStatement( "select * from msg where ts between ? and ?" ); + ps.setDate( 1, new java.sql.Date( start.getTime() ) ); + ps.setDate( 2, new java.sql.Date( end.getTime() ) ); + results[0] = ps.executeQuery(); + conn.close(); + } + + public static void retrieveTimestamp(Timestamp in, Timestamp[] out ) throws SQLException { + out[0] = in; + } + + private void createTestData(HibernateEntityManagerFactory entityManagerFactory) { + EntityManager em = entityManagerFactory.createEntityManager(); + em.getTransaction().begin(); + em.persist( new Message( 1, "test", now, now, now ) ); + em.getTransaction().commit(); + em.close(); + } + + private void deleteTestData(HibernateEntityManagerFactory entityManagerFactory) { + EntityManager em = entityManagerFactory.createEntityManager(); + em.getTransaction().begin(); + em.createQuery( "delete from Message" ).executeUpdate(); + em.getTransaction().commit(); + em.close(); + } + + private void dropProcedures(HibernateEntityManagerFactory emf) { + + final SessionFactoryImplementor sf = emf.unwrap( SessionFactoryImplementor.class ); + final Connection conn; + try { + conn = sf.getConnectionProvider().getConnection(); + conn.setAutoCommit( false ); + + try { + Statement statement = conn.createStatement(); + dropProcedures( statement ); + try { + statement.close(); + } + catch (SQLException ignore) { + } + } + finally { + try { + conn.commit(); + } + catch (SQLException e) { + System.out.println( "Unable to commit transaction after creating dropping procedures"); + } + + try { + sf.getConnectionProvider().closeConnection( conn ); + } + catch (SQLException ignore) { + } + } + } + catch (SQLException e) { + throw new RuntimeException( "Unable to drop stored procedures", e ); + } + } + + @Entity( name = "Message" ) + @Table( name = "MSG" ) + public static class Message { + @Id + private Integer id; + private String body; + @Column( name = "POST_DATE" ) + @Temporal( TemporalType.DATE ) + private Date postDate; + @Column( name = "POST_TIME" ) + @Temporal( TemporalType.TIME ) + private Date postTime; + @Column( name = "TS" ) + @Temporal( TemporalType.TIMESTAMP ) + private Date ts; + + public Message() { + } + + public Message(Integer id, String body, Date postDate, Date postTime, Date ts) { + this.id = id; + this.body = body; + this.postDate = postDate; + this.postTime = postTime; + this.ts = ts; + } + } +} diff --git a/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/query/DateTimeParameterTest.java b/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/query/DateTimeParameterTest.java new file mode 100644 index 0000000000..26dbd37c1c --- /dev/null +++ b/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/query/DateTimeParameterTest.java @@ -0,0 +1,116 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2013, Red Hat Inc. or third-party contributors as + * indicated by the @author tags or express copyright attribution + * statements applied by the authors. All third-party contributions are + * distributed under license by Red Hat Inc. + * + * This copyrighted material is made available to anyone wishing to use, modify, + * copy, or redistribute it subject to the terms and conditions of the GNU + * Lesser General Public License, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this distribution; if not, write to: + * Free Software Foundation, Inc. + * 51 Franklin Street, Fifth Floor + * Boston, MA 02110-1301 USA + */ +package org.hibernate.jpa.test.query; + +import javax.persistence.Entity; +import javax.persistence.EntityManager; +import javax.persistence.Id; +import javax.persistence.Query; +import javax.persistence.Table; +import javax.persistence.Temporal; +import javax.persistence.TemporalType; +import java.util.Date; +import java.util.GregorianCalendar; +import java.util.List; + +import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase; + +import org.junit.Test; + +import static org.junit.Assert.assertEquals; + +/** + * @author Steve Ebersole + */ +public class DateTimeParameterTest extends BaseEntityManagerFunctionalTestCase { + private static GregorianCalendar nowCal = new GregorianCalendar(); + private static Date now = new Date( nowCal.getTime().getTime() ); + + @Override + protected Class[] getAnnotatedClasses() { + return new Class[] { Thing.class }; + } + + @Test + public void testBindingCalendarAsDate() { + createTestData(); + + EntityManager em = getOrCreateEntityManager(); + em.getTransaction().begin(); + + try { + Query query = em.createQuery( "from Thing t where t.someDate = :aDate" ); + query.setParameter( "aDate", nowCal, TemporalType.DATE ); + List list = query.getResultList(); + assertEquals( 1, list.size() ); + } + finally { + em.getTransaction().rollback(); + em.close(); + } + + deleteTestData(); + } + + private void createTestData() { + EntityManager em = getOrCreateEntityManager(); + em.getTransaction().begin(); + em.persist( new Thing( 1, "test", now, now, now ) ); + em.getTransaction().commit(); + em.close(); + } + + private void deleteTestData() { + EntityManager em = getOrCreateEntityManager(); + em.getTransaction().begin(); + em.createQuery( "delete Thing" ).executeUpdate(); + em.getTransaction().commit(); + em.close(); + } + + @Entity( name="Thing" ) + @Table( name = "THING" ) + public static class Thing { + @Id + public Integer id; + public String someString; + @Temporal( TemporalType.DATE ) + public Date someDate; + @Temporal( TemporalType.TIME ) + public Date someTime; + @Temporal( TemporalType.TIMESTAMP ) + public Date someTimestamp; + + public Thing() { + } + + public Thing(Integer id, String someString, Date someDate, Date someTime, Date someTimestamp) { + this.id = id; + this.someString = someString; + this.someDate = someDate; + this.someTime = someTime; + this.someTimestamp = someTimestamp; + } + } +}