ItEr14S08ModeladoTempoItEr13S09: Adding hibernate types to allow mapping of partial dates and related classes.

This commit is contained in:
Óscar González Fernández 2009-06-27 16:49:23 +02:00 committed by Javier Moran Rua
parent 4860a11e54
commit 86f5cad2d7
10 changed files with 602 additions and 0 deletions

View file

@ -1,5 +1,7 @@
package org.navalplanner.business.common.partialtime; package org.navalplanner.business.common.partialtime;
import java.io.Serializable;
import java.sql.Timestamp;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.Date; import java.util.Date;
@ -389,4 +391,16 @@ public class PartialDate implements ReadablePartial {
+ this.granularity); + this.granularity);
return get(granularity.getMostSpecific()); return get(granularity.getMostSpecific());
} }
public Serializable[] getDataForPersistence() {
return new Serializable[] {
new Timestamp(normalizedInstant.getMillis()),
granularity.name() };
}
public static PartialDate createFromDataForPersistence(Object... values) {
Timestamp instant = (Timestamp) values[0];
Granularity granularity = Granularity.valueOf((String) values[1]);
return PartialDate.createFrom(instant).with(granularity);
}
} }

View file

@ -0,0 +1,135 @@
package org.navalplanner.business.common.partialtime.hibernate;
import java.io.Serializable;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Properties;
import org.hibernate.HibernateException;
import org.hibernate.engine.SessionImplementor;
import org.hibernate.type.CustomType;
import org.hibernate.type.Type;
import org.hibernate.usertype.CompositeUserType;
import org.navalplanner.business.common.partialtime.IntervalOfPartialDates;
import org.navalplanner.business.common.partialtime.PartialDate;
public class IntervalOfPartialDatesType implements CompositeUserType {
private static final String[] PROPERTY_NAMES = { "start", "end" };
private final PartialDateType PARTIAL_DATE_TYPE = new PartialDateType();
@Override
public IntervalOfPartialDates assemble(Serializable cached,
SessionImplementor session,
Object owner) throws HibernateException {
Object[] components = (Object[]) cached;
PartialDate start = PARTIAL_DATE_TYPE.assemble(
(Serializable) components[0], owner);
PartialDate end = PARTIAL_DATE_TYPE.assemble(
(Serializable) components[1], owner);
return new IntervalOfPartialDates(start, end);
}
@Override
public Serializable disassemble(Object value, SessionImplementor session)
throws HibernateException {
IntervalOfPartialDates interval = (IntervalOfPartialDates) value;
return new Object[] {
PARTIAL_DATE_TYPE.disassemble(interval.getStart()),
PARTIAL_DATE_TYPE.disassemble(interval.getEnd()) };
}
@Override
public Object deepCopy(Object value) throws HibernateException {
return value;
}
@Override
public boolean equals(Object x, Object y) throws HibernateException {
if (x == y)
return true;
if (x == null || y == null)
return false;
return x.equals(y);
}
@Override
public String[] getPropertyNames() {
return PROPERTY_NAMES;
}
@Override
public Type[] getPropertyTypes() {
Properties emptyProperties = new Properties();
CustomType partialDateTypeAsType = new CustomType(
PartialDateType.class,
emptyProperties);
return new Type[] { partialDateTypeAsType, partialDateTypeAsType };
}
@Override
public Object getPropertyValue(Object component, int property)
throws HibernateException {
IntervalOfPartialDates interval = (IntervalOfPartialDates) component;
return property == 0 ? interval.getStart() : interval.getEnd();
}
@Override
public int hashCode(Object x) throws HibernateException {
return x.hashCode();
}
@Override
public boolean isMutable() {
return false;
}
@Override
public Object nullSafeGet(ResultSet rs, String[] names,
SessionImplementor session, Object owner)
throws HibernateException, SQLException {
PartialDate start = PARTIAL_DATE_TYPE.nullSafeGet(rs, subArray(names,
0, 2), owner);
PartialDate end = PARTIAL_DATE_TYPE.nullSafeGet(rs, subArray(names, 2,
2), owner);
if (start == null || end == null)
return null;
return new IntervalOfPartialDates(start, end);
}
private static String[] subArray(String[] array, int initialPosition,
int size) {
String[] result = new String[size];
System.arraycopy(array, initialPosition, result, 0, size);
return result;
}
@Override
public void nullSafeSet(PreparedStatement st, Object value, int index,
SessionImplementor session) throws HibernateException, SQLException {
IntervalOfPartialDates interval = (IntervalOfPartialDates) value;
PARTIAL_DATE_TYPE.nullSafeSet(st, interval.getStart(), index);
PARTIAL_DATE_TYPE.nullSafeSet(st, interval.getEnd(), index + 2);
}
@Override
public Object replace(Object original, Object target,
SessionImplementor session, Object owner) throws HibernateException {
return original;
}
@Override
public Class returnedClass() {
return IntervalOfPartialDates.class;
}
@Override
public void setPropertyValue(Object component, int property, Object value)
throws HibernateException {
throw new UnsupportedOperationException(
"IntervalOfPartialDates is immutable");
}
}

View file

@ -0,0 +1,115 @@
package org.navalplanner.business.common.partialtime.hibernate;
import java.io.Serializable;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.sql.Types;
import org.hibernate.Hibernate;
import org.hibernate.HibernateException;
import org.hibernate.usertype.UserType;
import org.navalplanner.business.common.partialtime.PartialDate;
/**
* Persists a {@link PartialDate} through hibernate. <br />
* @author Óscar González Fernández<ogonzalez@igalia.com>
*/
public class PartialDateType implements UserType {
// TODO consider the possibility of using an integer for storing the enum
private static final int[] SQL_TYPES = { Types.TIMESTAMP, Types.VARCHAR };
@Override
public int[] sqlTypes() {
return SQL_TYPES;
}
@Override
public Class returnedClass() {
return PartialDate.class;
}
public static class CachedRepresentation implements Serializable {
private final Serializable[] fields;
private CachedRepresentation(Serializable[] fields) {
this.fields = fields;
}
public PartialDate toOriginal() {
return PartialDate.createFromDataForPersistence(fields[0],
fields[1]);
}
}
@Override
public PartialDate assemble(Serializable cached, Object owner)
throws HibernateException {
CachedRepresentation representation = (CachedRepresentation) cached;
return representation.toOriginal();
}
@Override
public Serializable disassemble(Object value) throws HibernateException {
PartialDate partialDate = (PartialDate) value;
return new CachedRepresentation(partialDate.getDataForPersistence());
}
public Object deepCopy(Object value) throws HibernateException {
return value;
}
@Override
public boolean equals(Object x, Object y) throws HibernateException {
if (x == y)
return true;
if (x == null || y == null)
return false;
return x.equals(y);
}
@Override
public int hashCode(Object x) throws HibernateException {
return x.hashCode();
}
@Override
public boolean isMutable() {
return false;
}
@Override
public PartialDate nullSafeGet(ResultSet rs, String[] names, Object owner)
throws HibernateException, SQLException {
Timestamp timestamp = (Timestamp) Hibernate.TIMESTAMP.nullSafeGet(rs,
names[0]);
String granularity = (String) Hibernate.STRING
.nullSafeGet(rs, names[1]);
if (timestamp == null || granularity == null)
return null;
return PartialDate.createFromDataForPersistence(timestamp, granularity);
}
@Override
public void nullSafeSet(PreparedStatement st, Object value, int index)
throws HibernateException, SQLException {
Timestamp timeToSet = null;
String granularityToSet = null;
PartialDate partialDate = (PartialDate) value;
if (partialDate != null) {
Serializable[] dataForPersistence = partialDate
.getDataForPersistence();
timeToSet = (Timestamp) dataForPersistence[0];
granularityToSet = (String) dataForPersistence[1];
}
Hibernate.TIMESTAMP.nullSafeSet(st, timeToSet, index);
Hibernate.STRING.nullSafeSet(st, granularityToSet, index + 1);
}
@Override
public Object replace(Object original, Object target, Object owner)
throws HibernateException {
return original;
}
}

View file

@ -0,0 +1,111 @@
package org.navalplanner.business.common.partialtime.hibernate;
import java.io.Serializable;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;
import java.util.Iterator;
import net.sf.json.JSONObject;
import org.hibernate.Hibernate;
import org.hibernate.HibernateException;
import org.hibernate.usertype.UserType;
import org.navalplanner.business.common.partialtime.TimeQuantity;
import org.navalplanner.business.common.partialtime.PartialDate.Granularity;
public class TimeQuantityType implements UserType {
private static final int[] SQL_TYPES = { Types.VARCHAR };
@Override
public int[] sqlTypes() {
return SQL_TYPES;
}
private static String asString(TimeQuantity timeQuantity) {
JSONObject jsonObject = new JSONObject();
for (Granularity granularity : Granularity.values()) {
Integer value = timeQuantity.valueFor(granularity);
if (value != 0)
jsonObject.put(granularity.name(), value);
}
return jsonObject.toString();
}
private static TimeQuantity fromString(String timeQuantityAsString) {
JSONObject jsonObject = JSONObject.fromObject(timeQuantityAsString);
Iterator keys = jsonObject.keys();
TimeQuantity result = TimeQuantity.empty();
while(keys.hasNext()){
Object key = keys.next();
Object object = jsonObject.get(key);
result = result.plus((Integer) object, Granularity
.valueOf((String) key));
}
return result;
}
@Override
public Object assemble(Serializable cached, Object owner)
throws HibernateException {
return fromString((String) cached);
}
@Override
public Serializable disassemble(Object value) throws HibernateException {
return asString((TimeQuantity) value);
}
@Override
public Object deepCopy(Object value) throws HibernateException {
return value;
}
@Override
public boolean equals(Object x, Object y) throws HibernateException {
if (x == y)
return true;
if (x == null || y == null)
return false;
return x.equals(y);
}
@Override
public int hashCode(Object x) throws HibernateException {
return x.hashCode();
}
@Override
public boolean isMutable() {
return false;
}
@Override
public Object nullSafeGet(ResultSet rs, String[] names, Object owner)
throws HibernateException, SQLException {
String timeQuantityAsString = (String) Hibernate.STRING.nullSafeGet(rs,
names);
return fromString(timeQuantityAsString);
}
@Override
public void nullSafeSet(PreparedStatement st, Object value, int index)
throws HibernateException, SQLException {
Hibernate.STRING.nullSafeSet(st, asString((TimeQuantity) value), index);
}
@Override
public Object replace(Object original, Object target, Object owner)
throws HibernateException {
return original;
}
@Override
public Class returnedClass() {
return TimeQuantity.class;
}
}

View file

@ -0,0 +1,37 @@
package org.navalplanner.business.common.test.partialtime;
import org.navalplanner.business.common.partialtime.IntervalOfPartialDates;
public class EntityContainingIntervalOfPartialDates {
private Long id;
private Long version;
private IntervalOfPartialDates interval;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public Long getVersion() {
return version;
}
public void setVersion(Long version) {
this.version = version;
}
public IntervalOfPartialDates getInterval() {
return interval;
}
public void setInterval(IntervalOfPartialDates interval) {
this.interval = interval;
}
}

View file

@ -0,0 +1,38 @@
package org.navalplanner.business.common.test.partialtime;
import org.navalplanner.business.common.partialtime.PartialDate;
public class EntityContainingPartialDate {
private Long id;
private Long version;
private PartialDate partialDate;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public Long getVersion() {
return version;
}
public void setVersion(Long version) {
this.version = version;
}
public PartialDate getPartialDate() {
return partialDate;
}
public void setPartialDate(PartialDate partialDate) {
this.partialDate = partialDate;
}
}

View file

@ -0,0 +1,37 @@
package org.navalplanner.business.common.test.partialtime;
import org.navalplanner.business.common.partialtime.TimeQuantity;
public class EntityContainingTimeQuantity {
private Long id;
private Long version;
private TimeQuantity timeQuantity;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public Long getVersion() {
return version;
}
public void setVersion(Long version) {
this.version = version;
}
public TimeQuantity getTimeQuantity() {
return timeQuantity;
}
public void setTimeQuantity(TimeQuantity timeQuantity) {
this.timeQuantity = timeQuantity;
}
}

View file

@ -0,0 +1,79 @@
package org.navalplanner.business.common.test.partialtime;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.junit.Assert.assertThat;
import static org.navalplanner.business.BusinessGlobalNames.BUSINESS_SPRING_CONFIG_FILE;
import static org.navalplanner.business.test.BusinessGlobalNames.BUSINESS_SPRING_CONFIG_TEST_FILE;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.joda.time.LocalDate;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.navalplanner.business.common.partialtime.IntervalOfPartialDates;
import org.navalplanner.business.common.partialtime.PartialDate;
import org.navalplanner.business.common.partialtime.TimeQuantity;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.transaction.annotation.Transactional;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { BUSINESS_SPRING_CONFIG_FILE,
BUSINESS_SPRING_CONFIG_TEST_FILE })
@Transactional
public class PartialDateRelatedClassesHibernateMappingTest {
@Autowired
private SessionFactory sessionFactory;
public Session getSession() {
return sessionFactory.getCurrentSession();
}
@Test
public void partialDatesCanBeSavedAndRetrieved() {
EntityContainingPartialDate entity = new EntityContainingPartialDate();
PartialDate partialDate = PartialDate.createFrom(new LocalDate(2000, 4,
20));
entity.setPartialDate(partialDate);
getSession().save(entity);
getSession().flush();
getSession().evict(entity);
EntityContainingPartialDate reloaded = (EntityContainingPartialDate) getSession()
.get(EntityContainingPartialDate.class, entity.getId());
assertThat(reloaded.getPartialDate(), equalTo(partialDate));
}
@Test
public void intervalsCanBeSavedAndRetrieved() {
EntityContainingIntervalOfPartialDates entity = new EntityContainingIntervalOfPartialDates();
PartialDate start = PartialDate.createFrom(new LocalDate(2000, 4, 20));
PartialDate end = PartialDate.createFrom(new LocalDate(2001, 4, 23));
IntervalOfPartialDates original = new IntervalOfPartialDates(start, end);
entity.setInterval(original);
getSession().save(entity);
getSession().flush();
getSession().evict(entity);
EntityContainingIntervalOfPartialDates reloaded = (EntityContainingIntervalOfPartialDates) getSession()
.get(
EntityContainingIntervalOfPartialDates.class, entity.getId());
assertThat(reloaded.getInterval(), equalTo(original));
}
@Test
public void timeQuantitysCanBeSavedAndRetrieved() {
EntityContainingTimeQuantity entity = new EntityContainingTimeQuantity();
TimeQuantity duration = new IntervalOfPartialDates(PartialDate.createFrom(new LocalDate(2000, 4, 20)), PartialDate.createFrom(new LocalDate(2001, 4, 23)))
.getDuration();
entity.setTimeQuantity(duration);
getSession().save(entity);
getSession().flush();
getSession().evict(entity);
EntityContainingTimeQuantity reloaded = (EntityContainingTimeQuantity) getSession()
.get(
EntityContainingTimeQuantity.class, entity.getId());
TimeQuantity timeQuantity = reloaded.getTimeQuantity();
assertThat(timeQuantity, equalTo(duration));
}
}

View file

@ -0,0 +1,33 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping default-access="field">
<class name="org.navalplanner.business.common.test.partialtime.EntityContainingPartialDate">
<id name="id">
<generator class="native"/>
</id>
<version name="version" type="long"/>
<property name="partialDate" type="org.navalplanner.business.common.partialtime.hibernate.PartialDateType">
<column name="normalizedInstant"></column>
<column name="granularity"></column>
</property>
</class>
<class name="org.navalplanner.business.common.test.partialtime.EntityContainingIntervalOfPartialDates">
<id name="id">
<generator class="native"/>
</id>
<version name="version" type="long"/>
<property name="interval" type="org.navalplanner.business.common.partialtime.hibernate.IntervalOfPartialDatesType">
<column name="start_instant"></column>
<column name="start_granularity"></column>
<column name="end_instant"></column>
<column name="end_granularity"></column>
</property>
</class>
<class name="org.navalplanner.business.common.test.partialtime.EntityContainingTimeQuantity">
<id name="id">
<generator class="native"/>
</id>
<version name="version" type="long"/>
<property name="timeQuantity" type="org.navalplanner.business.common.partialtime.hibernate.TimeQuantityType"></property>
</class>
</hibernate-mapping>

View file

@ -32,6 +32,9 @@
<value> <value>
org/navalplanner/business/orders/entities/Orders.hbm.xml org/navalplanner/business/orders/entities/Orders.hbm.xml
</value> </value>
<value>
TestEntities.hbm.xml
</value>
</list> </list>
</property> </property>