java.lang.StackOverflowError: null [Spring Boot、Hibernate]

发布于 2025-01-10 03:42:44 字数 4457 浏览 0 评论 0原文

我有两个类 User.javaAddress.java ,它们之间存在一对一双向映射

但是当我尝试使用 User 类获取地址时,我收到 "java.lang.StackOverflowError: null" 异常。

当我尝试从 Address 类中获取 User 时,也会发生同样的情况。

User.java

@Entity
@Table(name = "user")
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private long id;
    
    private String name;
    private String email;
    private String phone;
    private String password;
    private String imageUrl;
    
    @OneToOne(cascade = CascadeType.ALL)
    @JoinColumn(name = "address")
    private Address address;

Address.java

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private long id;
    
    @OneToOne(cascade = CascadeType.ALL, mappedBy = "address")
    private User user;
    
    private String country;
    private String state;
    private String city;
    private String street;
    private String pincode;

MainController.java

@Controller
public class MainController {
    @Autowired
    private UserDao userDao;
    
    @Autowired
    private AddressDao addressDao;
    
    @RequestMapping("/test")
    @ResponseBody
    public String test() {
        User user = new User();
        user.setName("name");
        user.setEmail("email");
        user.setPhone("phone");
        user.setPassword("password");
        user.setImageUrl("imageUrl");
        
        Address address = new Address();
        address.setCountry("country");
        address.setState("state");
        address.setCity("city");
        address.setStreet("street");
        address.setPincode("123456");
        
        user.setAddress(address);
        userDao.save(user);
        
        return "working";
    }
    
    @RequestMapping("/fetch")
    @ResponseBody
    public String fetch() {
        User user = userDao.getById((long) 1);
        System.out.println(user.getAddress());
        
        return "working";
    }
}

我正在使用 test() 函数将数据放入数据库并且工作正常。 数据库图像

但是当我调用 fetch() 函数时,我是出现以下错误

java.lang.StackOverflowError: null
    at org.hibernate.proxy.pojo.BasicLazyInitializer.invoke(BasicLazyInitializer.java:58) ~[hibernate-core-5.6.5.Final.jar:5.6.5.Final]
    at org.hibernate.proxy.pojo.bytebuddy.ByteBuddyInterceptor.intercept(ByteBuddyInterceptor.java:43) ~[hibernate-core-5.6.5.Final.jar:5.6.5.Final]
    at 

更新了 MainController.java

package com.demo.controller;

import java.util.Optional;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import com.demo.dao.AddressDao;
import com.demo.dao.UserDao;
import com.demo.entity.Address;
import com.demo.entity.User;

@Controller
public class MainController {
    @Autowired
    private UserDao userDao;
    
    @Autowired
    private AddressDao addressDao;
    
    @RequestMapping("/test")
    @ResponseBody
    public String test() {
        User user = new User();
        user.setName("name");
        user.setEmail("email");
        user.setPhone("phone");
        user.setPassword("password");
        user.setImageUrl("imageUrl");
        
        userDao.save(user);
        
        Address address = new Address();
        address.setCountry("country");
        address.setState("state");
        address.setCity("city");
        address.setStreet("street");
        address.setPincode("123456");
        
        addressDao.save(address);
        
        user.setAddress(address);
        userDao.save(user);
        
        return "working";
    }
    
    @RequestMapping("/fetch")
    @ResponseBody
    public String fetch() {
        Optional<User> op = userDao.findById((long) 1);
        User user = op.get();
        
        // working
        System.out.println(user.getName() + " " + user.getEmail() + " " + user.getPhone());
    
        // java.lang.StackOverflowError:null
        System.out.println(user.getAddress());
        
        return "working";
    }
}

I have two classes User.java and Address.java and there is a one-to-one bi-directional mapping between them.

But when I try to get the address using the User class I get an "java.lang.StackOverflowError: null" exception.

The same thing happens when I try to get the User from the Address class.

User.java

@Entity
@Table(name = "user")
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private long id;
    
    private String name;
    private String email;
    private String phone;
    private String password;
    private String imageUrl;
    
    @OneToOne(cascade = CascadeType.ALL)
    @JoinColumn(name = "address")
    private Address address;

Address.java

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private long id;
    
    @OneToOne(cascade = CascadeType.ALL, mappedBy = "address")
    private User user;
    
    private String country;
    private String state;
    private String city;
    private String street;
    private String pincode;

MainController.java

@Controller
public class MainController {
    @Autowired
    private UserDao userDao;
    
    @Autowired
    private AddressDao addressDao;
    
    @RequestMapping("/test")
    @ResponseBody
    public String test() {
        User user = new User();
        user.setName("name");
        user.setEmail("email");
        user.setPhone("phone");
        user.setPassword("password");
        user.setImageUrl("imageUrl");
        
        Address address = new Address();
        address.setCountry("country");
        address.setState("state");
        address.setCity("city");
        address.setStreet("street");
        address.setPincode("123456");
        
        user.setAddress(address);
        userDao.save(user);
        
        return "working";
    }
    
    @RequestMapping("/fetch")
    @ResponseBody
    public String fetch() {
        User user = userDao.getById((long) 1);
        System.out.println(user.getAddress());
        
        return "working";
    }
}

I am using the test() function to put data in the database and it is working fine.
database image

But when I call the fetch() function I am getting the following error

java.lang.StackOverflowError: null
    at org.hibernate.proxy.pojo.BasicLazyInitializer.invoke(BasicLazyInitializer.java:58) ~[hibernate-core-5.6.5.Final.jar:5.6.5.Final]
    at org.hibernate.proxy.pojo.bytebuddy.ByteBuddyInterceptor.intercept(ByteBuddyInterceptor.java:43) ~[hibernate-core-5.6.5.Final.jar:5.6.5.Final]
    at 

Updated MainController.java

package com.demo.controller;

import java.util.Optional;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import com.demo.dao.AddressDao;
import com.demo.dao.UserDao;
import com.demo.entity.Address;
import com.demo.entity.User;

@Controller
public class MainController {
    @Autowired
    private UserDao userDao;
    
    @Autowired
    private AddressDao addressDao;
    
    @RequestMapping("/test")
    @ResponseBody
    public String test() {
        User user = new User();
        user.setName("name");
        user.setEmail("email");
        user.setPhone("phone");
        user.setPassword("password");
        user.setImageUrl("imageUrl");
        
        userDao.save(user);
        
        Address address = new Address();
        address.setCountry("country");
        address.setState("state");
        address.setCity("city");
        address.setStreet("street");
        address.setPincode("123456");
        
        addressDao.save(address);
        
        user.setAddress(address);
        userDao.save(user);
        
        return "working";
    }
    
    @RequestMapping("/fetch")
    @ResponseBody
    public String fetch() {
        Optional<User> op = userDao.findById((long) 1);
        User user = op.get();
        
        // working
        System.out.println(user.getName() + " " + user.getEmail() + " " + user.getPhone());
    
        // java.lang.StackOverflowError:null
        System.out.println(user.getAddress());
        
        return "working";
    }
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

滥情哥ㄟ 2025-01-17 03:42:44

TLDR:您实际上并没有在任何地方保存任何内容,但是很容易修复。这是我的代码和解释:

MainController.java:

@RestController
public class MainController {
    private final UserRepository userRepository;
    private final AddressRepository addressRepository;

    public MainController(UserRepository userRepository, AddressRepository addressRepository){
        this.userRepository = userRepository;
        this.addressRepository = addressRepository;
    }

    @GetMapping("/test")
    public String test() {
        User user = new User();
        user.setName("name");
        user.setEmail("email");
        user.setPhone("phone");
        user.setPassword("password");
        user.setImageUrl("imageUrl");

        user = userRepository.save(user);
        System.out.println("saved user");

        Address address = new Address();
        address.setCountry("country");
        address.setState("state");
        address.setCity("city");
        address.setStreet("street");
        address.setPincode("123456");

        address = addressRepository.save(address);
        System.out.println("saved address");

        user.setAddress(address);
        userRepository.save(user);

        System.out.println("set user's address");
        return "working";
    }

    @GetMapping("/fetch")
    public String fetch() {
        Optional<User> optionalUser = userRepository.findById((long) 1);
        if(optionalUser.isPresent()){
            User user = optionalUser.get();
            System.out.println(user.getAddress());

            boolean addressExists = addressRepository.existsById((long) 1);
            System.out.println(addressExists);
            System.out.println(user.getAddress().getCountry());

            return "working";
        }

        System.out.println("Error: user with id 1 not found!");
        return "failing";
    }
}

User.java:

@Entity
@Table(name = "user")
public class User {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private long id;

    private String name;
    private String email;
    private String phone;
    private String password;
    private String imageUrl;

    @OneToOne(cascade = CascadeType.ALL)
    @JoinColumn(name = "address_id", referencedColumnName = "id")
    private Address address;

    //getters and setters omitted for brevity
}

Address.java:

@Entity
@Table(name = "address")
public class Address {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private long id;

    @OneToOne(mappedBy = "address")
    private User user;

    private String country;
    private String state;
    private String city;
    private String street;
    private String pincode;
    
    //getters and setters omitted for brevity
}

AddressRepository.java:

public interface AddressRepository extends CrudRepository<Address, Long> {

}

UserRepository.java:

public interface UserRepository extends CrudRepository<User, Long> {

}

UserDAO.java:

public class UserDAO {
    private final String name;
    private final String email;
    private final String phone;
    private final String imageUrl;
    
    public UserDAO(User user) {
        name = user.getName();
        email = user.getEmail();
        phone = user.getPhone();
        imageUrl = user.getImageUrl();
    }

    public String getName() {
        return name;
    }

    public String getEmail() {
        return email;
    }

    public String getPhone() {
        return phone;
    }

    public String getImageUrl() {
        return imageUrl;
    }
}

A DAO 没有与数据库的连接,它的意图就是缩写所代表的意思,简单地说传输数据,仅此而已。创建存储库时,您可以通过将对象保存在存储库中来将其粘贴在那里。请注意,通过使用正确的泛型扩展 CrudRepository,您甚至不需要自己实现这些方法。 save 方法实际上保存了 POJO,并返回保存的版本,这就是我执行 user = userRepository.save(user) 的原因,这乍一看似乎违反直觉,但是它只是帮助确保一切都如您所愿。如果您随后想要发送 UserDAO 对象作为响应,您可以使用从数据库返回的 user 对象来创建它,可能类似于:

UserDAO dao = new UserDAO(userRepository.save(user));

请注意MainController 中的 test 方法内部发生了什么。首先,我们创建 POJO User 对象并设置其字段。然后我们必须将其保存到存储库中,只有在调用存储库的 save 方法后它才会被持久化。请注意,user 对象在使用 address 更新后会再次保存。

这是一种非常粗略的做事方式,最好创建一个服务层并使用 @Transactional 注释来执行此操作,这意味着如果内部出现问题,所有内容都会回滚。方法注释为@Transactional。

另外,使用 CascadeType.ALL 可能不是您想要的,请参阅此答案

fetch 方法中,我确保 user 确实存在,但无法保证。为了避免 500 错误,重要的是在某些事情不起作用时有一个后备机制。

作为最后的旁注,您不应该像这样存储原始密码,您至少应该使用盐和胡椒进行哈希处理,或者使用许多可用的库之一来实现此类功能(尽管这样做可能会非常有趣)代码本身很脏)。您还应该考虑当出现问题时您要透露多少信息,因为您不想泄露太多可用于对特定用户进行匿名化的信息,甚至不想了解有关您的代码和系统架构的更多信息。

TLDR: you aren't actually saving anything anywhere, but it's easy to fix. Here's my code and my explanation:

MainController.java:

@RestController
public class MainController {
    private final UserRepository userRepository;
    private final AddressRepository addressRepository;

    public MainController(UserRepository userRepository, AddressRepository addressRepository){
        this.userRepository = userRepository;
        this.addressRepository = addressRepository;
    }

    @GetMapping("/test")
    public String test() {
        User user = new User();
        user.setName("name");
        user.setEmail("email");
        user.setPhone("phone");
        user.setPassword("password");
        user.setImageUrl("imageUrl");

        user = userRepository.save(user);
        System.out.println("saved user");

        Address address = new Address();
        address.setCountry("country");
        address.setState("state");
        address.setCity("city");
        address.setStreet("street");
        address.setPincode("123456");

        address = addressRepository.save(address);
        System.out.println("saved address");

        user.setAddress(address);
        userRepository.save(user);

        System.out.println("set user's address");
        return "working";
    }

    @GetMapping("/fetch")
    public String fetch() {
        Optional<User> optionalUser = userRepository.findById((long) 1);
        if(optionalUser.isPresent()){
            User user = optionalUser.get();
            System.out.println(user.getAddress());

            boolean addressExists = addressRepository.existsById((long) 1);
            System.out.println(addressExists);
            System.out.println(user.getAddress().getCountry());

            return "working";
        }

        System.out.println("Error: user with id 1 not found!");
        return "failing";
    }
}

User.java:

@Entity
@Table(name = "user")
public class User {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private long id;

    private String name;
    private String email;
    private String phone;
    private String password;
    private String imageUrl;

    @OneToOne(cascade = CascadeType.ALL)
    @JoinColumn(name = "address_id", referencedColumnName = "id")
    private Address address;

    //getters and setters omitted for brevity
}

Address.java:

@Entity
@Table(name = "address")
public class Address {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private long id;

    @OneToOne(mappedBy = "address")
    private User user;

    private String country;
    private String state;
    private String city;
    private String street;
    private String pincode;
    
    //getters and setters omitted for brevity
}

AddressRepository.java:

public interface AddressRepository extends CrudRepository<Address, Long> {

}

UserRepository.java:

public interface UserRepository extends CrudRepository<User, Long> {

}

UserDAO.java:

public class UserDAO {
    private final String name;
    private final String email;
    private final String phone;
    private final String imageUrl;
    
    public UserDAO(User user) {
        name = user.getName();
        email = user.getEmail();
        phone = user.getPhone();
        imageUrl = user.getImageUrl();
    }

    public String getName() {
        return name;
    }

    public String getEmail() {
        return email;
    }

    public String getPhone() {
        return phone;
    }

    public String getImageUrl() {
        return imageUrl;
    }
}

A DAO has no connection to the database, it's intent is what the acronym stands for, simply to transfer data, and that's it. When you make a repository, you can stick your objects there by saving them in the repository. Notice that by extending the CrudRepository with correct generics, you don't even need to implement the methods yourself. The save method actually saves the POJO, and returns the saved version, which is why I did user = userRepository.save(user), which may seem counterintuitive at first, but it simply helps ensure that everything is as you expect. If you then want to send the UserDAO object as a response, you can create it using the user object that is returned from the database, maybe something like:

UserDAO dao = new UserDAO(userRepository.save(user));

Please take notice of what is happening inside the test method in MainController. First, we create the POJO User object and set its fields. Then we have to save it to the repository, it is only persisted after you call save method of the repository. Please note that the user object is saved again once it is updated with the address.

This is a very crude way to do things, it is best to create a service layer and do this there with the @Transactional annotation, which would mean that everything is rolled back in case something goes wrong inside a method annotated as @Transactional.

Also, using CascadeType.ALL may be not what you want, please refer to this answer.

Inside fetch method, I ensure that the user indeed exists, which is not guaranteed. To avoid 500 errors, it's important to have a fallback mechanism for when something doesn't work.

As a final side note, you shouldn't be storing raw passwords like that, you should at least use hashing with salt and pepper, or use one of the many available libraries for implementing such functionality (although it can be quite fun getting down and dirty with the code itself). You should also consider how much information you are revealing when something does go wrong, as you don't want to give away too much information which could be used to deanonimise a specific user, or even learn more about your code and the system architecture.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文