具有manyToOne关系的休眠自定义类型

时间:2020-11-09 16:04:46

标签: java hibernate

我想要拥有的东西: 类别地址,代表全球范围内的地址,我也确实具有“国家/地区”实体,我想在此使用它作为关系,以减轻我指定的“到某个国家/地区的地址”的风险(是否值得缓解?)

我的一般问题,有可能吗?

顺便说一句,我在Java 11上使用了Hibernate 5.4.6

我们有一个简单的类:

public class Address {

    private Country country;
    private String city;
    private String street;
    private String number;
    private String zipCode;

    public Address(
            final Country country,
            final String city,
            final String street,
            final String number,
            final String zipCode
    ) {
        this.country = country;
        this.street = street;
        this.number = number;
        this.city = city;
        this.zipCode = zipCode;
    }

    protected Address() {
    }

    public String getFormatted() {
        return String.format("%s %s, %s %s, %s", street, number, city, zipCode, country);
    }
}

, 国家也很简单:

@Entity
@Table(name = "countries")
public class Country {

    @Column
    @Id
    @NotNull
    private String nameIso;

    @Column
    @NotNull
    private String name;

    @Column
    @NotNull
    private boolean isAllowed = false;

    protected Country() {
    }

    public Country(final String nameIso, final String name) {
        this.nameIso = nameIso;
        this.name = name;
    }

    public String getNameIso() {
         return nameIso;
    }
}

以及使用此类的示例实体:

@Entity
@Table(name = "delivery_address")
public class DeliveryAddress {

    @Id
    @Column
    @NotNull
    @Type(type = UUID)
    private java.util.UUID id;

    @ManyToOne(fetch = LAZY)
    private User user;

    @Column
    @NotNull
    private Address address;

    protected DeliveryAddress() {
    }

    public DeliveryAddress(User user, Address address) {
        this.id = java.util.UUID.randomUUID();
        this.user = user;
        this.address = address;
    }

    public Country getCountry() {
        return this.address.getCountry();
    }
}

我设法告诉休眠,通过不赞成使用的TypeFactory(ouch)将有很多类型, 这就是我最终得到的:

public class AddressType implements CompositeUserType {

    public static final String NAME = "address";

    private static final int COUNTRY_POSITION = 1;
    private static final int CITY_POSITION = 2;
    private static final int STREET_POSITION = 3;
    private static final int NUMBER_POSITION = 4;
    private static final int ZIP_POSITION = 5;

    private static final int COUNTRY_INDEX = COUNTRY_POSITION - 1;
    private static final int CITY_INDEX = CITY_POSITION - 1;
    private static final int STREET_INDEX = STREET_POSITION - 1;
    private static final int NUMBER_INDEX = NUMBER_POSITION - 1;
    private static final int ZIP_INDEX = ZIP_POSITION - 1;

    @Override
    public String[] getPropertyNames() {
        return new String[]{"country", "city", "street", "number", "zipCode"};
    }

    @Override
    public Type[] getPropertyTypes() {
        return new Type[]{
                new TypeConfiguration().getTypeResolver().getTypeFactory().manyToOne(Country.class.getCanonicalName()),
                StandardBasicTypes.STRING,
                StandardBasicTypes.STRING,
                StandardBasicTypes.STRING,
                StandardBasicTypes.STRING
        };
    }

    @Override
    public Object getPropertyValue(final Object component, final int property) throws HibernateException {
        if (component == null) {
            return null;
        }

        String propertyValue;

        switch (property) {
            case COUNTRY_POSITION:
                propertyValue = ((Address) component).getCountry().getNameIso();
                break;
            case CITY_POSITION:
                propertyValue = ((Address) component).getCity();
                break;
            case STREET_POSITION:
                propertyValue = ((Address) component).getStreet();
                break;
            case NUMBER_POSITION:
                propertyValue = ((Address) component).getNumber();
                break;
            case ZIP_POSITION:
                propertyValue = ((Address) component).getZipCode();
                break;
            default:
                propertyValue = null;
        }

        return propertyValue;
    }


    @Override
    public Class returnedClass() {
        return Address.class;
    }

    @Override
    public Object nullSafeGet(final ResultSet rs,
                              final String[] names,
                              final SharedSessionContractImplementor session,
                              final Object owner) throws HibernateException, SQLException {
        String country = rs.getString(names[COUNTRY_INDEX]);
        String city = rs.getString(names[CITY_INDEX]);
        String street = rs.getString(names[STREET_INDEX]);
        String number = rs.getString(names[NUMBER_INDEX]);
        String zipCode = rs.getString(names[ZIP_INDEX]);

        if (country != null && city != null && street != null && number != null && zipCode != null) {

/* 
********
* How to obtain instance of Country at this spot ?
*******
*/
            return new Address(country, city, street, number, zipCode);
        }

        return null;
    }

}

那么有可能在我的CompositeType中建立关系,还是应该停止尝试并只使用我的地址类中的任何国家/地区iso代码?

谢谢!

1 个答案:

答案 0 :(得分:0)

在JPA / Hibernate中,如果您控制代码,则此类类型最好表示为可嵌入对象。如果您无法更改类型的源代码,则复合/自定义类型的API更合适。

我建议您创建一个可嵌入存储ISO代码的可嵌入对象,如下所示:

@Embeddable
public class Country {
    private String iso;
}

请记住,如果您重复使用此类型,则可能必须在使用站点上通过@AttributeOverride更改列名称。