如何修复'org.hibernate.TransientPropertyValueException'?

时间:2019-09-09 10:10:16

标签: java spring hibernate jpa thymeleaf

我正在Web应用程序中设置客户端购物车。在添加Shopping Cart类和他的服务之前,一切都还好。现在,当我尝试启动Spring应用程序时,将显示此错误:

Caused by: org.hibernate.TransientPropertyValueException: object references an unsaved transient instance - save the transient instance before flushing : com.myBookstoreProject.domain.security.UserRole.role -> com.myBookstoreProject.domain.security.Role

我搜索了一个解决方案,但发现应用程序的实体存在问题。一种解决方案是将(cascade = CascadeType.ALL)添加到导致错误的实体。但是我的课程已经有了,在购物车课程开始之前一切都很好。

  • 用户类别:

    @Entity
    public class User implements UserDetails {
    
        @Id
        @GeneratedValue(strategy = GenerationType.AUTO)
        @Column(name = "id", nullable = false, updatable = false)
        private Long id;
        private String username;
        private String password;
        private String firstName;
        private String lastName;
    
        @Column(name = "email", nullable = false, updatable = false)
        private String email;
        private String phone;
        private boolean enabled = true;
    
        @OneToMany(mappedBy = "user", cascade = CascadeType.ALL, fetch = FetchType.EAGER)
        @JsonIgnore 
        private Set<UserRole> userRoles = new HashSet<>();
    
        @OneToMany(cascade = CascadeType.ALL, mappedBy = "user")
        private List<UserShipping> userShippingList;
    
        @OneToMany(cascade = CascadeType.ALL, mappedBy = "user")
        private List<UserPayment> userPaymentList;
    
        @OneToOne(cascade = CascadeType.ALL, mappedBy = "user")
        private ShoppingCart shoppingCart;
    
        // getters and setters..
    }
    
  • 角色

    @Entity 
    public class Role {
    
        @Id
        private int roleId;
        private String name;
    
        @OneToMany(mappedBy = "role", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
        private Set<UserRole> userRoles = new HashSet<UserRole>();
    
        // getters and setters..
    }
    
  • UserRole类:

    @Entity
    @Table(name = "user_role")
    public class UserRole {
    
        @Id
        @GeneratedValue(strategy = GenerationType.AUTO)
        private Long userRoleId;
    
        @ManyToOne(fetch = FetchType.EAGER)
        @JoinColumn(name = "user_id")
        private User user;
    
        @ManyToOne(fetch = FetchType.EAGER)
        @JoinColumn(name = "role_id")
        private Role role;
    
        // getters and setters..
    }
    
  • 购物车:

    @Entity
    public class ShoppingCart {
    
        @Id
        @GeneratedValue(strategy = GenerationType.AUTO)
        private Long id;
        private BigDecimal GrandTotal;
    
        @OneToMany(mappedBy="shoppingCart", cascade=CascadeType.ALL, fetch=FetchType.LAZY)
        @JsonIgnore
        private List<CartItem> cartItemList;
    
        @OneToOne(cascade=CascadeType.ALL)
        private User user;
        // getters and setters...
    }
    
  • 购物车服务实现

    @Service
    public class ShoppingCartServiceImpl implements ShoppingCartService {
    
        @Autowired
        private CartItemService cartItemService;
    
        @Autowired
        private ShoppingCartRepository shoppingCartRepository;
    
        @Override
        public ShoppingCart updateShoppingCart(ShoppingCart shoppingCart) {
            BigDecimal cartTotal = new BigDecimal(0);
    
            List<CartItem> cartItemList = cartItemService.findByShoppingCart(shoppingCart);
    
            for (CartItem cartItem : cartItemList) {
                if (cartItem.getBook().getInStockNumber() > 0) {
                    cartItemService.updateCartItem(cartItem);
                    cartTotal = cartTotal.add(cartItem.getSubtotal());
                }
            }
    
            shoppingCart.setGrandTotal(cartTotal);
    
            shoppingCartRepository.save(shoppingCart);
    
            return shoppingCart;
        }
    
    }
    
  • 用户服务实现:

在此类方法中,我添加了“ @Transactional”和5行购物车,然后出现错误

@Override
@Transactional
    public User createUser(User user, Set<UserRole> userRoles) throws Exception {
        User localUser = userRepository.findByUsername(user.getUsername());

        if (localUser != null) {
            LOG.info("user {} already exists. Nothing will be done.", user.getUsername());
        } else {
            for (UserRole ur : userRoles) {
                roleRepository.save(ur.getRole());
            }

            user.getUserRoles().addAll(userRoles);

            ShoppingCart shoppingCart = new ShoppingCart(); // 1
            shoppingCart.setUser(user); // 2
            user.setShoppingCart(shoppingCart); // 3

            user.setUserShippingList(new ArrayList<UserShipping>()); //4
            user.setUserPaymentList(new ArrayList<UserPayment>()); // 5

            localUser = userRepository.save(user);
        }
        return localUser;
    }

此错误将终止Spring应用程序,并且仅在MySql中创建表而不添加行。

修改1: 当我尝试将新用户添加到我的应用程序时,会出现问题。这是我的引导主类:

@SpringBootApplication
public class BookstoreProjectApplication implements CommandLineRunner {

    public static void main(String[] args) {
        SpringApplication.run(BookstoreProjectApplication.class, args);
    }

    @Autowired
    private UserService userService;

    @Override
    public void run(String... args) throws Exception {
        User user1 = new User();
        user1.setFirstName("New");
        user1.setLastName("User");
        user1.setUsername("j");
        user1.setPassword(SecurityUtility.passwordEncoder().encode("p"));
        user1.setEmail("newUser@gmail.com");
        Set<UserRole> userRoles = new HashSet<>();
        Role role1 = new Role();
        role1.setRoleId(1);
        role1.setName("ROLE_USER");
        userRoles.add(new UserRole(user1, role1));

        userService.createUser(user1, userRoles);
    }
}

如果我注释方法主体(运行),则服务器运行得很好,直到应创建新用户为止,然后出现错误。

1 个答案:

答案 0 :(得分:1)

您要从roles中持久保存userRole,然后将其分配给用户,但是在保存后,您不会将持久实体分配给角色,因此roles userRole中的不再与持久化的相同,并且也没有生成的id。当您保存一个实体,然后将其或父级作为值添加到另一个实体并且没有完全级联时,您将添加另一个对象。这意味着,使用保存时返回的对象并将其重新分配给您保存的对象,然后应该没问题,或者在所有地方使用级联,仅保存1个对象。

TLDR; userRoles'role与数据库中的Role实体不同。

编辑1:

Set<UserRole> userRoles更改为List<UserRole> userRoles(否则必须转换100次,因为遍历aSet时无法替换Set的值)然后替换

for (UserRole ur : userRoles) {
  roleRepository.save(ur.getRole());
}

使用

for (int i = 0; i < userRoles.size(); i++) {
  userRoles.get(i).setRole(roleRepository.save(userRoles.get(i).getRole())
}