Rax*_*kin 6 javascript java hibernate thymeleaf
我有 2 个类,其中一个属性与 OneToMany/ManyToOne 关系映射。问题是,当我执行选择并传递给 te 视图时,我想使用 Thymeleaf 将对象解析为 javascript,但它会无限循环导致这种关系。我的班级: 班级球员:
@Entity
@Table(name = "player")
public class Player {
@Id
@Column(name = "id")
@GeneratedValue
private int id;
@Column(name = "level")
private int level;
@Column(name = "experience")
private int experience;
@OneToMany(mappedBy="player", cascade = CascadeType.ALL)
private List<InventoryItem> inventory;
// Constructor
public Player() {
}
// Getters & Setters...
}
Run Code Online (Sandbox Code Playgroud)
类库存项目:
@Entity
@Table(name = "inventory_item")
public class InventoryItem {
@Id
@GeneratedValue
@Column(name = "id")
private int id;
@ManyToOne
@JoinColumn(name="id_player")
private Player player;
public InventoryItem() {
}
//Getters and Setters...
}
Run Code Online (Sandbox Code Playgroud)
然后我将 Player 对象传递给视图并使用 javascript 将它的 console.log 记录下来:
<script th:inline="javascript">
/*<![CDATA[*/
console.log([[${player}]]);
/*]]>*/
</script>
Run Code Online (Sandbox Code Playgroud)
这是错误:

解析为javascript时如何防止双向关系,例如忽略Player所有的atrubute InventoryItems?
I faced this problem today and I guess Thymeleaf does not offer an easy solution for that. You can always set the references to the parent to null before passing it to Thymeleaf, but that seems kind of ugly.
Investigating Thymeleaf's source code, I noticed that it uses Introspector to get information about the properties, so we can hide some properties by implementing a BeanInfo. The easiest way to do that is to create a class on the same package of your bean, with BeanInfo appended to the name. You can extend SimpleBeanInfo and implement only the methods that interest you (in this case getPropertyDescriptors).
In your example, you could do:
public class InventoryItemBeanInfo extends SimpleBeanInfo {
@Override
public PropertyDescriptor[] getPropertyDescriptors() {
try {
PropertyDescriptor id = new PropertyDescriptor("id", InventoryItem.class);
PropertyDescriptor[] descriptors = {id};
return descriptors;
} catch (IntrospectionException e) {
throw new Error(e.toString());
}
}
}
Run Code Online (Sandbox Code Playgroud)
This way, Thymeleaf wouldn't see your Player property and you wouldn't have the infinite recursion anymore.
The downside of this solution is that you have to remember that everytime you change the attributes of your InventoryItem bean you have to go to your BeanInfo and add the new attribute to it.
So I came up with another solution. It's a little bit more complicated, but once it's set up it's easier to maintain.
We start by creating an annotation to indicate that the property should be hidden. Let's call it HiddenProperty:
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface HiddenProperty {}
Run Code Online (Sandbox Code Playgroud)
Now we implement a generic BeanInfo class that will inspect a bean using reflection and find the properties. We will also check for the existence of our annotation and when it's present we ignore the method:
public class HiddenPropertyAwareBeanInfo extends SimpleBeanInfo {
private Class<?> beanClass;
private PropertyDescriptor[] descriptors;
public HiddenPropertyAwareBeanInfo(Class<?> beanClass) {
this.beanClass = beanClass;
}
@Override
public PropertyDescriptor[] getPropertyDescriptors() {
if(descriptors != null) {
return descriptors;
}
Method[] methodList = beanClass.getMethods();
List<PropertyDescriptor> propDescriptors = new ArrayList<>();
for(Method m : methodList) {
if(Modifier.isStatic(m.getModifiers())) {
continue;
}
try {
if(m.getParameterCount() == 0 && !m.isAnnotationPresent(HiddenProperty.class)) {
if(m.getName().startsWith("get")) {
propDescriptors.add(new PropertyDescriptor(m.getName().substring(3), beanClass));
} else if(m.getName().startsWith("is")) {
propDescriptors.add(new PropertyDescriptor(m.getName().substring(2), beanClass));
}
}
} catch(IntrospectionException ex) {
continue;
}
}
descriptors = new PropertyDescriptor[propDescriptors.size()];
return propDescriptors.toArray(descriptors);
}
}
Run Code Online (Sandbox Code Playgroud)
Now, create your BeanInfo that extends this class:
public class InventoryItemBeanInfo extends HiddenPropertyAwareBeanInfo {
public InventoryItemBeanInfo() {
super(InventoryItem.class);
}
}
Run Code Online (Sandbox Code Playgroud)
Finally, annotate the getters of the properties you want to hide:
@Entity
@Table(name = "inventory_item")
public class InventoryItem {
@Id
@GeneratedValue
@Column(name = "id")
private int id;
@ManyToOne
@JoinColumn(name="id_player")
private Player player;
public InventoryItem() {
}
@HiddenProperty
public Player getPlayer() {
return this.player;
}
//Other getters and setters...
}
Run Code Online (Sandbox Code Playgroud)
And there you have it. Now you can change your properties as much as you want and your Player property will not be seen by Thymeleaf.
One important thing to say about this solution is that it is not 100% compliant with the Java Beans spec, it doesn't consider the existence of indexed properties and some other details. You can take a look at the Introspector's source code to see how they extract this information and improve this solution.
But, personally, I think this should be fixed inside Thymeleaf. Thymeleaf is an incredibly elegant tool and having to resort to that kind of workaround makes me kind of sad.
| 归档时间: |
|
| 查看次数: |
856 次 |
| 最近记录: |