How to map an ArrayList of primitives to a single column?

前端 未结 4 2040
没有蜡笔的小新
没有蜡笔的小新 2020-12-31 11:02

Let\'s say I have the following situation:

Object Car has an ArrayList of prices, which are all numbers. Is it possible in Hibernate to save all the prices in a sin

相关标签:
4条回答
  • 2020-12-31 11:38

    AFAIR, Hibernate will use native serialization and store the resulting bytes in your column. I wouldn't do that though, and use a dedicated transformation that would make the prices readable in the database, and at least be able to use the data without needing Java native serialization:

    @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;
    }
    
    0 讨论(0)
  • 2020-12-31 11:42

    I updated Chris's code to utilize the Jackson JSON library for lists and maps:

    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;
    }
    

    This allows storing just about any types in a human-readable way in a string column, and makes it so you don't need a separate class for every type of list or hashmap. It also automatically escapes the strings.

    0 讨论(0)
  • 2020-12-31 11:44

    You can implement your own custom type as an array:

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

    Also, it is not that hard to find some implementations, some of them going as far to let you compare those arrays in a HQL where clause.

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

    I personally never thought i would try something like this. But now I am very curious.

    0 讨论(0)
  • 2020-12-31 11:46

    I know this is an old question but for anyone trying to do this in a JPA context you can do this

    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());
            }
        }
    }
    

    Then to use it something like this in your entity

    @Entity
    public class SomeEntity
    {
    
       @Id
       @GeneratedValue(strategy = GenerationType.IDENTITY)
       private Integer id;
    
       @Column
       @Convert(converter = IntArrayToStringConverter.class)
       private List<Integer> integers;
    
       ...
    }
    
    0 讨论(0)
提交回复
热议问题