Задача
Есть Java-приложение, имеющее внутри большое количество ORM-сущностей (Entity).
Необходимо реализовать сущность ExtendedAttributes, которую можно прикрепить к любой другой сущности без дополнительной доработки.
Решение
На помощь к нам приходит CompositeUserType, который содержит внутри себя class и id той сущности, которую мы хотим привязать. Вот и всё решение. А дальше — код.
Для сокращения поста импорты, геттеры, сеттеры и методы «по-умолчанию» из классов вырезаны.
ExtendedAttributes.class — именно его мы и реализуем в рамках данной задачи
@Entity @Table(name = "extattributes") @TypeDef(name = "entityType", typeClass = hibernate.GenericEntityType.class) public class ExtendedAttributes extends GenericEntity { private static final long serialVersionUID = 1L; @Id private Long id; @Type(type = "entityType") @Columns(columns = { @Column(name = "object_id"), @Column(name = "object_class") }) private GenericEntity object; private String property; private String value; @Override public Class<?> getType() { return ExtendedAttributes.class; } @Override public Long getId() { return id; } }
GenericEntity.class — его наследуют все сущности в данном приложении.
public abstract class GenericEntity implements Serializable { private static final long serialVersionUID = 1L; public abstract Serializable getId(); public abstract Class<?> getType(); }
EntityWrapper.class — обертка для несуществующих сущностей.
public class EntityWrapper extends GenericEntity { private static final long serialVersionUID = 1L; private String name; public EntityWrapper(String name) { super(); this.name = name; } @Override public Serializable getId() { return name; } @Override public Class<?> getType() { return EntityWrapper.class; } }
GenericEntityType.class — а вот и тот самый тип. Состоит из двух String-полей, возвращает GenericEntity.
determineIdType() — единственный настоящий костыль, но чем заменить я пока не нашел.
Id других типов в приложении нет.
public class GenericEntityType implements CompositeUserType { private static final Type[] SQL_TYPES = { StringType.INSTANCE, StringType.INSTANCE }; private static final String[] SQL_NAMES = { "id", "class" }; @Override public String[] getPropertyNames() { return SQL_NAMES; } @Override public Type[] getPropertyTypes() { return SQL_TYPES; } @Override public Class<?> returnedClass() { return GenericEntity.class; } @Override public boolean equals(Object o1, Object o2) throws HibernateException { if (o1 == o2) return true; if (o1 != null && o2 != null) return o1.equals(o2); return false; } @Override public Object getPropertyValue(Object object, int index) throws HibernateException { GenericEntity dto = (GenericEntity) object; if (index == 0) { return dto.getId(); } else if (index == 1) { return dto.getType(); } else { throw new HibernateException("Unknown index [ " + index + " ]"); } } @Override public int hashCode(Object object) throws HibernateException { return (object != null) ? object.hashCode() : 0; } @Override public boolean isMutable() { return false; } @Override public Object nullSafeGet(ResultSet rs, String[] names, SessionImplementor session, Object owner) throws HibernateException, SQLException { if (names.length == 2) { String id = (String) StringType.INSTANCE.get(rs, names[0], session); String clazz = (String) StringType.INSTANCE.get(rs, names[1], session); if (id != null && clazz != null) { try { Class.forName(clazz); return session.immediateLoad(clazz, determineIdType(id)); } catch (ClassNotFoundException e) { return new EntityWrapper(id); } } } return null; } @Override public void nullSafeSet(PreparedStatement st, Object value, int index, SessionImplementor session) throws HibernateException, SQLException { if (value == null) { StringType.INSTANCE.set(st, null, index, session); ClassType.INSTANCE.set(st, null, index + 1, session); } else { final GenericEntity dto = (GenericEntity) value; StringType.INSTANCE.set(st, dto.getId().toString(), index, session); ClassType.INSTANCE.set(st, dto.getType(), index + 1, session); } } /** * Автоопределение типа. Long/String/Double * * @param id * @return */ private Serializable determineIdType(String id) { try { if (id.matches("^\\d+$")) { return Long.valueOf(id); } else if (id.matches("^\\d+[\\.,]\\d+$")) { return Double.valueOf(id); } else { return id; } } catch (NumberFormatException e) { return id; } }
А теперь — как получить:
public static Criterion criterionForEntity(String field, GenericEntity object) { String id = ""; if (object.getId() != null) { id = object.getId().toString(); } return Restrictions.and(Restrictions.eq(field + ".id", id), Restrictions.eq(field + ".class", object.getType() .getCanonicalName())); }
Спасибо за внимание. Надеюсь, это вам пригодится 🙂
ссылка на оригинал статьи http://habrahabr.ru/post/158547/
Добавить комментарий