java.lang.StackOverflowError: null [Spring Boot, Hibernate]
java.lang.StackOverflowError: null [Spring Boot, Hibernate]
我有两个 classes User.java 和 Address.java 还有一个 它们之间的一对一双向映射。
但是当我尝试使用 User class 获取地址时,我得到一个 "java.lang.WhosebugError: null"异常。
当我尝试从 Address class.
获取 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() 函数将数据放入数据库,它工作正常。
database image
但是当我调用 fetch() 函数时出现以下错误
java.lang.WhosebugError: 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.WhosebugError:null
System.out.println(user.getAddress());
return "working";
}
}
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;
}
}
DAO 与数据库没有连接,它的意图是首字母缩略词所代表的,只是为了传输数据,仅此而已。创建存储库时,您可以通过将对象保存在存储库中来将它们粘贴在那里。请注意,通过使用正确的泛型扩展 CrudRepository
,您甚至不需要自己实现这些方法。 save
方法实际上保存了 POJO,而 returns 保存了版本,这就是为什么我这样做 user = userRepository.save(user)
,乍一看似乎违反直觉,但它只是帮助确保一切如你所愿预计。如果您随后想发送 UserDAO
对象作为响应,您可以使用从数据库返回的 user
对象创建它,可能类似于:
UserDAO dao = new UserDAO(userRepository.save(user));
请注意MainController
中test
方法内部发生的事情。首先,我们创建 POJO User
对象并设置它的字段。然后我们必须将它保存到存储库中,它只有在您调用存储库的 save
方法后才会持久化。请注意,user
对象在使用 address
.
更新后会再次保存
这是一种非常粗糙的做事方式,最好创建一个服务层并在其中使用 @Transactional
注释执行此操作,这意味着一切都会回滚,以防万一内部出现问题注释为 @Transactional
.
的方法
另外,使用CascadeType.ALL
可能不是你想要的,请参考这个answer。
在 fetch
方法中,我确保 user
确实存在,但不能保证。为避免 500 错误,重要的是要有一个回退机制,以应对出现问题的情况。
最后一点,你不应该像那样存储原始密码,你至少应该使用加盐和胡椒的散列,或者使用许多可用的库之一来实现这样的功能(尽管它可以是弄脏代码本身非常有趣)。您还应该考虑当出现问题时您透露了多少信息,因为您不想泄露太多可能被用来对特定用户进行匿名化处理的信息,甚至不想了解有关您的代码和系统架构的更多信息。
我有两个 classes User.java 和 Address.java 还有一个 它们之间的一对一双向映射。
但是当我尝试使用 User class 获取地址时,我得到一个 "java.lang.WhosebugError: null"异常。
当我尝试从 Address class.
获取 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() 函数将数据放入数据库,它工作正常。 database image
但是当我调用 fetch() 函数时出现以下错误
java.lang.WhosebugError: 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.WhosebugError:null
System.out.println(user.getAddress());
return "working";
}
}
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;
}
}
DAO 与数据库没有连接,它的意图是首字母缩略词所代表的,只是为了传输数据,仅此而已。创建存储库时,您可以通过将对象保存在存储库中来将它们粘贴在那里。请注意,通过使用正确的泛型扩展 CrudRepository
,您甚至不需要自己实现这些方法。 save
方法实际上保存了 POJO,而 returns 保存了版本,这就是为什么我这样做 user = userRepository.save(user)
,乍一看似乎违反直觉,但它只是帮助确保一切如你所愿预计。如果您随后想发送 UserDAO
对象作为响应,您可以使用从数据库返回的 user
对象创建它,可能类似于:
UserDAO dao = new UserDAO(userRepository.save(user));
请注意MainController
中test
方法内部发生的事情。首先,我们创建 POJO User
对象并设置它的字段。然后我们必须将它保存到存储库中,它只有在您调用存储库的 save
方法后才会持久化。请注意,user
对象在使用 address
.
这是一种非常粗糙的做事方式,最好创建一个服务层并在其中使用 @Transactional
注释执行此操作,这意味着一切都会回滚,以防万一内部出现问题注释为 @Transactional
.
另外,使用CascadeType.ALL
可能不是你想要的,请参考这个answer。
在 fetch
方法中,我确保 user
确实存在,但不能保证。为避免 500 错误,重要的是要有一个回退机制,以应对出现问题的情况。
最后一点,你不应该像那样存储原始密码,你至少应该使用加盐和胡椒的散列,或者使用许多可用的库之一来实现这样的功能(尽管它可以是弄脏代码本身非常有趣)。您还应该考虑当出现问题时您透露了多少信息,因为您不想泄露太多可能被用来对特定用户进行匿名化处理的信息,甚至不想了解有关您的代码和系统架构的更多信息。