11

假设我有以下情况:

Object Car 有一个价格的 ArrayList,它们都是数字。在 Hibernate 中是否可以将所有价格保存在单个列中?我知道这违反了第一范式,但在某些情况下,您可能不希望将它们保存在单独的表中,就像传统上在一对多或多对多关系中所做的那样。

在 JDO 中,我可以通过将 ArrayList 保存在 BLOB 列中轻松地做到这一点。

一些有用的相关 SOF 问题:Hibernate 中原始类型的ArrayList和Map ArrayList with Hibernate

任何想法将不胜感激!

4

4 回答 4

24

我知道这是一个老问题,但对于任何试图在 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());
        }
    }
}

然后在你的实体中使用类似这样的东西

@Entity
public class SomeEntity
{

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

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

   ...
}
于 2016-03-15T18:28:06.960 回答
7

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;
}
于 2013-05-04T11:46:17.667 回答
1

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

http://docs.jboss.org/hibernate/core/3.6/reference/en-US/html/types.html#types-custom

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

https://forum.hibernate.org/viewtopic.php?t=946973

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

于 2013-05-07T22:31:23.397 回答
1

我更新了 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.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;
}

这允许以人类可读的方式在字符串列中存储几乎任何类型,并使得您不需要为每种类型的列表或哈希图创建一个单独的类。它还会自动转义字符串。

于 2020-12-28T15:49:47.370 回答