web-dev-qa-db-fra.com

Mappage d'énumération à une table avec annotation de mise en veille prolongée

J'ai une table DEAL et une table DEAL_TYPE. Je voudrais mapper ce code:

public class Deal {
   DealType type;
}

public enum DealType {
   BASE("Base"), EXTRA("Extra");
}

Le problème est que les données existent déjà dans la base de données. Et j'ai du mal à mapper les classes à la base de données.

La base de données ressemble à ça:

   TABLE DEAL {
      Long id;
      Long typeId;
   }

   TABLE DEAL_TYPE {
       Long id;
       String text;
   }

Je sais que je pourrais utiliser une simple relation @OneToMany d'un type de transaction à l'autre, mais je préférerais utiliser une énumération. Est-ce possible?

Je l'ai presque fait fonctionner en utilisant un type EnumType.ORDINAL. Mais malheureusement, mes identifiants dans ma table de type d'accord ne sont pas séquentiels et ne commencent pas à 1.

Aucune suggestion?

28

Hibernate est un peu terrible chez Enums. C'est un échec étrange d'un ORM par ailleurs assez bon. Le moyen le plus "simple" de le contourner est de déclarer votre Enum un type d'hibernation personnalisé. Heureusement, Hibernate a écrit un exemple d'implémentation que vous pouvez intégrer textuellement dans votre application:

http://www.hibernate.org/265.html

Ils incluent même des instructions sur la façon de l'utiliser. C'est le modèle que j'utilise chaque fois que je me retrouve avec le besoin de persister les énumérations.

17

J'ai créé une classe similaire à celle suggérée par hibernate uniquement qui est configurable et il n'est pas nécessaire de créer un nouveau type uniquement pour cette persistance.

Peut être utilisé comme

@Type(type = "ro.raisercostin.hibernate.EnumUserType", parameters = @Parameter(name = "type", value = "DealType"))
DealType dealType;

J'ai ajouté une implémentation de ParameterizedType pour prendre en charge le paramètre passé.

public class EnumUserType implements UserType, ParameterizedType {

    private static final int[] SQL_TYPES = { Types.VARCHAR };
    private Class clazz = null;

    public EnumUserType() {
    }

    @Override
    public void setParameterValues(Properties parameters) {
        String className = (String) parameters.get("type");
        try {
            this.clazz = Class.forName(className);
        } catch (ClassNotFoundException e) {
            throw new RuntimeException("Couldn't get the class for name [" + className + "].", e);
        }
    }

    public int[] sqlTypes() {
        return SQL_TYPES;
    }

    public Class returnedClass() {
        return clazz;
    }

    public Object nullSafeGet(ResultSet resultSet, String[] names, Object owner) throws HibernateException,
            SQLException {
        String name = resultSet.getString(names[0]);
        Object result = null;
        if (!resultSet.wasNull()) {
            result = Enum.valueOf(clazz, name);
        }
        return result;
    }

    public void nullSafeSet(PreparedStatement preparedStatement, Object value, int index) throws HibernateException,
            SQLException {
        if (null == value) {
            preparedStatement.setNull(index, Types.VARCHAR);
        } else {
            preparedStatement.setString(index, ((Enum) value).name());
        }
    }

    public Object deepCopy(Object value) throws HibernateException {
        return value;
    }

    public boolean isMutable() {
        return false;
    }

    public Object assemble(Serializable cached, Object owner) throws HibernateException {
        return cached;
    }

    public Serializable disassemble(Object value) throws HibernateException {
        return (Serializable) value;
    }

    public Object replace(Object original, Object target, Object owner) throws HibernateException {
        return original;
    }

    public int hashCode(Object x) throws HibernateException {
        return x.hashCode();
    }

    public boolean equals(Object x, Object y) throws HibernateException {
        if (x == y) {
            return true;
        }
        if ((null == x) || (null == y)) {
            return false;
        }
        return x.equals(y);
    }
}
14
raisercostin

Vous pouvez annoter l'énumération avec @Entity et utilisez un tuplizer custoumn pour créer les instances de l'énumération avec Enum.valueOf

La déclaration enum ressemble alors à:

@Entity
@Table(name = "node_interface_type")
@Tuplizer(impl = EnumTuplizer.class)
public enum Type {
    WIRED, WIRELESS, WIRELESS_SENSOR_NODE;
    @Id
    public String name = toString();
}

Et le Tuplizer c'est:

public class EnumTuplizer extends PojoEntityTuplizer {
    public EnumTuplizer(EntityMetamodel entityMetamodel, PersistentClass mappedEntity) {
        super(entityMetamodel, mappedEntity);
    }

    @Override
    protected Instantiator buildInstantiator(final PersistentClass persistentClass) {
        return new Instantiator() {
            @Override
            public Object instantiate(Serializable id) {
                try {
                    return Enum.valueOf(
                            (Class) persistentClass.getClass().getClassLoader().loadClass(persistentClass.getClassName()),
                            (String) id
                    );
                } catch (ClassNotFoundException e) {
                    throw new AssertionError(e);
                }
            }

            @Override
            public Object instantiate() {
                throw new UnsupportedOperationException();
            }

            @Override
            public boolean isInstance(Object object) {
                throw new UnsupportedOperationException();
            }
        };
    }
}
7
don_jones

Si vous souhaitez utiliser l'entité en lecture seule, vous pouvez utiliser @Formula et @Enumerated. Essayez quelque chose comme:

@Entity
public class Deal {
   @Formula("(select text from DEAL_TYPE dt where dt.id = typeId)")
   @Enumerated(EnumType.STRING)
   DealType type;
}
3
xmedeko

Bien que loin d'être idéal, ma solution à ce problème était d'utiliser EnumStringType et une vue modifiable dénormalisée.

1
Jeff Mc