[英]Spring Data findOne() NullPointerException
I think I'm missing some core concept, because I encounter several problems, but let's start with this one: when a User
with a Subscription
is persisted in database and I try to get it using findOne(id)
, I get NullPointerException
.我想我错过了一些核心概念,因为我遇到了几个问题,但让我们从这个开始:当具有
Subscription
的User
持久保存在数据库中并且我尝试使用findOne(id)
获取它时,我得到NullPointerException
。 I tried to debug deep inside generated code and it appears that for some reason hashCode()
of Subscription
object is called, which also for unclear reason has only an id
set and all other properties are null
, but because they (probably) take part in the hashCode()
method by calling their own hashCode()
, I get this exception.我试图深入调试生成的代码,似乎由于某种原因调用了
Subscription
对象的hashCode()
,这也出于不清楚的原因只有一个id
集,所有其他属性都为null
,但因为它们(可能)参与hashCode()
方法通过调用他们自己的hashCode()
,我得到这个异常。
So basically what I want is user be a part of many communities, in each of them he can create a subscription to their content.所以基本上我想要的是用户成为许多社区的一部分,在每个社区中他都可以创建对他们内容的订阅。 When I first call to
SubscriptionController
, everything goes fine and it creates User
, Subscription
and Community
, I can see them in database, all good.当我第一次调用
SubscriptionController
,一切顺利,它创建了User
、 Subscription
和Community
,我可以在数据库中看到它们,一切都很好。 But then when I call UserRepository.findOne()
, which is CrudRepository
, inside UserSerivce
- I get the exception.但后来当我打电话
UserRepository.findOne()
这是CrudRepository
,里面UserSerivce
-我得到的异常。
I've been trying to figure this out for two weeks and no luck, so I really hope someone can spend some time helping me with this.我一直在努力解决这个问题两周,但没有运气,所以我真的希望有人能花一些时间来帮助我解决这个问题。 Below are classes:
下面是类:
User:用户:
@Entity
@Data
@NoArgsConstructor
public class User {
@Column(nullable = false)
@Id
private Integer id;
@OneToMany(mappedBy = "user", fetch = FetchType.EAGER, cascade = CascadeType.ALL)
@JsonIgnore
Set<Subscription> subscriptions;
@OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
@JoinTable(
joinColumns = {@JoinColumn(name = "user_id", referencedColumnName = "id")},
inverseJoinColumns = {@JoinColumn(name = "payment_id", referencedColumnName = "id", unique = true)}
)
@JsonIgnore
Set<Payment> payments;
public User(Integer userId) {
this.id = userId;
}
}
Subscription:订阅:
@Entity
@Data
@NoArgsConstructor
public class Subscription {
@Column
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@JsonIgnore
private Integer id;
@ManyToOne(cascade = {CascadeType.MERGE, CascadeType.REFRESH})
@JoinColumn(name = "user_id", nullable = false)
private User user;
@ManyToOne(cascade = {CascadeType.MERGE, CascadeType.REFRESH})
@JoinColumn(name = "community_id", nullable = false)
private Community community;
@Column(nullable = false)
private Boolean isActive;
@Column(nullable = false)
private Date endDate;
@OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
@JoinTable(
joinColumns = {@JoinColumn(name = "subscription_id", referencedColumnName = "id")},
inverseJoinColumns = {@JoinColumn(name = "payment_id", referencedColumnName = "id", unique = true)}
)
private Set<Payment> payments;
public Subscription(User user, Community community, Boolean isActive) {
this.user = user;
this.community = community;
this.isActive = isActive;
this.endDate = new Date();
}
}
Community:社区:
@Data
@Entity
@NoArgsConstructor
public class Community {
@Column(nullable = false)
@Id
private Integer id;
@OneToMany(mappedBy = "community", fetch = FetchType.LAZY, cascade = {CascadeType.MERGE, CascadeType.REFRESH})
@JsonIgnore
private Set<Subscription> subscriptions;
public Community(Integer communityId) {
this.id = communityId;
}
}
I also have services for each of them:我也为他们每个人提供服务:
UserService:用户服务:
@Service
public class UserService implements IService<User> {
@Autowired
private UserRepository userRepository;
@Transactional
public User get(@NotNull Integer userId) {
User user = userRepository.findOne(userId);
if (user == null)
return userRepository.save(new User(userId));
return user;
}
@Override
public User save(@Valid User user) {
return userRepository.save(user);
}
}
SubscriptionService:订阅服务:
@Service
public class SubscriptionService implements IService<Subscription> {
@Autowired
SubscriptionRepository subscriptionRepository;
@Autowired
PaymentRepository paymentRepository;
@Override
public Subscription get(@NotNull Integer id) {
return subscriptionRepository.findOne(id);
}
public Subscription getByUserAndCommunity(@Valid User user, @Valid Community community) {
Subscription subscription = subscriptionRepository.findByUserAndCommunity(user, community);
if (subscription != null)
return subscription;
subscription = new Subscription(user, community, false);
return subscriptionRepository.save(subscription);
}
@Transactional
public Subscription activate(@Valid Subscription subscription, @Valid Payment payment, @Future Date endDate) {
paymentRepository.save(payment);
Set<Payment> payments = subscription.getPayments();
if (payments == null)
payments = new HashSet<>();
payments.add(payment);
subscription.setEndDate(endDate);
subscription.setIsActive(true);
return subscriptionRepository.save(subscription);
}
@Override
public Subscription save(@Valid Subscription e) {
return subscriptionRepository.save(e);
}
}
And CommunityService:和社区服务:
@Service
public class CommunityService implements IService<Community> {
@Autowired
private CommunityRepository communityRepository;
@Override
@Transactional
public Community get(@NotNull Integer id) {
Community community = communityRepository.findOne(id);
if (community == null)
return communityRepository.save(new Community(id));
return community;
}
@Override
public Community save(@Valid Community community) {
return communityRepository.save(community);
}
}
Controller:控制器:
@RestController
public class SubscriptionController {
@Autowired
private SubscriptionService subscriptionService;
@Autowired
private CommunityService communityService;
@Autowired
private PaymentService paymentService;
@PostMapping("/subscribe")
public ResponseEntity<Subscription> subscribe(@RequestParam("communityId") Integer communityId, @RequestBody @Valid Payment payment) {
if(!paymentService.checkPayment(payment))
return ResponseEntity
.status(HttpStatus.BAD_REQUEST)
.body(null);
VkAuthentication vkAuthentication = (VkAuthentication) SecurityContextHolder.getContext().getAuthentication();
User user = vkAuthentication.getUser();
Community community = communityService.get(communityId);
Subscription subscription = subscriptionService.getByUserAndCommunity(user, community);
Calendar calendar = Calendar.getInstance();
Date newEndDate = DateUtils.addDays(new Date(), calendar.getActualMaximum(Calendar.DAY_OF_MONTH));
subscription = subscriptionService.activate(subscription, payment, newEndDate);
return ResponseEntity
.status(HttpStatus.OK)
.body(subscription);
}
}
And here's some stack trace:这是一些堆栈跟踪:
java.lang.NullPointerException: null
at org.hibernate.engine.internal.StatefulPersistenceContext.getLoadedCollectionOwnerOrNull(StatefulPersistenceContext.java:786) ~[hibernate-core-5.0.12.Final.jar:5.0.12.Final]
at org.hibernate.event.spi.AbstractCollectionEvent.getLoadedOwnerOrNull(AbstractCollectionEvent.java:58) ~[hibernate-core-5.0.12.Final.jar:5.0.12.Final]
at org.hibernate.event.spi.InitializeCollectionEvent.<init>(InitializeCollectionEvent.java:22) ~[hibernate-core-5.0.12.Final.jar:5.0.12.Final]
at org.hibernate.internal.SessionImpl.initializeCollection(SessionImpl.java:1989) ~[hibernate-core-5.0.12.Final.jar:5.0.12.Final]
at org.hibernate.collection.internal.AbstractPersistentCollection$4.doWork(AbstractPersistentCollection.java:570) ~[hibernate-core-5.0.12.Final.jar:5.0.12.Final]
at org.hibernate.collection.internal.AbstractPersistentCollection.withTemporarySessionIfNeeded(AbstractPersistentCollection.java:252) ~[hibernate-core-5.0.12.Final.jar:5.0.12.Final]
at org.hibernate.collection.internal.AbstractPersistentCollection.initialize(AbstractPersistentCollection.java:566) ~[hibernate-core-5.0.12.Final.jar:5.0.12.Final]
at org.hibernate.collection.internal.AbstractPersistentCollection.read(AbstractPersistentCollection.java:135) ~[hibernate-core-5.0.12.Final.jar:5.0.12.Final]
at org.hibernate.collection.internal.PersistentSet.hashCode(PersistentSet.java:430) ~[hibernate-core-5.0.12.Final.jar:5.0.12.Final]
at zhiyest.subscriptionsbackend.domain.User.hashCode(User.java:14) ~[classes/:na]
at zhiyest.subscriptionsbackend.domain.Subscription.hashCode(Subscription.java:15) ~[classes/:na]
at java.util.HashMap.hash(HashMap.java:338) ~[na:1.8.0_111]
at java.util.HashMap.put(HashMap.java:611) ~[na:1.8.0_111]
at java.util.HashSet.add(HashSet.java:219) ~[na:1.8.0_111]
at java.util.AbstractCollection.addAll(AbstractCollection.java:344) ~[na:1.8.0_111]
at org.hibernate.collection.internal.PersistentSet.endRead(PersistentSet.java:327) ~[hibernate-core-5.0.12.Final.jar:5.0.12.Final]
at org.hibernate.engine.loading.internal.CollectionLoadContext.endLoadingCollection(CollectionLoadContext.java:234) ~[hibernate-core-5.0.12.Final.jar:5.0.12.Final]
at org.hibernate.engine.loading.internal.CollectionLoadContext.endLoadingCollections(CollectionLoadContext.java:221) ~[hibernate-core-5.0.12.Final.jar:5.0.12.Final]
at org.hibernate.engine.loading.internal.CollectionLoadContext.endLoadingCollections(CollectionLoadContext.java:194) ~[hibernate-core-5.0.12.Final.jar:5.0.12.Final]
at org.hibernate.loader.plan.exec.process.internal.CollectionReferenceInitializerImpl.endLoading(CollectionReferenceInitializerImpl.java:154) ~[hibernate-core-5.0.12.Final.jar:5.0.12.Final]
at org.hibernate.loader.plan.exec.process.internal.AbstractRowReader.finishLoadingCollections(AbstractRowReader.java:249) ~[hibernate-core-5.0.12.Final.jar:5.0.12.Final]
at ...
I don't even understand why does it call Subscription.hashCode()
when it's findOne()
for User
...我甚至不明白为什么它叫
Subscription.hashCode()
时,它的findOne()
的User
...
upd:更新:
at org.springframework.data.repository.core.support.SurroundingTransactionDetectorMethodInterceptor.invoke(SurroundingTransactionDetectorMethodInterceptor.java:57) ~[spring-data-commons-1.13.4.RELEASE.jar:na]
...
at zhiyest.subscriptionsbackend.logging.Logger.logAround(Logger.java:29) ~[classes/:na]
...
at zhiyest.subscriptionsbackend.services.UserService$$EnhancerBySpringCGLIB$$6e00bac4.get(<generated>) ~[classes/:na]
at zhiyest.subscriptionsbackend.security.VkAuthenticationProvider.authenticate(VkAuthenticationProvider.java:23) ~[classes/:na]
at zhiyest.subscriptionsbackend.security.VkAuthenticationProvider$$FastClassBySpringCGLIB$$24f3d662.invoke(<generated>) ~[classes/:na]
...
at zhiyest.subscriptionsbackend.security.VkAuthenticationProvider$$EnhancerBySpringCGLIB$$4d8d8001.authenticate(<generated>) ~[classes/:na]
at org.springframework.security.authentication.ProviderManager.authenticate(ProviderManager.java:174) ~[spring-security-core-4.2.3.RELEASE.jar:4.2.3.RELEASE]
at org.springframework.security.authentication.ProviderManager.authenticate(ProviderManager.java:199) ~[spring-security-core-4.2.3.RELEASE.jar:4.2.3.RELEASE]
at org.springframework.security.access.intercept.AbstractSecurityInterceptor.authenticateIfRequired(AbstractSecurityInterceptor.java:354) ~[spring-security-core-4.2.3.RELEASE.jar:4.2.3.RELEASE]
at org.springframework.security.access.intercept.AbstractSecurityInterceptor.beforeInvocation(AbstractSecurityInterceptor.java:229) ~[spring-security-core-4.2.3.RELEASE.jar:4.2.3.RELEASE]
...
I guess that problem is @Data
.我猜这个问题是
@Data
。
This lombok annotation is the cause of recursive dependencies ( toString()
and hashcode()
).这个 lombok 注释是递归依赖(
toString()
和hashcode()
)的原因。 Try to use @Getter
and @Setter
instead of @Data
.尝试使用
@Getter
和@Setter
而不是@Data
。
I hope it will help.我希望它会有所帮助。
This appears to be a bug in a certain version of Hibernate (see first post below).这似乎是某个版本的 Hibernate 中的一个错误(请参阅下面的第一篇文章)。 If you have a nested set of other Hibernate entities, it seems to have a problem accessing them inside of its hashCode() method.
如果您有一组嵌套的其他 Hibernate 实体,则在其 hashCode() 方法内部访问它们似乎有问题。 I confirmed this through debugging.
我通过调试确认了这一点。
You could manually generate a hash/code equals and remove the other entity.您可以手动生成哈希/代码等于并删除其他实体。 Or you could do something like this in your Lombok, suggested by a comment in the second post below:
或者你可以在你的 Lombok 中做这样的事情,下面第二篇文章中的评论建议:
@EqualsAndHashCode(exclude={"subscriptions"})
I say "something" because I haven't read your object graph closely enough to see which one you should exclude.我说“某事”是因为我没有足够仔细地阅读您的对象图,以了解您应该排除哪一个。 But it's an easy solution, and generally, it makes no sense to have subsidiary contents of an object be part of its logical identity.
但这是一个简单的解决方案,通常,将对象的附属内容作为其逻辑标识的一部分是没有意义的。
Hibernate 4.2.20 Object HashCode Method Causing NullPointerException Hibernate 4.2.20 对象 HashCode 方法导致 NullPointerException
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.