Java Spring: 枚举(Enum)在(LinkedHash)Map 中的键不是有序的。

huangapple go评论76阅读模式
英文:

Java Spring: Enum keys in (LinkedHash)Map are not ordered

问题

我的实体`Mealplan`具有一个`mealsPerWeek`属性其类型为LinkedHashMap

@Entity
public class Mealplan {

@Id
private int id;

@ManyToMany
private Map<Weekday, Meal> mealsPerWeek = new LinkedHashMap<>();

public Map<Weekday, Meal> getMealsPerWeek() {
    return mealsPerWeek;
}

}

映射属性的键是`Weekday`,它是一个枚举

public enum Weekday {
    Monday, Tuesday, Wednesday, Thursday, Friday
}

现在我期望在我的 API 中餐点按照枚举的正确顺序显示但实际上显示的顺序相当随机

{
id: 1,
mealsPerWeek: {
  Tuesday: {
   id: 3,
   name: "Salad",
   price: 3,
   type: "vegan"
},
  Monday: {
   id: 4,
   name: "Lentil Soup",
   price: 23.5,
   type: "vegan"
},
...

如何在我的 REST API 中对其进行排序以便按照正确顺序显示键

编辑我通过`data.sql`在每次应用程序启动时插入对象并意识到每次顺序都不同

源代码https://gitlab.com/joshua.olberg/java-spring-backend
英文:

My entity Mealplan has an mealsPerWeek property as a LinkedHashMap.

@Entity
public class Mealplan {

@Id
private int id;

@ManyToMany
private Map&lt;Weekday, Meal&gt; mealsPerWeek = new LinkedHashMap&lt;&gt;();

public Map&lt;Weekday, Meal&gt; getMealsPerWeek() {
    return mealsPerWeek;
}

}

The key of the map property is Weekdayand is an enumeration:

public enum Wochentag {
    Monday, Tuesday, Wednesday, Thursday, Friday
}

Now I expect that in my API the Meals get displayed in the correct order of the enum. But it is displayed pretty random:

{
id: 1,
mealsPerWeek: {
  Tuesday: {
   id: 3,
   name: &quot;Salad&quot;,
   preis: 3,
   art: &quot;vegan&quot;
},
  Monday: {
   id: 4,
   name: &quot;Linsensuppe&quot;,
   preis: 23.5,
   art: &quot;vegan&quot;
},

How do I order it in my REST API so it displays the keys in its correct order?

Edit: I insert objects via data.sql on every application startup and realised, that everytime the order is differnet.

Source Code: https://gitlab.com/joshua.olberg/java-spring-backend

答案1

得分: 3

问题在于在从数据库加载地图(以及通常情况下加载所有集合)时,Hibernate使用自己的集合类型。

Hibernate使用了自己的集合实现,这些实现具有惰性加载、缓存或状态更改检测语义。因此,持久化集合必须声明为接口类型。实际接口可能是java.util.Collection、java.util.List、java.util.Set、java.util.Map、java.util.SortedSet、java.util.SortedMap,甚至其他对象类型(这意味着您需要编写org.hibernate.usertype.UserCollectionType的实现)。

正如下面的示例所示,重要的是要使用接口类型,而不是在实体映射中声明的集合实现。

示例1:Hibernate使用了自己的集合实现

@Entity(name = "Person")
public static class Person {
    @Id
    private Long id;

    @ElementCollection
    private List<String> phones = new ArrayList<>();

    public List<String> getPhones() {
        return phones;
    }
}

Person person = entityManager.find(Person.class, 1L);
// 抛出java.lang.ClassCastException:无法将org.hibernate.collection.internal.PersistentBag转换为java.util.ArrayList
ArrayList<String> phones = (ArrayList<String>) person.getPhones();

因此:

private Map<Weekday, Meal> mealsPerWeek = new LinkedHashMap<>();

将被PersistentMap替代。
这就是为什么使用EnumMap或TreeMap初始化Map会失败的原因。

您希望Hibernate使用PersistentSortedMap,为此:

  • 使用正确的接口SortedMap
  • 添加@SortNatural@SortComparator
英文:

The problem is that when loading the map (and generally, all collections) from the db, hibernate uses its own collection types.

https://docs.jboss.org/hibernate/orm/5.1/userguide/html_single/chapters/domain/collections.html

> Hibernate uses its own collection implementations which are enriched with lazy-loading, caching or state change detection semantics. For this reason, persistent collections must be declared as an interface type. The actual interface might be java.util.Collection, java.util.List, java.util.Set, java.util.Map, java.util.SortedSet, java.util.SortedMap or even other object types (meaning you will have to write an implementation of org.hibernate.usertype.UserCollectionType).

> As the following example demonstrates, it’s important to use the interface type and not the collection implementation, as declared in the entity mapping.

> Example 1. Hibernate uses its own collection implementations
>```
@Entity(name = "Person")
public static class Person {
@Id
private Long id;

> @ElementCollection
private List<String> phones = new ArrayList<>();

> public List<String> getPhones() {
return phones;
}
>}

> Person person = entityManager.find( Person.class, 1L );
//Throws java.lang.ClassCastException: org.hibernate.collection.internal.PersistentBag cannot be cast to java.util.ArrayList
ArrayList<String> phones = (ArrayList<String>) person.getPhones();
> ```

Therefore:

private Map&lt;Weekday, Meal&gt; mealsPerWeek = new LinkedHashMap&lt;&gt;();

will be replaced by PersistentMap.
That is why initializing a Map with EnumMap or TreeMap fails.

You want Hibernate to use PersistentSortedMap, and for that

  • use a correct interface SortedMap
  • add @SortNatural or @SortComparator

答案2

得分: 0

EnumMap可能对您有所帮助。迭代顺序基于枚举中声明的顺序 - https://docs.oracle.com/javase/7/docs/api/java/util/EnumMap.html

英文:

EnumMap may help you here. Iteration order is based on declared order in the Enum - https://docs.oracle.com/javase/7/docs/api/java/util/EnumMap.html

huangapple
  • 本文由 发表于 2020年4月9日 03:48:30
  • 转载请务必保留本文链接:https://go.coder-hub.com/61108878.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定