英文:
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<Weekday, Meal> mealsPerWeek = new LinkedHashMap<>();
public Map<Weekday, Meal> getMealsPerWeek() {
return mealsPerWeek;
}
}
The key of the map property is Weekday
and 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: "Salad",
preis: 3,
art: "vegan"
},
Monday: {
id: 4,
name: "Linsensuppe",
preis: 23.5,
art: "vegan"
},
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<Weekday, Meal> mealsPerWeek = new LinkedHashMap<>();
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
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论