简体   繁体   English

Spring Data findOne() NullPointerException

[英]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 .我想我错过了一些核心概念,因为我遇到了几个问题,但让我们从这个开始:当具有SubscriptionUser持久保存在数据库中并且我尝试使用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 ,一切顺利,它创建了UserSubscriptionCommunity ,我可以在数据库中看到它们,一切都很好。 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

How do you retrieve nested Sets? 你如何检索嵌套的集合?

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM