Java JPA具有额外枚举列的多对多关系
我正在尝试将一个实体持久化,该实体具有要枚举到Google App Engine数据存储中的对象映射。实体类使用JPA进行注释 事件类 我还没有在Java JPA具有额外枚举列的多对多关系,java,google-app-engine,jpa,enums,Java,Google App Engine,Jpa,Enums,我正在尝试将一个实体持久化,该实体具有要枚举到Google App Engine数据存储中的对象映射。实体类使用JPA进行注释 事件类 我还没有在User类中定义对此地图的任何引用。这是一种单向关系 用户类 Event.addresses列看起来相当棘手。所以我进行了测试,检查是否一切正常。嗯,事实并非如此。我尝试将事件实体保存到数据存储时遇到异常: java.lang.IllegalArgumentException: addressees: Response is not a support
User
类中定义对此地图的任何引用。这是一种单向关系
用户类
Event.addresses
列看起来相当棘手。所以我进行了测试,检查是否一切正常。嗯,事实并非如此。我尝试将事件
实体保存到数据存储时遇到异常:
java.lang.IllegalArgumentException: addressees: Response is not a supported property type.
at com.google.appengine.api.datastore.DataTypeUtils.checkSupportedSingleValue(DataTypeUtils.java:235)
at com.google.appengine.api.datastore.DataTypeUtils.checkSupportedValue(DataTypeUtils.java:199)
at com.google.appengine.api.datastore.DataTypeUtils.checkSupportedValue(DataTypeUtils.java:173)
at com.google.appengine.api.datastore.DataTypeUtils.checkSupportedValue(DataTypeUtils.java:148)
at com.google.appengine.api.datastore.PropertyContainer.setProperty(PropertyContainer.java:101)
根据DataNucleus,默认情况下,Enum是一种持久数据类型。因此,我不明白为什么会收到这样的错误消息:“响应不是受支持的属性类型”我怀疑问题出在用户类上。也许仅仅从事件到用户的关联是不够的,用户也应该有一个到事件的关联。因此,我向用户添加了
事件
字段,如下所示:
@Unowned
@ElementCollection
@CollectionTable(name = "user_event_responses")
@ManyToMany(mappedBy="addressees", targetEntity = Event.class)
@MapKeyJoinColumn
@Enumerated
@Column(name = "response")
private Map<Event, Response> events;
@无主
@元素集合
@CollectionTable(name=“用户\事件\响应”)
@ManyToMany(mappedBy=“addressees”,targetEntity=Event.class)
@MapKeyJoinColumn
@列举
@列(name=“response”)
私人地图活动;
反正也没用。然后我读了类似的问题,没有找到快速的答案。请给我看一个DataNucleus/JPA中多对多关系与额外列的示例 创建两个具有多对多关系但关系联接表具有附加数据的类的问题是一个常见的问题 我在WikiBooks上找到了关于这个话题的好例子,在Giovanni Gargiulo的文章中也找到了。我在官方文件中找到的参考资料已经很久很久了:和 在这种情况下,最好的解决方案是创建一个对联接表建模的类 因此将创建一个
EventUserResponse
类。它将有一个多对一事件和用户,以及一个用于附加数据的属性。事件和用户对EventUserResponse
将有一对多的响应。不幸的是,我没有设法为这个类映射复合主键。DataNucleus增强器拒绝增强没有主键的实体类。所以我使用了一个简单的自动生成ID
结果应该是以下是资料来源: EventUserAssociation类 如果Lombok注释(例如,
@noargsconstuctor
)对您来说似乎不熟悉,您可能需要查看。把我们从样板代码中解救出来是件好事
事件类
emf服务类
用法示例:
EntityManager em = EMFService.getFactory().createEntityManager();
EntityTransaction tx = em.getTransaction();
tx.begin();
try {
User fromContact = User.find(fromId, em);
Event event = Event.builder()
/* attributes initialization */
.build();
em.persist(event);
User toUser = User.find(toId, em);
event.addAddressee(toUser, Response.UNDEFINED);
tx.commit();
} finally {
if (tx.isActive()) tx.rollback();
em.close();
}
应允许跨组事务处理,以使其工作()。将以下属性添加到persistence.xml
:
<property name="datanucleus.appengine.datastoreEnableXGTransactions" value="true" />
最后,关于问题中的代码,AppEngine中不允许有名为key
的主键
@Unowned
@ElementCollection
@CollectionTable(name = "user_event_responses")
@ManyToMany(mappedBy="addressees", targetEntity = Event.class)
@MapKeyJoinColumn
@Enumerated
@Column(name = "response")
private Map<Event, Response> events;
@Entity
@Table(name = "event_user_response")
@NoArgsConstructor
@AllArgsConstructor
@Getter @Setter
@EqualsAndHashCode(callSuper = true, exclude = {"attendee", "event"})
public class EventUserAssociation extends AbstractEntity {
@Unowned
@ManyToOne
@PrimaryKeyJoinColumn(name = "eventId", referencedColumnName = "_id")
private Event event;
@Unowned
@ManyToOne
@PrimaryKeyJoinColumn(name = "attendeeId", referencedColumnName = "_id")
private User attendee;
@Enumerated
private Response response;
}
@Entity
@Builder
@EqualsAndHashCode(callSuper = false)
public @Data class Event extends AbstractEntity {
/* attributes are omitted */
// all addressees are initially present in this map with response set to UNDEFINED
// if user has received and read notification, than the response is updated to YES, NO, or MAYBE
@Singular
@Setter(AccessLevel.PRIVATE)
@OneToMany(mappedBy="event", cascade = CascadeType.ALL)
private List<EventUserAssociation> addressees = new ArrayList<>();
/**
* Add an addressee to the event.
* Create an association object for the relationship and set its data.
*
* @param addressee a user to whom this event notification is addressed
* @param response his response.
*/
public boolean addAddressee(User addressee, Response response) {
EventUserAssociation association = new EventUserAssociation(this, addressee, response);
// Add the association object to this event
return this.addressees.add(association) &&
// Also add the association object to the addressee.
addressee.getEvents().add(association);
}
public List<User> getAddressees() {
List<User> result = new ArrayList<>();
for (EventUserAssociation association : addressees)
result.add(association.getAttendee());
return result;
}
}
@Entity
@NoArgsConstructor
@RequiredArgsConstructor
@Getter @Setter
public class User extends AbstractEntity {
/* non-significant attributes are omitted */
@Setter(AccessLevel.PRIVATE)
@Unowned
@OneToMany(mappedBy="attendee", cascade = CascadeType.ALL)
private List<EventUserAssociation> events = new ArrayList<>();
public static User find(String attribute, EntityManager em) {
/* implementation omitted */
}
}
@MappedSuperclass
@NoArgsConstructor
@EqualsAndHashCode
public abstract class AbstractEntity {
@Id
@Column(name = "_id")
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Getter
protected Key id;
}
public abstract class EMFService {
@Getter
private static final EntityManagerFactory emfInstance = Persistence.createEntityManagerFactory("transactions-optional");
}
EntityManager em = EMFService.getFactory().createEntityManager();
EntityTransaction tx = em.getTransaction();
tx.begin();
try {
User fromContact = User.find(fromId, em);
Event event = Event.builder()
/* attributes initialization */
.build();
em.persist(event);
User toUser = User.find(toId, em);
event.addAddressee(toUser, Response.UNDEFINED);
tx.commit();
} finally {
if (tx.isActive()) tx.rollback();
em.close();
}
<property name="datanucleus.appengine.datastoreEnableXGTransactions" value="true" />