Hibernate 如何将基本体的ArrayList映射到单个列?

Hibernate 如何将基本体的ArrayList映射到单个列?,hibernate,many-to-many,one-to-many,Hibernate,Many To Many,One To Many,假设我有以下情况: 目标车有一个价格列表,都是数字。在Hibernate中是否可以将所有价格保存在一列中?我知道这违反了第一个正常形式,但可能有一些情况下,您不希望将它们保存在单独的表中,就像经典的一对多或多对多关系中那样 在JDO中,我可以通过将ArrayList保存在BLOB列中来轻松实现这一点 一些有用的相关SOF问题:和 任何想法都将受到高度赞赏 AFAIR,Hibernate将使用本机序列化并将结果字节存储在列中。但我不会这样做,而是使用专用转换,使价格在数据库中可读,并且至少能够在不

假设我有以下情况:

目标车有一个价格列表,都是数字。在Hibernate中是否可以将所有价格保存在一列中?我知道这违反了第一个正常形式,但可能有一些情况下,您不希望将它们保存在单独的表中,就像经典的一对多或多对多关系中那样

在JDO中,我可以通过将ArrayList保存在BLOB列中来轻松实现这一点

一些有用的相关SOF问题:和


任何想法都将受到高度赞赏

AFAIR,Hibernate将使用本机序列化并将结果字节存储在列中。但我不会这样做,而是使用专用转换,使价格在数据库中可读,并且至少能够在不需要Java本机序列化的情况下使用数据:

@Basic
private String prices;

public void setPrices(List<Integer> prices) {
    this.prices = Joiner.on(',').join(prices);
}

public List<Integer> getPrices() {
    List<Integer> result = new ArrayList<Integer>();
    for (String s : Splitter.on(',').split(this.prices)) {
        result.add(Integer.valueOf(s));
    }
    return result;
}
@Basic
私人字符串价格;
公共价格(标价){
this.prices=Joiner.on(',').join(prices);
}
公开价目表{
列表结果=新建ArrayList();
for(字符串s:Splitter.on(',').split(this.prices)){
结果.add(整数.valueOf(s));
}
返回结果;
}

您可以将自己的自定义类型实现为数组:

另外,找到一些实现也不难,其中一些甚至可以让您在HQL where子句中比较这些数组


我个人从未想过我会尝试这样的事情。但现在我很好奇。

我知道这是一个老问题,但对于任何试图在JPA环境下这样做的人来说,你可以这样做

import org.apache.commons.lang3.StringUtils;
import javax.persistence.AttributeConverter;
import javax.persistence.Converter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import java.util.Collections;

@Converter
public class IntArrayToStringConverter implements AttributeConverter<List<Integer>,String>{
    @Override
    public String convertToDatabaseColumn(List<Integer> attribute) {
        return attribute == null ? null : StringUtils.join(attribute,",");
    }

    @Override
    public List<Integer> convertToEntityAttribute(String dbData) {
        if (StringUtils.isBlank(dbData))
            return Collections.emptyList();

        try (Stream<String> stream = Arrays.stream(dbData.split(","))) {
            return stream.map(Integer::parseInt).collect(Collectors.toList());
        }
    }
}
import org.apache.commons.lang3.StringUtils;
导入javax.persistence.AttributeConverter;
导入javax.persistence.Converter;
导入java.util.ArrayList;
导入java.util.array;
导入java.util.List;
导入java.util.stream.collector;
导入java.util.stream.stream;
导入java.util.Collections;
@转换器
公共类IntArrayToStringConverter实现AttributeConverter{
@凌驾
公共字符串convertToDatabaseColumn(列表属性){
返回属性==null?null:StringUtils.join(属性,“”,“”);
}
@凌驾
公共列表convertToEntityAttribute(字符串dbData){
if(StringUtils.isBlank(dbData))
返回集合。emptyList();
try(Stream=Arrays.Stream(dbData.split(“,”)){
return stream.map(Integer::parseInt).collect(Collectors.toList());
}
}
}
然后在你的实体中使用类似的东西

@Entity
public class SomeEntity
{

   @Id
   @GeneratedValue(strategy = GenerationType.IDENTITY)
   private Integer id;

   @Column
   @Convert(converter = IntArrayToStringConverter.class)
   private List<Integer> integers;

   ...
}
@实体
公共类实体
{
@身份证
@GeneratedValue(策略=GenerationType.IDENTITY)
私有整数id;
@纵队
@Convert(converter=IntArrayToStringConverter.class)
私有列表整数;
...
}

我更新了Chris的代码,将Jackson JSON库用于列表和地图:

import java.util.List;

import javax.persistence.AttributeConverter;
import javax.persistence.Converter;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;

@Converter
public class ListToJsonConverter<T> implements AttributeConverter<List<T>, String> {

    private static ObjectMapper mapper = new ObjectMapper();

    @Override
    public String convertToDatabaseColumn(List<T> attribute) {
        if (attribute == null) {
            return null;
        }
        try {
            return mapper.writeValueAsString(attribute);
        } catch (JsonProcessingException e) {
            e.printStackTrace();
        }
        return null;
    }

    @SuppressWarnings("unchecked")
    @Override
    public List<T> convertToEntityAttribute(String dbData) {
        if (dbData == null || dbData.isEmpty()) {
            return null;
        }
        try {
            return mapper.readValue(dbData, List.class);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}
import java.util.List;
导入javax.persistence.AttributeConverter;
导入javax.persistence.Converter;
导入com.fasterxml.jackson.core.JsonProcessingException;
导入com.fasterxml.jackson.databind.ObjectMapper;
@转换器
公共类ListToJsonConverter实现AttributeConverter{
私有静态ObjectMapper mapper=新ObjectMapper();
@凌驾
公共字符串convertToDatabaseColumn(列表属性){
if(属性==null){
返回null;
}
试一试{
返回mapper.writeValueAsString(属性);
}捕获(JsonProcessingException e){
e、 printStackTrace();
}
返回null;
}
@抑制警告(“未选中”)
@凌驾
公共列表convertToEntityAttribute(字符串dbData){
if(dbData==null | | dbData.isEmpty()){
返回null;
}
试一试{
返回mapper.readValue(dbData,List.class);
}捕获(例外e){
抛出新的运行时异常(e);
}
}
}
import java.util.Map;
导入javax.persistence.AttributeConverter;
导入javax.persistence.Converter;
导入com.fasterxml.jackson.core.JsonProcessingException;
导入com.fasterxml.jackson.databind.ObjectMapper;
@转换器
公共类MapToJsonConverter实现AttributeConverter{
私有静态ObjectMapper mapper=新ObjectMapper();
@凌驾
公共字符串convertToDatabaseColumn(映射属性){
if(属性==null){
返回null;
}
试一试{
返回mapper.writeValueAsString(属性);
}捕获(JsonProcessingException e){
e、 printStackTrace();
}
返回null;
}
@抑制警告(“未选中”)
@凌驾
公共映射convertToEntityAttribute(字符串dbData){
if(dbData==null | | dbData.isEmpty()){
返回null;
}
试一试{
返回mapper.readValue(dbData,Map.class);
}捕获(例外e){
抛出新的运行时异常(e);
}
}
}
@列(name=“示例列表”)
@Convert(converter=ListToJsonConverter.class)
公共列表getExampleList(){
返回示例列表;
}
@列(name=“example\u map”)
@Convert(converter=MapToJsonConverter.class)
公共地图getExampleMap(){
返回示例图;
}

这允许以人类可读的方式在字符串列中存储几乎任何类型,这样就不需要为每种类型的list或hashmap使用单独的类。它还会自动转义字符串。

正确。那是我在想的另一件事。把它们写成逗号分隔的值之类的,但这看起来像是一种黑客行为。与本机序列化相关,这是我尝试过的,hibernate抱怨说,因为它无法将列表映射到列类型。我会继续尝试,看看会有什么结果。非常感谢你的回答。非常有趣。我试试看。非常感谢。很好的解决方案,但请注意,这只在JPA2.1之后才可用。在我的例子中,我得到一个警告:“找不到匹配的类型descr
import java.util.Map;

import javax.persistence.AttributeConverter;
import javax.persistence.Converter;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;

@Converter
public class MapToJsonConverter<T, K> implements AttributeConverter<Map<T, K>, String> {

    private static ObjectMapper mapper = new ObjectMapper();

    @Override
    public String convertToDatabaseColumn(Map<T, K> attribute) {
        if (attribute == null) {
            return null;
        }
        try {
            return mapper.writeValueAsString(attribute);
        } catch (JsonProcessingException e) {
            e.printStackTrace();
        }
        return null;
    }

    @SuppressWarnings("unchecked")
    @Override
    public Map<T, K> convertToEntityAttribute(String dbData) {
        if (dbData == null || dbData.isEmpty()) {
            return null;
        }
        try {
            return mapper.readValue(dbData, Map.class);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}
@Column(name = "example_list")
@Convert(converter = ListToJsonConverter.class)
public List<String> getExampleList() {
    return exampleList;
}

@Column(name = "example_map")
@Convert(converter = MapToJsonConverter.class)
public Map<String, String> getExampleMap() {
    return exampleMap;
}