我遇到了一个相当奇怪的问题。有一个实体 User 和这个类的对象需要通过多对多的关系相互连接。因此,除了主表“user”之外,还应该出现另外 2 个表“customer_authors”“author_customers”
@Entity
public class User {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
// ID пользователя
private Long userId;
// "customer_authors"
@ManyToMany(cascade=CascadeType.ALL)
@JoinTable(name = "customer_authors",
joinColumns = @JoinColumn(name = "customer_id"),
inverseJoinColumns = @JoinColumn(name = "author_id"))
private List<User> authorsList = new ArrayList<>();
// "author_customers"
@ManyToMany(cascade=CascadeType.ALL)
@JoinTable(name = "author_customers",
joinColumns = @JoinColumn(name = "author_id"),
inverseJoinColumns = @JoinColumn(name = "customer_id"))
private List<User> customersList = new ArrayList<>();
// getters, setters and other data.
}
在我的代码中没有任何地方使用数组 asList() / 创建了一个固定大小的列表。确切地说,根据谷歌搜索和stackoverflow,这是类似错误的原因......
就我而言,如果我在 @Service UserService 中使用类似的代码:
user.setUserEmail(userEmail);
user.setUserPassword(passwordEncoder.encode(userPassword));
userRepository.save(user);
User referrer = userRepository.findUserByUserId(referrerId);
referrer.getAuthorsList().add(user);
userRepository.save(referrer);
然后我得到相应的错误 -
UnsupportedOperationException
// excerpt
java.lang.UnsupportedOperationException: null
at com.sun.proxy.$Proxy142.save(Unknown Source) ~[na:na]
at info.md7.textpool.services.UserService.addUser(UserService.java:252) ~[classes/:na]
at info.md7.textpool.controllers.UserController.userRegistration(UserController.java:54) ~[classes/:na]
但同时,如果你在控制器中使用这样的东西进行测试:
User customer = (User) userService.findUserByEmail(currentUser.getUsername());
User author = userRepository.findByUserEmail("sukkivulmo@desoz.com");
customer.getAuthorsList().add(author);
userRepository.save(author);
然后确实将引用者 ID 和用户 ID 的必要信息添加到表中。
可能是什么错误?也许有人经历过这个并且知道如何解决它?先感谢您!
来自 UserService 的完整方法片段:
public boolean addUser( //todo добавить тип работы
String userFullname,
String userEmail,
String userPassword,
String prefLangs,
String prefCats,
Double paymentCost,
String paymentMethod,
String paymentWallet,
Long referrerId,
User user
) throws MessagingException {
User userFromDbEmail = userRepository.findByUserEmail(userEmail);
User referrer = userRepository.findUserByUserId(referrerId);
if(userFromDbEmail != null) {
return false;
}
/*
* Проверяем наличие данных в полях prefLang, prefCats, paymentCost, paymentMethod, paymentWallet.
* И если они имеются в одном из полей, то регистрируем пользователя, как Автора.
* В противном случае, создаем нового Заказчика.
*
*/
if(
prefLangs != null && !prefLangs.isEmpty() ||
prefCats != null && !prefCats.isEmpty() ||
paymentCost != null ||
paymentMethod != null && !paymentMethod.isEmpty() ||
paymentWallet != null && !paymentWallet.isEmpty()
) {
user.setUserFullname(userFullname);
user.setUserEmail(userEmail);
user.setUserPassword(passwordEncoder.encode(userPassword));
user.setRegDate(LocalDateTime.now());
user.setUserActive(false);
user.setRoles(Collections.singleton(Role.AUTHOR));
user.setActivationCode(UUID.randomUUID().toString());
user.setUserMode("author");
user.setReceiveEmails(true);
// Если пользователь был приглашен, то находим реферрера и добавлем в его список нового пользователя
User referrer = userRepository.findUserByUserId(referrerId);
user.setReferrerId(referrer);
userRepository.save(user);
/* Добавляем метаданные для юзера
С связи с особенностями верстки, данные скриптом вставляю в hidden input поле, после чего
получаю String разделенный запятыми и разобрав добавляю данные в user_meta
*/
assert prefLangs != null;
String[] prefLang = prefLangs.split(",");
for (String metaValue : prefLang) {
UserMeta userMeta = new UserMeta();
String metaKey = "prefLang";
userMeta.setMetaKey(metaKey);
userMeta.setMetaValue(metaValue);
userMeta.setUser(user);
userMetaRepository.save(userMeta);
}
assert prefCats != null;
String[] prefCat = prefCats.split(",");
for (String metaValue : prefCat) {
UserMeta userMeta = new UserMeta();
String metaKey = "prefCat";
userMeta.setMetaKey(metaKey);
userMeta.setMetaValue(metaValue);
userMeta.setUser(user);
userMetaRepository.save(userMeta);
}
UserMeta paymentMeta = new UserMeta();
paymentMeta.setMetaKey(paymentMethod);
paymentMeta.setMetaValue(paymentWallet);
paymentMeta.setUser(user);
userMetaRepository.save(paymentMeta);
if(paymentCost != null) {
UserMeta paymentCostMeta = new UserMeta();
paymentCostMeta.setMetaKey("paymentCost");
paymentCostMeta.setMetaValue(paymentCost.toString());
paymentCostMeta.setUser(user);
userMetaRepository.save(paymentCostMeta);
}
referrer.getAuthorsList().add(user);
userRepository.save(referrer);
} else {
user.setUserFullname(userFullname);
user.setUserEmail(userEmail);
user.setUserPassword(passwordEncoder.encode(userPassword));
user.setRegDate(LocalDateTime.now());
user.setUserActive(false);
user.setRoles(Collections.singleton(Role.CUSTOMER));
user.setActivationCode(UUID.randomUUID().toString());
user.setUserMode("customer");
user.setReceiveEmails(true);
// Если пользователь был приглашен, то находим реферрера и добавлем в его список нового пользователя
User referrer = userRepository.findUserByUserId(referrerId);
user.setReferrerId(referrer);
userRepository.save(user);
referrer.getCustomersList().add(user);
userRepository.save(referrer);
}
return true;
}
您需要查看Array list的方向,它返回
list,它Arrays.asList返回一个您无法添加元素的固定大小的列表。可以像这样创建一个可变列表UnsupportedOperationException 来自哪里?
Java 中的集合可以有可选的方法。
当调用此实现中未提供其实现的方法时,将抛出以下内容
UnsupportedOperationException:接口中此类方法的存在可能令人费解,但在这种情况下,据我了解,这是由于与旧版本 Java 的向后兼容性。
正如其他人所说,集合既可以是可变的,也可以是不可变的。然而,与普遍看法相反,在这种情况下,异常不是由于使用
Arrays.asList(作者明确地用新对象初始化字段ArrayList'а)。虽然它非常非常接近,显然有必要朝这个方向挖掘。
不幸的是,所有的注意力都集中在两个领域:
authorsList和customersList.有非常可疑的镜像链接,甚至有级联操作,在这种特殊情况下显然不能以那种形式应用。而且,删除时
cascade=CascadeType.ALL错误消失了。我们稍后会回到他们身边。错误更加平淡无奇:
作者显式地创建了一个单例(不可修改的集合)并将其传递给角色列表。
之后,这个对象就被成功保存了。
在我们将此用户添加到另一个用户的受邀客户端列表并保存之前使用 hibernate 获得的第二个用户(邀请者)之后,就会出现错误。
保存第二个用户及其与第一个用户的关系后,hibernate
cascade=CascadeType.ALL会查看customersList并开始更新给定列表中的第一个用户对象。覆盖在集合的字段中找到的所有字段 hibernate 清理然后再次检索其中的数据。所以它涉及roles一个单例的字段,它位于第一个用户中,而该用户又位于第二个用户的列表中。试图清除它并得到UnsupportedOperationException。要解决此问题,只需编写:
在这种情况下,
cascade=CascadeType.ALL这并不重要。但是,你不能就这样离开它。因为,在删除邀请用户时,所有作者和客户都会被自动删除,在我看来,这与作者的意图不符。
以上这两个字段(
authorsList和customersList)至少应该放老实说,这些字段是多余的,你可以去掉它们。
毕竟,对于每个受邀用户,我们都设置了referrer(受邀用户)。
因此,我们可以添加一个逆属性
invitedUsers并按角色过滤它。或者只是通过推荐人和角色选择用户。