【问题标题】:Hibernate + JPA + spring boot org.hibernate.PersistentObjectException: detached entity passed to persistHibernate + JPA + spring boot org.hibernate.PersistentObjectException:分离的实体传递给持久化
【发布时间】:2018-01-05 14:26:19
【问题描述】:

我正在使用 Spring Boot 4、Hibernate 和 JPA 注释。我遇到了这个错误

org.hibernate.PersistentObjectException:分离的实体传递给 坚持下去。

我尝试在互联网上进行搜索,但无法得到正确的答案。我尝试使用merge 而不是persist,但它不起作用。此外,我认为更新资源时会使用merge

这是我的代码:

`

package com.matome.users.login.stats.springbootStarter.model;

import java.io.Serializable;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.NamedQuery;
import javax.persistence.NamedQueries;

import com.matome.users.login.stats.springbootStarter.userconstants.UserRepositoryConstants;

@NamedQueries({
      @NamedQuery(name = UserRepositoryConstants.NAME_GET_ALL_USERS,
          query = UserRepositoryConstants.QUERY_GET_ALL_USERS),
      @NamedQuery(name = UserRepositoryConstants.NAME_GET_USER_BY_ID,
      query = UserRepositoryConstants.QUERY_GET_USER_BY_ID),
      @NamedQuery(name = UserRepositoryConstants.NAME_GET_USER_BY_USERNAME,
      query = UserRepositoryConstants.QUERY_GET_USER_BY_USERNAME),
      @NamedQuery(name = UserRepositoryConstants.NAME_DELETE_USER,
      query = UserRepositoryConstants.QUERY_DELETE_USER)
})

@Entity
public class User implements Serializable{

    private static final long serialVersionUID = 1L;

    @Id @GeneratedValue(strategy = GenerationType.AUTO)
    Long id;
    String username;
    String password;
    String phone;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public String getPhone() {
        return phone;
    }

    public void setPhone(String phone) {
        this.phone = phone;
    }
}

` CRUD服务

`

package com.matome.users.login.stats.springbootStarter.CRUDService;

import javax.persistence.EntityManager;
import javax.persistence.NoResultException;
import javax.persistence.PersistenceContext;
import javax.transaction.Transactional;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.matome.users.login.stats.springbootStarter.model.User;
import com.matome.users.login.stats.springbootStarter.repository.UserRepository;


@Service
@Transactional
public class UserCRUDService {
    @Autowired
    UserRepository userRepository;

    public User createUser(User user) {
        userRepository.save(user);
        return user;
    }

    public User updateUser(User user) {
        User existingUser = userRepository.getUserById(user.getId());

        if (existingUser == null) {
           throw new NoResultException();
        }

        existingUser.setId(user.getId());
        existingUser.setUsername(user.getUsername());
        existingUser.setPhone(user.getPhone());
        existingUser.setPassword(user.getPassword());
        userRepository.update(user);
        return user;
    }

    public User deleteUser(long id) {
        User user = userRepository.getUserById(id);

        if (user == null) {
          throw new NoResultException();
        }

        userRepository.delete(user);
        return user;
    }

}

`

存储库

`

    package com.matome.users.login.stats.springbootStarter.repository;

import java.util.List;

import javax.persistence.TypedQuery;

import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;

import com.matome.users.login.stats.springbootStarter.model.User;
import com.matome.users.login.stats.springbootStarter.userconstants.UserRepositoryConstants;

@Repository
@Transactional
public class UserRepository extends AbstractRepository<User> {
    TypedQuery<User> query;

    public List<User> getAllUsers() {
        query = entityManager.createNamedQuery(UserRepositoryConstants.NAME_GET_ALL_USERS, User.class);
        return query.getResultList();
    }

    public User getUserById(long id) {
        query = entityManager.createNamedQuery(UserRepositoryConstants.NAME_GET_USER_BY_ID, User.class);
        query.setParameter("id", id);
        return query.getSingleResult();
    }

    public User getUserByUsername(String username) {
        query = entityManager.createNamedQuery(UserRepositoryConstants.NAME_GET_USER_BY_USERNAME, User.class);
        query.setParameter("username", username);
        return query.getSingleResult();
    }
}

` 抽象存储库

`

package com.matome.users.login.stats.springbootStarter.repository;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;

import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

@Transactional(readOnly = true, propagation = Propagation.SUPPORTS)
public abstract class AbstractRepository<T> {

    @PersistenceContext
    protected EntityManager entityManager;

    @Transactional(propagation = Propagation.MANDATORY)
    public void save(T entity) {
        entityManager.persist(entity);
        entityManager.flush();
        entityManager.refresh(entity);
    }

    @Transactional
    public T update(T entity) {
        return entityManager.merge(entity);
    }

    @Transactional
    public void delete(T entity) {
        entityManager.remove(entity);
    }
}

`

工厂 `

 package com.matome.users.login.stats.springbootStarter.factories;

import java.util.ArrayList;
import java.util.List;

import org.springframework.stereotype.Service;

import com.matome.users.login.stats.springbootStarter.BackingForm.UserBackingForm;
import com.matome.users.login.stats.springbootStarter.model.User;
import com.matome.users.login.stats.springbootStarter.viewmodels.UserViewModel;

@Service
public class UserFactory {

    public UserViewModel createViewModel(User user) {
        UserViewModel viewModel = new UserViewModel();
        viewModel.setId(user.getId());
        viewModel.setUsername(user.getUsername());
        viewModel.setPhone(user.getPhone());
        return viewModel;
    }

    public List<UserViewModel> createVewModels(List<User> users) {
        List<UserViewModel> viewModels = new ArrayList<>();

        if (users != null) {
          for (User user : users) {
              viewModels.add(createViewModel(user));
          }
        }
        return viewModels;
    }

    public User createEntity(UserBackingForm userBackingForm) {
        User user = new User();
        user.setId(userBackingForm.getId());
        user.setUsername(userBackingForm.getUsername());
        user.setPassword(userBackingForm.getPassword());
        user.setPhone(userBackingForm.getPhone());
        return user;
    }

}

` 支持表格

`

package com.matome.users.login.stats.springbootStarter.BackingForm;

public class UserBackingForm {
    private long id;
    private String username;
    private String password;
    private String phone;

    public long getId() {
        return id;
    }

    public void setId(long id) {
        this.id = id;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public String getPhone() {
        return phone;
    }

    public void setPhone(String phone) {
        this.phone = phone;
    }
}

`

控制器

`

package com.matome.users.login.stats.springbootStarter.controller;

import java.util.HashMap;
import java.util.Map;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

import com.matome.users.login.stats.springbootStarter.BackingForm.UserBackingForm;
import com.matome.users.login.stats.springbootStarter.CRUDService.UserCRUDService;
import com.matome.users.login.stats.springbootStarter.factories.UserFactory;
import com.matome.users.login.stats.springbootStarter.model.User;
import com.matome.users.login.stats.springbootStarter.repository.UserRepository;
import com.matome.users.login.stats.springbootStarter.viewmodels.UserViewModel;


@RestController
@RequestMapping("api")
public class UserController {
    @Autowired
    private UserRepository userRepository;
    @Autowired
    private UserCRUDService userCRUDService;
    @Autowired
    private UserFactory userFactory;

    @RequestMapping(value = "/users", method = RequestMethod.GET)
    public Map<String, Object> getAllUsers() {
        Map<String, Object> map = new HashMap<>();
        map.put("users", userRepository.getAllUsers());
        return map;
    }

    @RequestMapping(value = "{id}", method = RequestMethod.GET)
    public Map<String, Object> getUserById(@PathVariable long id) {
        Map<String, Object> map = new HashMap<>();
        map.put("user", userRepository.getUserById(id));
        return map;
    }


@RequestMapping(value = "/user/add", method = RequestMethod.POST)
public UserViewModel addUser(@RequestBody UserBackingForm form) {
    User user = userFactory.createEntity(form);
    //user.setIsActive(true);
    user = userCRUDService.createUser(user);
    return userFactory.createViewModel(user);
 }

@RequestMapping(value = "/user/add/{id}", method =    RequestMethod.PUT)
public Map<String, Object> updateUser(@PathVariable("id") Long id, @RequestBody User user) {
    Map<String, Object> map = new HashMap<>();
    user.setId(id);
    map.put("updatedUser", userCRUDService.updateUser(user));
    return map;
}

@RequestMapping(value = "/test", method = RequestMethod.GET)
public String testFinal() {
    return "User test sucessfully";
}

@RequestMapping(value = "{id}", method = RequestMethod.DELETE)
public void deleteUser(@PathVariable("id") Long id) {
    User user = userRepository.getUserById(id);
    userRepository.delete(user);
}

}

`

查看模型

`

  package com.matome.users.login.stats.springbootStarter.viewmodels;

public class UserViewModel extends BaseViewModel<Long> {
    private String username;
    private String phone;

    public String getUsername() {
        return username;
    }
    public void setUsername(String username) {
        this.username = username;
    }
    public String getPhone() {
        return phone;
    }
    public void setPhone(String phone) {
        this.phone = phone;
    }
}

`

基础视图模型

`
  package com.matome.users.login.stats.springbootStarter.viewmodels;

public abstract class BaseViewModel<T> {

  private T id;

  public T getId() {
    return id;
  }

  public void setId(T id) {
    this.id = id;
  }

  @Override
  public int hashCode() {
    final int prime = 31;
    int result = 1;
    result = prime * result + ((getId() == null) ? 0 : getId().hashCode());
    return result;
  }

  @Override
  public boolean equals(Object obj) {
    if (this == obj)
      return true;
    if (obj == null)
      return false;
    if (getClass() != obj.getClass())
      return false;
    @SuppressWarnings("unchecked")
    BaseViewModel<T> other = (BaseViewModel<T>) obj;
    if (getId() == null) {
      if (other.getId() != null)
        return false;
    } else if (!getId().equals(other.getId()))
      return false;
    return true;
  }

}

`

主要

`    


  package com.matome.users.login.stats.springbootStarter;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class Main { public static void main(String[] args) {
        SpringApplication.run(Main.class,args);

    }

}

` 堆栈跟踪

`

org.hibernate.PersistentObjectException: detached entity passed to persist: com.matome.users.login.stats.springbootStarter.model.User
    at org.hibernate.event.internal.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:124) ~[hibernate-core-5.0.11.Final.jar:5.0.11.Final]
    at org.hibernate.event.internal.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:58) ~[hibernate-core-5.0.11.Final.jar:5.0.11.Final]
    at org.hibernate.internal.SessionImpl.firePersist(SessionImpl.java:775) ~[hibernate-core-5.0.11.Final.jar:5.0.11.Final]
    at org.hibernate.internal.SessionImpl.persist(SessionImpl.java:748) ~[hibernate-core-5.0.11.Final.jar:5.0.11.Final]
    at org.hibernate.internal.SessionImpl.persist(SessionImpl.java:753) ~[hibernate-core-5.0.11.Final.jar:5.0.11.Final]
    at org.hibernate.jpa.spi.AbstractEntityManagerImpl.persist(AbstractEntityManagerImpl.java:1146) ~[hibernate-entitymanager-5.0.11.Final.jar:5.0.11.Final]
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_131]
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_131]
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_131]
    at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_131]
    at org.springframework.orm.jpa.ExtendedEntityManagerCreator$ExtendedEntityManagerInvocationHandler.invoke(ExtendedEntityManagerCreator.java:347) ~[spring-orm-4.3.4.RELEASE.jar:4.3.4.RELEASE]
    at com.sun.proxy.$Proxy78.persist(Unknown Source) ~[na:na]
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_131]

`

【问题讨论】:

    标签: java spring hibernate jpa


    【解决方案1】:

    你能提供完整的堆栈跟踪吗?

    给你的一些提示: 您应该只在服务级别设置@Transactional,而不是 dao / repository。

    Entitymanager 刷新方法意味着从数据库中获取数据以重新创建实体。如果您只是持久化实体,则无需刷新它。

    @Transactional(propagation = Propagation.MANDATORY)
    public void save(T entity) {
        entityManager.persist(entity);
        entityManager.flush();  //  This is not really usefull on a small transaction
        entityManager.refresh(entity); // No need for that : persist means entity already in sync
    }
    

    在更新方面,如果您有一个托管实体,则无需将其同步回数据库,JPA 会自动处理。 (并合并数据库中的推送实体,强制更新所有列):

    public User updateUser(User user) {
        User existingUser = userRepository.getUserById(user.getId());
    
        // ...
        existingUser.setPassword(user.getPassword());
        // ...
        userRepository.update(user);  // This is not needed !
        return user;
    }
    

    如果你想要最简单的东西,你应该看看龙目岛。它将帮助您编写最简单的 bean(@Data 生成 getter/setter ...) 你用的是 Spring,所以看看 spring-Data-JPA,它为你写了一些麻烦的代码。

    【讨论】:

    • 嗨 Wargre 我已经编辑了问题以包含堆栈跟踪。感谢您的建议,我会尝试龙目岛。
    【解决方案2】:

    在我看来,问题在于您已将 idUser 实体声明为自动生成的:

    @Id @GeneratedValue(strategy = GenerationType.AUTO)
    Long id;
    

    当您创建 User 对象时,您手动设置了 id:

     public User createEntity(UserBackingForm userBackingForm) {
        User user = new User();
        user.setId(userBackingForm.getId());
        ...
    

    因此,当您将其传递给 persist / save 时,持久性提供程序将不允许这样做,因为它期望客户端不会设置 id

    底线是,当您的目标是持久化一个新条目时,您不应该针对当前配置设置id,您应该没问题。

    【讨论】:

    • 您好 Maciej,感谢您的建议。我删除了自动生成并添加了这个:`user.setId(form.getId());用户 = userCRUDService.createUser(user);返回 userFactory.createViewModel(user); `
    • 以上修改导致了这个异常"exception": "org.springframework.dao.InvalidDataAccessResourceUsageException", "message": "could not prepare statement; SQL [insert into user (password, phone, username, id) values (?, ?, ?, ?)]; nested exception is org.hibernate.exception.SQLGrammarException: could not prepare statement
    • 记得从 UserRepository 中删除 Transactional 注释,因为您已经将服务设为事务性
    • 抱歉回复晚了。问题是 DB(derby) 将用户表与用户表中的另一个默认构建混淆了。我将 pom 从 derby 更改为 h2 db,一切正常。
    猜你喜欢
    • 2017-10-21
    • 1970-01-01
    • 1970-01-01
    • 2018-09-10
    • 1970-01-01
    • 2015-11-03
    • 2020-11-02
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多