Recebo o seguinte erro ao salvar o objeto usando o Hibernate
object references an unsaved transient instance - save the transient instance before flushing
Recebo o seguinte erro ao salvar o objeto usando o Hibernate
object references an unsaved transient instance - save the transient instance before flushing
Respostas:
Você deve incluir cascade="all"
(se estiver usando xml) ou cascade=CascadeType.ALL
(se estiver usando anotações) em seu mapeamento de coleção.
Isso acontece porque você tem uma coleção em sua entidade e essa coleção possui um ou mais itens que não estão presentes no banco de dados. Ao especificar as opções acima, você diz ao hibernate para salvá-las no banco de dados ao salvar seus pais.
Eu acredito que isso pode ser apenas uma resposta repetida, mas apenas para esclarecer, eu fiz isso em um @OneToOne
mapeamento e também em um @OneToMany
. Nos dois casos, foi o fato de que o Child
objeto que eu estava adicionando ao Parent
ainda não estava salvo no banco de dados. Então, quando eu adicionava o Child
e Parent
, em seguida, salvava o Parent
, o Hibernate lançava a "object references an unsaved transient instance - save the transient instance before flushing"
mensagem ao salvar o pai.
Adicionando no cascade = {CascadeType.ALL}
na Parent's
referência à Child
resolveu o problema em ambos os casos. Isso salvou o Child
e o Parent
.
Desculpe por repetir as respostas, só queria esclarecer mais as pessoas.
@OneToOne(cascade = {CascadeType.ALL})
@JoinColumn(name = "performancelog_id")
public PerformanceLog getPerformanceLog() {
return performanceLog;
}
new MyEntity
(sem sincronizar com o banco de dados - liberação), em vez de obter sua instância sincronizada do banco de dados. Fazer consultas do Hibernate usando essa instância informa que o que você espera estar no banco de dados difere do que você tem na memória do aplicativo. Nesse caso - simplesmente sincronize / instale sua entidade a partir do DB e use-a. Nenhum CascadeType.ALL é necessário então.
Isso acontece ao salvar um objeto quando o Hibernate pensa que precisa salvar um objeto associado àquele que você está salvando.
Eu tinha esse problema e não queria salvar as alterações no objeto referenciado, por isso queria que o tipo de cascata fosse NENHUM.
O truque é garantir que o ID e VERSION no objeto referenciado estejam definidos para que o Hibernate não pense que o objeto referenciado é um novo objeto que precisa ser salvo. Isso funcionou para mim.
Examine todos os relacionamentos da classe que você está salvando para descobrir os objetos associados (e os objetos associados aos objetos associados) e verifique se o ID e VERSION estão definidos em todos os objetos da árvore de objetos.
Como expliquei neste artigo ao usar o JPA e o Hibernate, uma entidade pode estar em um dos 4 estados a seguir:
Novo - Um objeto recém-criado que nunca foi associado a uma Sessão do Hibernate (também conhecido como Contexto de Persistência) e não está mapeado para nenhuma linha da tabela de banco de dados é considerado no estado Novo ou Transitório.
Para se tornar persistente, precisamos chamar explicitamente o persist
método ou fazer uso do mecanismo de persistência transitiva.
Persistente - Uma entidade persistente foi associada a uma linha da tabela do banco de dados e está sendo gerenciada pelo Contexto de Persistência atualmente em execução.
Qualquer alteração feita em uma entidade será detectada e propagada no banco de dados (durante o tempo de liberação da sessão).
Desanexado - Depois que o Contexto de Persistência em execução no momento é fechado, todas as entidades gerenciadas anteriormente ficam desanexadas. As alterações sucessivas não serão mais rastreadas e nenhuma sincronização automática de banco de dados ocorrerá.
Removido - Embora a JPA exija que apenas as entidades gerenciadas possam ser removidas, o Hibernate também pode excluir entidades desanexadas (mas apenas por meio de uma remove
chamada de método).
Para mover uma entidade de um estado para outro, você pode usar os métodos persist
, remove
ou merge
.
O problema que você está descrevendo na sua pergunta:
object references an unsaved transient instance - save the transient instance before flushing
é causado pela associação de uma entidade no estado de Novo a uma entidade que está no estado de Gerenciado .
Isso pode acontecer quando você está associando uma entidade filha a uma coleção um para muitos na entidade pai, e a coleção não faz cascade
o estado de transição.
Portanto, como expliquei neste artigo , você pode corrigir isso adicionando cascata à associação de entidades que acionou essa falha, da seguinte maneira:
@OneToOne
associação@OneToOne(
mappedBy = "post",
orphanRemoval = true,
cascade = CascadeType.ALL)
private PostDetails details;
Observe o
CascadeType.ALL
valor que adicionamos para ocascade
atributo.
@OneToMany
associação@OneToMany(
mappedBy = "post",
orphanRemoval = true,
cascade = CascadeType.ALL)
private List<Comment> comments = new ArrayList<>();
Novamente, o CascadeType.ALL
é adequado para as associações bidirecionais@OneToMany
.
Agora, para que a cascata funcione corretamente em um bidirecional, você também precisa garantir que as associações pai e filho estejam sincronizadas.
Confira este artigo para obter mais detalhes sobre qual é a melhor maneira de atingir esse objetivo.
@ManyToMany
associação@ManyToMany(
mappedBy = "authors",
cascade = {
CascadeType.PERSIST,
CascadeType.MERGE
}
)
private List<Book> books = new ArrayList<>();
Em uma @ManyToMany
associação, você não pode usar CascadeType.ALL
ou, orphanRemoval
como isso, irá propagar a transição do estado da entidade de exclusão de um pai para outra entidade pai.
Portanto, para @ManyToMany
associações, você geralmente coloca em cascata as operações CascadeType.PERSIST
ou CascadeType.MERGE
. Como alternativa, você pode expandir isso para DETACH
ou REFRESH
.
Para mais detalhes sobre a melhor maneira de mapear uma
@ManyToMany
associação, consulte este artigo também.
Ou, se você deseja usar "poderes" mínimos (por exemplo, se você não deseja excluir em cascata) para obter o que deseja, use
import org.hibernate.annotations.Cascade;
import org.hibernate.annotations.CascadeType;
...
@Cascade({CascadeType.SAVE_UPDATE})
private Set<Child> children;
@ManyToMany(cascade={PERSIST, MERGE, REFRESH, DETACH})
(todos, exceto REMOVE), não atualiza em cascata as atualizações, como o Hibernate CascadeType.SAVE_UPDATE
.
No meu caso, foi causado por não estar CascadeType
do @ManyToOne
lado do relacionamento bidirecional. Para ser mais preciso, eu estava CascadeType.ALL
do @OneToMany
lado e não estava @ManyToOne
. Adicionando CascadeType.ALL
ao @ManyToOne
problema resolvido.
Lado um para muitos :
@OneToMany(cascade = CascadeType.ALL, mappedBy="globalConfig", orphanRemoval = true)
private Set<GlobalConfigScope>gcScopeSet;
Lado muitos-para-um (causou o problema)
@ManyToOne
@JoinColumn(name="global_config_id")
private GlobalConfig globalConfig;
Muitos-para-um (corrigido adicionando CascadeType.PERSIST
)
@ManyToOne(cascade = CascadeType.PERSIST)
@JoinColumn(name="global_config_id")
private GlobalConfig globalConfig;
Isso ocorreu para mim ao persistir em uma entidade na qual o registro existente no banco de dados tinha um valor NULL para o campo anotado com @Version (para bloqueio otimista). A atualização do valor NULL para 0 no banco de dados corrigiu isso.
Esta não é a única razão para o erro. Encontrei-o agora devido a um erro de digitação na minha codificação, que acredito, definiu o valor de uma entidade que já estava salva.
X x2 = new X();
x.setXid(memberid); // Error happened here - x was a previous global entity I created earlier
Y.setX(x2);
Eu localizei o erro encontrando exatamente qual variável causou o erro (neste caso String xid
). Eu usei um catch
todo o bloco de código que salvou a entidade e imprimiu os traços.
{
code block that performed the operation
} catch (Exception e) {
e.printStackTrace(); // put a break-point here and inspect the 'e'
return ERROR;
}
Não use Cascade.All
até que você realmente precise. Role
e Permission
ter manyToMany
relação bidirecional . Em seguida, o código a seguir funcionaria bem
Permission p = new Permission();
p.setName("help");
Permission p2 = new Permission();
p2.setName("self_info");
p = (Permission)crudRepository.save(p); // returned p has id filled in.
p2 = (Permission)crudRepository.save(p2); // so does p2.
Role role = new Role();
role.setAvailable(true);
role.setDescription("a test role");
role.setRole("admin");
List<Permission> pList = new ArrayList<Permission>();
pList.add(p);
pList.add(p2);
role.setPermissions(pList);
crudRepository.save(role);
enquanto se o objeto é apenas um "novo", ele lançaria o mesmo erro.
Se sua coleção for anulável, tente: object.SetYouColection(null);
Para adicionar meus 2 centavos, recebi o mesmo problema ao enviar acidentalmente null
como o ID. O código abaixo mostra meu cenário (e o OP não mencionou nenhum cenário específico) .
Employee emp = new Employee();
emp.setDept(new Dept(deptId)); // --> when deptId PKID is null, same error will be thrown
// calls to other setters...
em.persist(emp);
Aqui, estou configurando o ID do departamento existente para uma nova instância de funcionário sem realmente obter a entidade do departamento primeiro, pois não quero que outra consulta de seleção seja acionada.
Em alguns cenários, o deptId
PKID é proveniente null
do método de chamada e estou recebendo o mesmo erro.
Portanto, observe os null
valores do ID da PK
Esse problema aconteceu comigo quando eu criei uma nova entidade e uma entidade associada em um método marcado como e @Transactional
, em seguida, realizei uma consulta antes de salvar. Ex
@Transactional
public someService() {
Entity someEntity = new Entity();
AssocaiatedEntity associatedEntity = new AssocaitedEntity();
someEntity.setAssociatedEntity(associatedEntity);
associatedEntity.setEntity(someEntity);
// Performing any query was causing hibernate to attempt to persist the new entity. It would then throw an exception
someDao.getSomething();
entityDao.create(someEntity);
}
Para corrigir, executei a consulta antes de criar a nova entidade.
além de todas as outras boas respostas, isso pode acontecer se você merge
persistir em um objeto e acidentalmente esquecer de usar a referência mesclada do objeto na classe pai. considere o seguinte exemplo
merge(A);
B.setA(A);
persist(B);
Nesse caso, você mescla, A
mas esquece de usar o objeto mesclado de A
. Para resolver o problema, você deve reescrever o código assim.
A=merge(A);//difference is here
B.setA(A);
persist(B);
Eu também enfrentei a mesma situação. Ao definir a anotação a seguir, a propriedade resolveu a exceção solicitada.
A exceção que eu enfrentei.
Exception in thread "main" java.lang.IllegalStateException: org.hibernate.TransientObjectException: object references an unsaved transient instance - save the transient instance before flushing: com.model.Car_OneToMany
Para superar, a anotação que usei.
@OneToMany(cascade = {CascadeType.ALL})
@Column(name = "ListOfCarsDrivenByDriver")
private List<Car_OneToMany> listOfCarsBeingDriven = new ArrayList<Car_OneToMany>();
O que fez o Hibernate lançar a exceção:
Essa exceção é lançada no seu console porque o objeto filho anexado ao objeto pai não está presente no banco de dados naquele momento.
Ao fornecer @OneToMany(cascade = {CascadeType.ALL})
, ele diz ao Hibernate para salvá-los no banco de dados enquanto salva o objeto pai.
Por uma questão de completude: A
org.hibernate.TransientPropertyValueException
com mensagem
object references an unsaved transient instance - save the transient instance before flushing
também ocorrerá quando você tentar persistir / mesclar uma entidade com uma referência a outra entidade que por acaso é desanexada .
Um outro motivo possível: no meu caso, eu estava tentando salvar o filho antes de salvar o pai, em uma entidade nova.
O código era algo parecido com isso em um modelo User.java:
this.lastName = lastName;
this.isAdmin = isAdmin;
this.accountStatus = "Active";
this.setNewPassword(password);
this.timeJoin = new Date();
create();
O método setNewPassword () cria um registro PasswordHistory e o adiciona à coleção de histórico em User. Como a instrução create () ainda não havia sido executada pelo pai, ela estava tentando salvar em uma coleção de uma entidade que ainda não havia sido criada. Tudo o que tive que fazer para corrigi-lo foi mover a chamada setNewPassword () após a chamada para create ().
this.lastName = lastName;
this.isAdmin = isAdmin;
this.accountStatus = "Active";
this.timeJoin = new Date();
create();
this.setNewPassword(password);
Há outra possibilidade que pode causar esse erro no modo de hibernação. Você pode definir uma referência não salva do seu objeto A
para uma entidade anexada B
e desejar persistir o objeto C
. Mesmo neste caso, você receberá o erro mencionado acima.
Eu acho que é porque você tentou persistir um objeto que tem uma referência a outro objeto que ainda não persiste e, portanto, tenta no "lado do banco de dados" colocar uma referência a uma linha que não existe
A maneira simples de resolver esse problema é salvar as duas entidades. primeiro salve a entidade filha e, em seguida, salve a entidade pai. Porque a entidade pai depende da entidade filha para o valor da chave estrangeira.
Abaixo exame simples de um para um relacionamento
insert into Department (name, numOfemp, Depno) values (?, ?, ?)
Hibernate: insert into Employee (SSN, dep_Depno, firstName, lastName, middleName, empno) values (?, ?, ?, ?, ?, ?)
Session session=sf.openSession();
session.beginTransaction();
session.save(dep);
session.save(emp);
Uma causa possível do erro é a inexistência da configuração do valor da entidade controladora; por exemplo, para um relacionamento departamento-funcionários, você deve escrever isso para corrigir o erro:
Department dept = (Department)session.load(Department.class, dept_code); // dept_code is from the jsp form which you get in the controller with @RequestParam String department
employee.setDepartment(dept);
Há muitas possibilidades desse erro, outras também estão em adicionar página ou editar página. No meu caso, eu estava tentando salvar um objeto AdvanceSalary. O problema é que, na edição, o AdvanceSalary employee.employee_id é nulo, pois na edição não foi definido o employee.employee_id. Eu criei um campo oculto e o configurei. meu código funcionando absolutamente bem.
@Entity(name = "ic_advance_salary")
@Table(name = "ic_advance_salary")
public class AdvanceSalary extends BaseDO{
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id")
private Integer id;
@ManyToOne(fetch = FetchType.EAGER)
@JoinColumn(name = "employee_id", nullable = false)
private Employee employee;
@Column(name = "employee_id", insertable=false, updatable=false)
@NotNull(message="Please enter employee Id")
private Long employee_id;
@Column(name = "advance_date")
@DateTimeFormat(pattern = "dd-MMM-yyyy")
@NotNull(message="Please enter advance date")
private Date advance_date;
@Column(name = "amount")
@NotNull(message="Please enter Paid Amount")
private Double amount;
@Column(name = "cheque_date")
@DateTimeFormat(pattern = "dd-MMM-yyyy")
private Date cheque_date;
@Column(name = "cheque_no")
private String cheque_no;
@Column(name = "remarks")
private String remarks;
public AdvanceSalary() {
}
public AdvanceSalary(Integer advance_salary_id) {
this.id = advance_salary_id;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public Employee getEmployee() {
return employee;
}
public void setEmployee(Employee employee) {
this.employee = employee;
}
public Long getEmployee_id() {
return employee_id;
}
public void setEmployee_id(Long employee_id) {
this.employee_id = employee_id;
}
}
Eu enfrentei essa exceção quando não persisti no objeto pai, mas estava salvando o filho. Para resolver o problema, na mesma sessão persisti os objetos filho e pai e usei CascadeType.ALL no pai.
Caso 1: Eu estava recebendo essa exceção ao tentar criar um pai e salvar a referência pai no filho e, em seguida, em alguma outra consulta DELETE / UPDATE (JPQL). Então, eu apenas libero () a entidade recém-criada depois de criar pai e depois de criar filho usando a mesma referência pai. Funcionou para mim.
Caso 2:
Classe pai
public class Reference implements Serializable {
@Id
@Column(precision=20, scale=0)
private BigInteger id;
@Temporal(TemporalType.TIMESTAMP)
private Date modifiedOn;
@OneToOne(mappedBy="reference")
private ReferenceAdditionalDetails refAddDetails;
.
.
.
}
Classe Criança:
public class ReferenceAdditionalDetails implements Serializable{
private static final long serialVersionUID = 1L;
@Id
@OneToOne
@JoinColumn(name="reference",referencedColumnName="id")
private Reference reference;
private String preferedSector1;
private String preferedSector2;
.
.
}
No caso acima, onde pai (Referência) e filho (ReferenceAdditionalDetails) tendo um relacionamento OneToOne e quando você tenta criar a entidade Reference e, em seguida, seu filho (ReferenceAdditionalDetails), ela fornecerá a mesma exceção. Portanto, para evitar a exceção, você deve definir nulo para a classe filho e criar o pai. (Código de exemplo)
.
.
reference.setRefAddDetails(null);
reference = referenceDao.create(reference);
entityManager.flush();
.
.
Meu problema estava relacionado ao @BeforeEach
JUnit. E mesmo que eu salvei as entidades relacionadas (no meu caso @ManyToOne
), recebi o mesmo erro.
O problema está de alguma forma relacionado à sequência que tenho em meus pais. Se eu atribuir o valor a esse atributo, o problema será resolvido.
Ex. Se eu tiver a entidade Pergunta que pode ter algumas categorias (uma ou mais) e a entidade Pergunta tiver uma sequência:
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "feedbackSeq")
@Id
private Long id;
Eu tenho que atribuir o valor question.setId(1L);
Basta criar o Constructor do seu mapeamento em sua classe base. Como se você deseja uma relação individual na Entidade A, Entidade B. Se você está usando A como classe base, A deve ter um Construtor e B como argumento.