Итак, я потратил некоторое время на эту проблему и нашел решение. Это не красиво, но, по крайней мере, отправная точка - возможно, кто-то дополнит это некоторыми полезными комментариями.
Некоторая информация о отображении, которую я нашел в процессе:
Класс, который содержит базовое отображение типов Hibernate на типы свойств: org.hibernate.type.TypeFactory. Все эти отображения хранятся в неизменяемой карте
private static final Map BASIC_TYPES;
...
basics.put( java.util.Date.class.getName(), Hibernate.TIMESTAMP );
...
BASIC_TYPES = Collections.unmodifiableMap( basics );
Как вы можете видеть с типом java.util.Date, связанным с типом Hibernate org.hibernate.type.TimestampType
Следующий интересный момент - создание Hibernate org.hibernate.cfg.Configuration - объекта, который содержит всю информацию о сопоставленных классах. Эти классы и их свойства могут быть извлечены следующим образом:
Iterator clsMappings = cfg.getClassMappings();
while(clsMappings.hasNext()){
PersistentClass mapping = (PersistentClass) clsMappings.next();
handleProperties(mapping.getPropertyIterator(), map);
}
Подавляющее большинство свойств являются объектами типов org.hibernate.mapping.SimpleValue. Наш интерес представляет метод SimpleValue.getType () - в этом методе определяется, какой тип будет использоваться для преобразования значений свойств туда-обратно при работе с БД
Type result = TypeFactory.heuristicType(typeName, typeParameters);
На данный момент я понимаю, что я не могу изменить BASIC_TYPES - единственный способ - заменить объект SimpleValue на свойства типов java.util.Date для моего пользовательского объекта, который сможет знать точный тип для преобразования. .
Решение:
Создание пользовательской фабрики диспетчера сущностей контейнера путем расширения класса HibernatePersistence и переопределения его метода createContainerEntityManagerFactory:
public class HibernatePersistenceExtensions extends HibernatePersistence {
@Override
public EntityManagerFactory createContainerEntityManagerFactory(PersistenceUnitInfo info, Map map) {
if ("true".equals(map.get("hibernate.use.custom.entity.manager.factory"))) {
return CustomeEntityManagerFactoryFactory.createCustomEntityManagerFactory(info, map);
} else {
return super.createContainerEntityManagerFactory(info, map);
}
}
}
Создание объекта конфигурации Hibernate, изменение значений объектов для свойств java.util.Date, а затем создание настраиваемой фабрики диспетчера сущностей.
public class ReattachingEntityManagerFactoryFactory {
@SuppressWarnings("rawtypes")
public static EntityManagerFactory createContainerEntityManagerFactory(
PersistenceUnitInfo info, Map map) {
Ejb3Configuration cfg = new Ejb3Configuration();
Ejb3Configuration configured = cfg.configure( info, map );
handleClassMappings(cfg, map);
return configured != null ? configured.buildEntityManagerFactory() : null;
}
@SuppressWarnings("rawtypes")
private static void handleClassMappings(Ejb3Configuration cfg, Map map) {
Iterator clsMappings = cfg.getClassMappings();
while(clsMappings.hasNext()){
PersistentClass mapping = (PersistentClass) clsMappings.next();
handleProperties(mapping.getPropertyIterator(), map);
}
}
private static void handleProperties(Iterator props, Map map) {
while(props.hasNext()){
Property prop = (Property) props.next();
Value value = prop.getValue();
if (value instanceof Component) {
Component c = (Component) value;
handleProperties(c.getPropertyIterator(), map);
} else {
handleReturnUtilDateInsteadOfTimestamp(prop, map);
}
}
private static void handleReturnUtilDateInsteadOfTimestamp(Property prop, Map map) {
if ("true".equals(map.get("hibernate.return.date.instead.of.timestamp"))) {
Value value = prop.getValue();
if (value instanceof SimpleValue) {
SimpleValue simpleValue = (SimpleValue) value;
String typeName = simpleValue.getTypeName();
if ("java.util.Date".equals(typeName)) {
UtilDateSimpleValue udsv = new UtilDateSimpleValue(simpleValue);
prop.setValue(udsv);
}
}
}
}
}
Как вы можете видеть, я просто перебираю каждое свойство и заменяю SimpleValue-object на UtilDateSimpleValue для свойств типа java.util.Date. Это очень простой класс - он реализует тот же интерфейс, что и объект SimpleValue, например, org.hibernate.mapping.KeyValue. В конструктор передается оригинальный объект SimpleValue - поэтому каждый вызов UtilDateSimpleValue перенаправляется на исходный объект с одним исключением - метод getType (...) возвращает мой пользовательский тип.
public class UtilDateSimpleValue implements KeyValue{
private SimpleValue value;
public UtilDateSimpleValue(SimpleValue value) {
this.value = value;
}
public SimpleValue getValue() {
return value;
}
@Override
public int getColumnSpan() {
return value.getColumnSpan();
}
...
@Override
public Type getType() throws MappingException {
final String typeName = value.getTypeName();
if (typeName == null) {
throw new MappingException("No type name");
}
Type result = new UtilDateUserType();
return result;
}
...
}
И последний шаг - реализация UtilDateUserType. Я просто расширяю оригинальный org.hibernate.type.TimestampType и переопределяю его метод get () следующим образом:
public class UtilDateUserType extends TimestampType{
@Override
public Object get(ResultSet rs, String name) throws SQLException {
Timestamp ts = rs.getTimestamp(name);
Date result = null;
if(ts != null){
result = new Date(ts.getTime());
}
return result;
}
}
Вот и все. Немного хитро, но теперь каждое свойство java.util.Date возвращается как java.util.Date без каких-либо дополнительных модификаций существующего кода (аннотации или изменение сеттеров). Как я выяснил в Hibernate 4 или выше, есть гораздо более простой способ заменить ваш собственный тип (подробности см. Здесь: Hibernate TypeResolver ). Любые предложения или критика приветствуются.