首页 > 解决方案 > 带有转换器问题的休眠 UserCollectionType

问题描述

如果我们为@ElementCollection 使用Converter,其中collection 是默认列表、集合或映射,那么这将按预期工作。

public abstract class MyEntity {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private long id;

    @ElementCollection(targetClass = State.class, fetch = FetchType.EAGER)
    @CollectionTable(name = "states")
    @Column(name = "state")
    @Access(value = AccessType.PROPERTY)
    @Convert(converter = StateAttributeConverter.class)
    private Set<State> states;
}

public enum State {
    FIRST(10), SECOND(5), THIRD(2);

    private final int value;

    public static State ofValue(int value) {
        for(State state : values()) {
            if(State.getValue() == value) {
                return state;
            }
        }
        throw new IllegalArgumentException("");
    }

    State(int value) {
        this.value = value;
    }

    public int getValue() {
        return value;
    }
}


@Converter
public class StateAttributeConverter implements AttributeConverter<State, Integer> {
    private static final State[] allStates = State.values();
        
    @Override
    public Integer convertToDatabaseColumn(State state) {
        return state.getValue();
    }
    
    @Override
    public State convertToEntityAttribute(Integer integer) {
        return State.ofValue(integer);
    }
}

在表中,我观察到值 10、5 和 2(根据需要)

但是,如果我实现自定义状态集合并为其实现org.hibernate.UserCollectionType,那么转换器将被忽略。对于标准集合(List 或 Set),使用了类似的实现(org.hibernate.SetTypeorg.hibernate.ListType)并且在它们中转换成功。

我的收藏和 UserCollectionType 实现:

public class States implements Set<State> {
    private EnumSet<State> states;

    public static States full() {
        return new States(Set.of(State.values()));
    }

    public static States empty() {
        return new States(new HashSet<>());
    }

    public States(Set<State> states) {
        this.states = states.isEmpty() ? EnumSet.noneOf(State.class) : EnumSet.copyOf(states);
    }

    States() { // For JPA
        this.states = EnumSet.noneOf(State.class);
    }

    @Override
    public Iterator<State> iterator() {
        return this.states.iterator();
    }

    @Override
    public int size() {
        return this.states.size();
    }

    /* set methods */
}

public class StatesType implements UserCollectionType {
    @Override
    public PersistentCollection instantiate(SharedSessionContractImplementor session, CollectionPersister persister) throws HibernateException {
        return new PersistentSet(session);
    }

    @Override
    public PersistentCollection wrap(SharedSessionContractImplementor session, Object set) {
        return new PersistentSet(session, (Set)set);
    }

    @Override
    public Iterator getElementsIterator(Object collection) {
        return ((States)collection).iterator();
    }

    @Override
    public boolean contains(Object collection, Object entity) {
        return ((States)collection).contains((State)entity);
    }

    @Override
    public Object indexOf(Object collection, Object entity) {
        throw new UnsupportedOperationException("");
    }

    @Override
    public Object replaceElements(Object original, Object target, CollectionPersister persister, Object owner, Map copyCache, SharedSessionContractImplementor session) throws HibernateException {
        States result = (States)target;
        result.clear();
        result.addAll((States)original);
        return result;
    }

    @Override
    public Object instantiate(int i) {
        return States.empty();
    }
}

在这种情况下,转换器将被忽略,并且在表中我看到了 State (Enum) 的ordinal()的值- 0、1 和 2。

@ElementCollection(targetClass = State.class, fetch = FetchType.EAGER)
@CollectionTable(name = "states")
@Column(name = "state")
@Access(value = AccessType.PROPERTY)
@CollectionType(type = "org.my.package.StatesType")
@Convert(converter = StateAttributeConverter.class)
private States states;

如何让UserCollectionType使用转换器?

标签: javahibernatejpa

解决方案


推荐阅读