Olá pessoal
deixa eu explicar meu problema: em um sistema que estou fazendo, um usuário (User) pode ter vários privilégios (Role), um privilégio pode estar associado a vários usuários. Ou seja, um relacionamento many-to-many unidirecional.
Imagens valem mais que palavras então… é disso que estou falando:

Quando eu adiciono um objeto Role, tudo funciona normalmente, mas na hora de cadastrar um usuário, obtenho o seguinte erro:
javax.persistence.PersistenceException: org.hibernate.PersistentObjectException: detached entity passed to persist: com.klimp.entities.Role
Já segui um tutorial (http://uaihebert.com/?p=52) e estou desconfiando que seja alguma coisa relacionada ao atributo “mappedBy”.
Outras referências onde pesquisei:
https://forum.hibernate.org/viewtopic.php?f=9&t=970398
http://www.guj.com.br/java/127260-resolvido-manytomany-problemas-com-relacionamento-jpa
http://www.guj.com.br/java/85186-relacionamento-manytomany-hibernate
Classe “Role”
@Entity
@Table(name="roles",uniqueConstraints=@UniqueConstraint(columnNames="code"))
public class Role implements Serializable {
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
protected int identifier;
protected String code;
protected String summary;
...
}
Classe User:
@Entity
@Table(name = "users")
public class User implements Serializable {
public static final int PASSWORD_MIN_LENGTH = 6;
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
protected int userID;
protected String userName;
@Column(length=128)
protected String password;
protected String name;
protected String lastName;
@Temporal(TemporalType.DATE)
@Column(nullable = true)
protected Date accountExpirationDate;
@ManyToMany(
fetch = FetchType.EAGER,
targetEntity = Role.class,
cascade=CascadeType.ALL)
protected List<Role> roles;
...
}
Qualquer ajuda será muito bem vinda!
Obrigado!
Faça um find ou um getReference em com.klimp.entities.Role antes de adicioná-lo a um relacionamento.
aqui mostra como utilizar o getRefernce que ajuda no desempenho: JPA Consultas e Dicas.
jakefrog
Quando a tela é criada eu faço um find() para popular a lista de privilégios.
De qualquer maneira, vou dar uma lida no link que você mandou.
Abraço!
[quote=marcos.9306]jakefrog
Quando a tela é criada eu faço um find() para popular a lista de privilégios.
De qualquer maneira, vou dar uma lida no link que você mandou.
Abraço![/quote]Você faz o find antes de salvar? Ou antes d enviar pra tela do usuário?
Se for antes de enviar para a tela do usuário não irá fazer diferença, pois o objeto se torna detached quando a transação é finalizada (exceção é Statefull EJB).
Na hora de popular a tela.
Mas porque exatamente eu teria que dar o find() antes de salvar?
A consulta que eu faço para popular a tela me trás objetos com o id da Role carregado :?
O código para submeter as alterações é esse aqui:
public class UserDAO extends DataAccessObject<User> {
public void save(User user) {
createTransactionAndBegin();
entityManager.persist(user);
commitTransaction();
}
...
}
Isso é conceito do JPA/HIbernate.
Uma transação para ser finalizada com sucesso, todo objeto que for salvo/alterado/excluído tem que estar “atualizado” na transação.
Esse é o conceito de attached. Dettached é quando um objeto não está atualizado na transação.
Esse livro é muito bom: http://www.amazon.com/Pro-JPA-Mastering-Persistence-Technology/dp/1430219564/ref=sr_1_1?ie=UTF8&qid=1342208712&sr=8-1&keywords=jpa+2+pro
Você pode achar no google explicações mais detalhadas sobre esse assunto. [=
Esse código abaixo funcionou, mas haveria outra solução mais elegante (ou menos feia) que essa?
public void save(User user) {
createTransactionAndBegin();
ArrayList<Role> attachedRoles = new ArrayList<>();
for(Role role: user.getRoles()) {
Role attached = entityManager.find(Role.class, role.getIdentifier());
attachedRoles.add(attached);
}
user.setRoles(attachedRoles);
entityManager.persist(user);
commitTransaction();
}
Eu gosto de usar o getReference.
Mas aí fica a gosto do cliente. [=
Com getReference() também funciona perfeitamente.
Pelo o que eu li, o getReference evita a consulta ao banco de dados, usando a cache do próprio JPA/Hibernate.
Então pelo jeito, é a melhor solução mesmo…
Muito obrigado pela atenção!
[quote=marcos.9306]Com getReference() também funciona perfeitamente.
Pelo o que eu li, o getReference evita a consulta ao banco de dados, usando a cache do próprio JPA/Hibernate.
Então pelo jeito, é a melhor solução mesmo…
Muito obrigado pela atenção![/quote]O find também evitaria. Mas em ambos os casos só evita caso o objeto esteja dentro do PersistenceContext. Caso o objeto não esteja, ambos irão no DB.
O x da questão é que o cada um traz do DB que facilita o consumo de banda. [=