Transações em EJB 3 + JPA

7 respostas
G

Estou com uma dúvida sobre como as transações funcionam no EJB.
Para melhor a compreensão estou apresentando abaixo as classes que criei para realizar meus testes.
O que estou tentando testar é como utilizar a mesma transação para dois métodos de dois session beans diferentes, para que se ocorrer um erro em um deles o outro, caso já tenha sido executado, esteja incluído no rollback da transação. Esse teste pode ser observado no método gravar da classe UsuarioBusiness.
Eu forcei um erro na atualização da entidade usuário setando o campo cpf como nulo e observando se o insert de usuarioHist seria incluido no rollback. Eu cheguei até a mudar o TransactionManagement de CONTAINER para BEAN (controlando eu mesmo a transação), o que não ajudou em nada.

O que eu gostaria de saber é: Como eu faço para se ocorrer algum erro na atualização dos dados do usuario o registro de usuarioHist gravado seja desfeito.

O banco de dados que estou utilizando é o MySQL com tabelas criadas com a engine INNODB.

UsuarioDAO.java

@TransactionManagement(TransactionManagementType.CONTAINER)
@Stateless(name = "usuarioDAO")
@LocalBean
public class UsuarioDAO {

	/**
	 * Gerenciador de entidades
	 */
	@PersistenceContext(unitName = "experimento_pu")
	private EntityManager	entityManager;
	
	/**
	 * Default constructor.
	 */
	public UsuarioDAO() {
	}
	
	/**
	 * @param usuario
	 */
	@TransactionAttribute(TransactionAttributeType.REQUIRED)
	public void excluir(final Usuario usuario) {
		this.entityManager.remove(this.entityManager.find(Usuario.class, usuario.getId()));
	}

	/**
	 * @param usuario
	 * @return Usuario que foi persistido no banco
	 * @throws Exception 
	 */
	@TransactionAttribute(TransactionAttributeType.REQUIRED)
	public Usuario gravar(final Usuario usuario) throws Exception {
		this.entityManager.joinTransaction();
		Usuario temp = this.entityManager.merge(usuario);
		return temp;
	}

	/**
	 * @return lista de usuarios ordenada pelos seus respsctivos nomes
	 */
	@TransactionAttribute(TransactionAttributeType.SUPPORTS)
	public List<Usuario> listarTodos() {
		TypedQuery<Usuario> query = this.entityManager.createQuery("SELECT u FROM Usuario u ORDER BY u.nome", Usuario.class);
		return query.getResultList();
	}

	/**
	 * @param first
	 * @param pageSize
	 * @return lista de usuarios paginada e ordenada pelos seus respsctivos
	 *         nomes
	 */
	@TransactionAttribute(TransactionAttributeType.SUPPORTS)
	public List<Usuario> listarTodosPaginado(int first, int pageSize) {
		TypedQuery<Usuario> query = this.entityManager.createQuery("SELECT u FROM Usuario u ORDER BY u.nome", Usuario.class);
		query.setFirstResult(first);
		query.setMaxResults(pageSize);
		return query.getResultList();
	}

	/**
	 * @param usuario
	 */
	@TransactionAttribute(TransactionAttributeType.REQUIRED)
	public void persistir(final Usuario usuario) {
		this.entityManager.persist(usuario);
	}

	/**
	 * @param id
	 * @return Usuario que tem o identificador informado
	 */
	@TransactionAttribute(TransactionAttributeType.SUPPORTS)
	public Usuario pesquisarPorId(final Long id) {
		return this.entityManager.find(Usuario.class, id);
	}

	/**
	 * @return quantidade total de registros
	 */
	@TransactionAttribute(TransactionAttributeType.SUPPORTS)
	public Long qtdListarTodosPaginado() {
		TypedQuery<Long> query = this.entityManager.createQuery("SELECT COUNT(u) FROM Usuario u", Long.class);
		return query.getSingleResult();
	}
	
}

UsuarioHistDAO.java

@TransactionManagement(TransactionManagementType.CONTAINER)
@Stateless(name = "usuarioHistDAO")
@LocalBean
public class UsuarioHistDAO{

	/**
	 * Gerenciador de entidades
	 */
	@PersistenceContext(unitName = "experimento_pu")
	private EntityManager	entityManager;
	
	/**
	 * Default constructor.
	 */
	public UsuarioHistDAO() {
	}
	
	/**
	 * @param usuarioHist
	 * @return Historico do usuario que foi persistido no banco
	 * @throws Exception 
	 */
	@TransactionAttribute(TransactionAttributeType.REQUIRED)
	public UsuarioHist gravar(final UsuarioHist usuarioHist) throws Exception {
		this.entityManager.joinTransaction();
		UsuarioHist temp =  this.entityManager.merge(usuarioHist);
		return temp;
	}

	/**
	 * @param usuarioHist Historico do usuario
	 */
	@TransactionAttribute(TransactionAttributeType.REQUIRED)
	public void excluir(final UsuarioHist usuarioHist) {
		this.entityManager.remove(this.entityManager.find(UsuarioHist.class, usuarioHist.getId()));
	}

	/**
	 * @return Lista de historico do usuarios ordenada pelos seus respsctivos ids
	 */
	@TransactionAttribute(TransactionAttributeType.SUPPORTS)
	public List<UsuarioHist> listarTodos() {
		TypedQuery<UsuarioHist> query = this.entityManager.createQuery("SELECT uh FROM UsuarioHist uh ORDER BY uh.id", UsuarioHist.class);
		return query.getResultList();
	}

	/**
	 * @param usuarioHist Historico do usuario
	 */
	@TransactionAttribute(TransactionAttributeType.REQUIRED)
	public void persistir(final UsuarioHist usuarioHist) {
		this.entityManager.persist(usuarioHist);
	}

	/**
	 * @param id
	 * @return Historico do usuario que tem o identificador informado
	 */
	@TransactionAttribute(TransactionAttributeType.SUPPORTS)
	public UsuarioHist pesquisarPorId(final Long id) {
		return this.entityManager.find(UsuarioHist.class, id);
	}
	
}

UsuarioBusiness.java

@TransactionManagement(TransactionManagementType.CONTAINER)
@Stateless(name = "usuarioBusiness")
public class UsuarioBusiness implements
							UsuarioBusinessLocal {

	/**
	 * 
	 */
	@EJB
	private UsuarioDAO		usuarioDAO;

	/**
	 * 
	 */
	@EJB
	private UsuarioHistDAO	usuarioHistDAO;

	/**
	 * Default constructor.
	 */
	public UsuarioBusiness() {
	}

	/**
	 * @param usuario
	 */
	@Override
	@TransactionAttribute(TransactionAttributeType.REQUIRED)
	public void excluir(final Usuario usuario) {
		this.usuarioDAO.excluir(usuario);
	}

	/**
	 * @param usuario
	 * @return Usuario que foi persistido no banco
	 */
	@Override
	@TransactionAttribute(TransactionAttributeType.REQUIRED)
	public Usuario gravar(	final Usuario usuario,
							final Usuario autenticado) {
		try {
			if (usuario.getId() != null) {
				usuario.setCpf(null);

				this.usuarioHistDAO.gravar(new UsuarioHist(	this.usuarioDAO.pesquisarPorId(usuario.getId()),
															autenticado));

			} else {
				usuario.setCriadoPor(autenticado);
				usuario.setCriado(Calendar.getInstance().getTime());
			}
			return this.usuarioDAO.gravar(usuario);
		} catch (Exception e) {
			e.printStackTrace();
			return null;
		}
	}

	/**
	 * @return lista de usuarios ordenada pelos seus respsctivos nomes
	 */
	@Override
	@TransactionAttribute(TransactionAttributeType.SUPPORTS)
	public List<Usuario> listarTodos() {
		return this.usuarioDAO.listarTodos();
	}

	/**
	 * @param first
	 * @param pageSize
	 * @return lista de usuarios paginada e ordenada pelos seus respsctivos
	 *         nomes
	 */
	@Override
	@TransactionAttribute(TransactionAttributeType.SUPPORTS)
	public List<Usuario> listarTodosPaginado(	int first,
												int pageSize) {
		return this.usuarioDAO.listarTodosPaginado(	first,
													pageSize);
	}

	/**
	 * @param id
	 * @return Usuario que tem o identificador informado
	 */
	@Override
	@TransactionAttribute(TransactionAttributeType.SUPPORTS)
	public Usuario pesquisarPorId(final Long id) {
		return this.usuarioDAO.pesquisarPorId(id);
	}

	/**
	 * @return quantidade total de registros
	 */
	@Override
	@TransactionAttribute(TransactionAttributeType.SUPPORTS)
	public Long qtdListarTodosPaginado() {
		return this.usuarioDAO.qtdListarTodosPaginado();
	}

}

persistence.xml

<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.0"
	xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">
	<persistence-unit name="experimento_pu" transaction-type="JTA">
		<provider>org.hibernate.ejb.HibernatePersistence</provider>
		<jta-data-source>java:jboss/datasources/TesteDS</jta-data-source>
		<class>br.com.experimento.entity.Erro</class>
		<class>br.com.experimento.entity.Usuario</class>
		<class>br.com.experimento.entity.UsuarioHist</class>
		<properties>
			<property name="hibernate.dialect" value="org.hibernate.dialect.MySQLDialect" />
			<property name="hibernate.show_sql" value="false" />
			<property name="hibernate.format_sql" value="true" />
			<property name="hibernate.max_fetch_depth" value="3" />
			<property name="hibernate.connection.autocommit" value="false"/>
		</properties>
	</persistence-unit>
</persistence>

Usuario.java

@Entity
@Table(name = "usuario")
public class Usuario implements
					Serializable {

	/**
	 * 
	 */
	private static final long	serialVersionUID	= -5957682803614915740L;

	/**
	 * 
	 */
	@Id
	@GeneratedValue(strategy = GenerationType.IDENTITY)
	@Column(name = "USU_ID",
			updatable = false,
			unique = true,
			nullable = false)
	private Long				id;

	/**
	 * 
	 */
	@Column(name = "USU_CPF", nullable = false, length = 11)
	private String				cpf;

	/**
	 * 
	 */
	@Temporal(TemporalType.TIMESTAMP)
	@Column(name = "USU_CRIADO",
			updatable = false,
			nullable = false)
	private Date				criado;

	/**
	 * 
	 */
	@Column(name = "USU_LOGIN",
			nullable = false,
			length = 50)
	private String				login;

	/**
	 * 
	 */
	@Column(name = "USU_NOME",
			nullable = false,
			length = 100)
	private String				nome;

	/**
	 * 
	 */
	@Column(name = "USU_SENHA",
			nullable = false,
			length = 100)
	private String				senha;

	/**
	 * 
	 */
	@Column(name = "USU_STATUS", nullable = false)
	private boolean				status;

	/**
	 * 
	 */
	@Temporal(TemporalType.TIMESTAMP)
	@Column(name = "USU_ULTIMO_ACESSO")
	private Date				ultimoAcesso;

	/**
	 * 
	 */
	@Version
	@Column(name = "USU_VERSAO",
			updatable = false,
			nullable = false)
	private Long				versao;

	/**
	 * uni-directional many-to-one association to Usuario
	 */
	@ManyToOne
	@JoinColumn(name = "USU_CRIADO_POR")
	private Usuario				criadoPor;

	/**
	 * bi-directional many-to-one association to UsuarioHist
	 */
	@OneToMany(	mappedBy = "usuario",
				cascade = CascadeType.ALL,
				fetch = FetchType.EAGER)
	private List<UsuarioHist>	historico;

	/**
	 * 
	 */
	public Usuario() {
	}

	/**
	 * @return CPF
	 */
	public String getCpf() {
		return this.cpf;
	}

	/**
	 * @return Data em que o registro foi criado
	 */
	public Date getCriado() {
		return this.criado;
	}

	/**
	 * @return Usuario que criou o registro
	 */
	public Usuario getCriadoPor() {
		return this.criadoPor;
	}

	/**
	 * @return Historico do registro
	 */
	public List<UsuarioHist> getHistorico() {
		return this.historico;
	}

	/**
	 * @return Identificador do usuario
	 */
	public Long getId() {
		return this.id;
	}

	/**
	 * @return Utilizado para identificar um usuario no sistema
	 */
	public String getLogin() {
		return this.login;
	}

	/**
	 * @return Nome do usuario
	 */
	public String getNome() {
		return this.nome;
	}

	/**
	 * @return Nome do usuario responsavel pela criacao do registro
	 */
	public String getNomeCriador() {
		if (this.criadoPor != null
			&& this.criadoPor.getNome() != null
			&& !this.criadoPor.getNome().isEmpty()) {
			return this.criadoPor.getNome();
		} else {
			return "N/A";
		}
	}

	/**
	 * @return Data da ultima alteracao realizada no registro
	 */
	public Date getDataUltimaAlteracao() {
		if (this.historico != null
			&& !this.historico.isEmpty()) {
			return this.historico.get(this.historico.size() - 1).getAlterado();
		} else {
			return null;
		}
	}

	/**
	 * @return Nome do usuario responsavel pela ultima alteracao realizada no
	 *         registro
	 */
	public String getUsuarioUltimaAlteracao() {
		if (this.historico != null
			&& !this.historico.isEmpty()) {
			return this.historico.get(this.historico.size() - 1).getNomeAlterador();
		} else {
			return "N/A";
		}
	}

	/**
	 * @return Utilizado para autenticar um usuario no sistema
	 */
	public String getSenha() {
		return this.senha;
	}

	/**
	 * @return Data da ultima autenticacao do usuario no sistema
	 */
	public Date getUltimoAcesso() {
		return this.ultimoAcesso;
	}

	/**
	 * @return versao do registro
	 */
	public Long getVersao() {
		return this.versao;
	}

	/**
	 * @param cpf
	 */
	public void setCpf(String cpf) {
		this.cpf = cpf;
	}

	/**
	 * @param criado
	 */
	public void setCriado(Date criado) {
		this.criado = criado;
	}

	/**
	 * @param criadoPor
	 */
	public void setCriadoPor(Usuario criadoPor) {
		this.criadoPor = criadoPor;
	}

	/**
	 * @param historico
	 */
	public void setHistorico(List<UsuarioHist> historico) {
		this.historico = historico;
	}

	/**
	 * @param id
	 */
	public void setId(Long id) {
		this.id = id;
	}

	/**
	 * @param login
	 */
	public void setLogin(String login) {
		this.login = login;
	}

	/**
	 * @param nome
	 */
	public void setNome(String nome) {
		this.nome = nome;
	}

	/**
	 * @param senha
	 */
	public void setSenha(String senha) {
		this.senha = senha;
	}

	/**
	 * @param ultimoAcesso
	 */
	public void setUltimoAcesso(Date ultimoAcesso) {
		this.ultimoAcesso = ultimoAcesso;
	}

	/**
	 * @param versao
	 */
	public void setVersao(Long versao) {
		this.versao = versao;
	}

	/**
	 * @return Status do registro true = ativo, false = inativo
	 */
	public boolean isStatus() {
		return this.status;
	}

	/**
	 * @param status
	 */
	public void setStatus(final boolean status) {
		this.status = status;
	}

	/**
	 * @return Status do usuario, ativo ou inativo
	 */
	public String getStatusFormatado() {
		if (this.status) {
			return "Ativo";
		} else {
			return "Inativo";
		}
	}

}

UsuarioHist.java

@Entity
@Table(name = "usuario_hist")
public class UsuarioHist implements
						Serializable {

	/**
	 * 
	 */
	private static final long	serialVersionUID	= -5589225593308150574L;

	/**
	 * 
	 */
	@Id
	@GeneratedValue(strategy = GenerationType.IDENTITY)
	@Column(name = "USH_ID", updatable = false, unique = true, nullable = false)
	private Long				id;

	/**
	 * 
	 */
	@Temporal(TemporalType.TIMESTAMP)
	@Column(name = "USU_ALTERADO", updatable = false, nullable = false)
	private Date				alterado;

	/**
	 * 
	 */
	@Column(name = "USU_CPF", updatable = false, nullable = false, length = 11)
	private String				cpf;

	/**
	 * 
	 */
	@Column(name = "USU_LOGIN", updatable = false, nullable = false, length = 50)
	private String				login;

	/**
	 * 
	 */
	@Column(name = "USU_NOME", updatable = false, nullable = false, length = 100)
	private String				nome;

	/**
	 * 
	 */
	@Column(name = "USU_SENHA", updatable = false, nullable = false, length = 100)
	private String				senha;

	/**
	 * 
	 */
	@Column(name = "USU_STATUS", updatable = false, nullable = false)
	private boolean				status;

	/**
	 * 
	 */
	@Column(name = "USU_VERSAO", updatable = false, nullable = false)
	private Long				versao;

	// bi-directional many-to-one association to Usuario
	/**
	 * 
	 */
	@ManyToOne
	@JoinColumn(name = "USH_USU_ID", nullable = false)
	private Usuario				usuario;

	// uni-directional many-to-one association to Usuario
	/**
	 * 
	 */
	@ManyToOne
	@JoinColumn(name = "USU_ALTERADO_POR", nullable = false)
	private Usuario				alteradoPor;

	/**
	 * 
	 */
	public UsuarioHist() {
	}

	/**
	 * @param usuario
	 * @param autenticado
	 */
	public UsuarioHist(Usuario usuario, Usuario autenticado) {
		this.usuario = usuario;
		this.alterado = Calendar.getInstance().getTime();
		this.alteradoPor = autenticado;
		this.cpf = usuario.getCpf();
		this.nome = usuario.getNome();
		this.login = usuario.getLogin();
		this.senha = usuario.getSenha();
		this.status = usuario.isStatus();
		this.versao = usuario.getVersao();
	}

	/**
	 * @return Data emq ue o registro foi alterado
	 */
	public Date getAlterado() {
		return this.alterado;
	}

	/**
	 * @return usuario responsavel pela alteracao
	 */
	public Usuario getAlteradoPor() {
		return this.alteradoPor;
	}

	/**
	 * @return CPF
	 */
	public String getCpf() {
		return this.cpf;
	}

	/**
	 * @return Identificador do historico
	 */
	public Long getId() {
		return this.id;
	}

	/**
	 * @return Login
	 */
	public String getLogin() {
		return this.login;
	}

	/**
	 * @return Nome
	 */
	public String getNome() {
		return this.nome;
	}

	/**
	 * @return Senha
	 */
	public String getSenha() {
		return this.senha;
	}

	/**
	 * @return Status
	 */
	public boolean getStatus() {
		return this.status;
	}

	/**
	 * @return Usuario a quem esse historico pertence
	 */
	public Usuario getUsuario() {
		return this.usuario;
	}

	/**
	 * @return Versao original antes da alteracao
	 */
	public Long getVersao() {
		return this.versao;
	}

	/**
	 * @return Nome do usuario responsavel pela criacao do registro
	 */
	public String getNomeAlterador() {
		if (this.alteradoPor != null && this.alteradoPor.getNome() != null && !this.alteradoPor.getNome().isEmpty()) {
			return this.alteradoPor.getNome();
		} else {
			return "N/A";
		}
	}

	/**
	 * @param alterado
	 */
	public void setAlterado(Date alterado) {
		this.alterado = alterado;
	}

	/**
	 * @param alteradoPor
	 */
	public void setAlteradoPor(Usuario alteradoPor) {
		this.alteradoPor = alteradoPor;
	}

	/**
	 * @param cpf
	 */
	public void setCpf(String cpf) {
		this.cpf = cpf;
	}

	/**
	 * @param id
	 */
	public void setId(Long id) {
		this.id = id;
	}

	/**
	 * @param login
	 */
	public void setLogin(String login) {
		this.login = login;
	}

	/**
	 * @param nome
	 */
	public void setNome(String nome) {
		this.nome = nome;
	}

	/**
	 * @param senha
	 */
	public void setSenha(String senha) {
		this.senha = senha;
	}

	/**
	 * @param status
	 */
	public void setStatus(boolean status) {
		this.status = status;
	}

	/**
	 * @param usuario
	 */
	public void setUsuario(Usuario usuario) {
		this.usuario = usuario;
	}

	/**
	 * @param versao
	 */
	public void setVersao(Long versao) {
		this.versao = versao;
	}

}

Tabela usuario

+-------------------+---------------------+------+-----+-------------------+----------------+
| Field             | Type                | Null | Key | Default           | Extra          |
+-------------------+---------------------+------+-----+-------------------+----------------+
| USU_ID            | bigint(18) unsigned | NO   | PRI | NULL              | auto_increment |
| USU_CPF           | varchar(11)         | NO   |     | NULL              |                |
| USU_NOME          | varchar(100)        | NO   |     | NULL              |                |
| USU_LOGIN         | varchar(50)         | NO   |     | NULL              |                |
| USU_SENHA         | varchar(100)        | NO   |     | NULL              |                |
| USU_STATUS        | tinyint(1)          | NO   |     | 1                 |                |
| USU_ULTIMO_ACESSO | timestamp           | YES  |     | NULL              |                |
| USU_CRIADO        | timestamp           | NO   |     | CURRENT_TIMESTAMP |                |
| USU_CRIADO_POR    | bigint(18) unsigned | YES  | MUL | NULL              |                |
| USU_VERSAO        | bigint(18) unsigned | NO   |     | NULL              |                |
+-------------------+---------------------+------+-----+-------------------+----------------+

Tabela usuario_hist

+------------------+---------------------+------+-----+-------------------+----------------+
| Field            | Type                | Null | Key | Default           | Extra          |
+------------------+---------------------+------+-----+-------------------+----------------+
| USH_ID           | bigint(18) unsigned | NO   | PRI | NULL              | auto_increment |
| USH_USU_ID       | bigint(18) unsigned | NO   | MUL | NULL              |                |
| USU_CPF          | varchar(11)         | NO   |     | NULL              |                |
| USU_NOME         | varchar(100)        | NO   |     | NULL              |                |
| USU_LOGIN        | varchar(50)         | NO   |     | NULL              |                |
| USU_SENHA        | varchar(100)        | NO   |     | NULL              |                |
| USU_STATUS       | tinyint(1)          | NO   |     | NULL              |                |
| USU_VERSAO       | bigint(18) unsigned | NO   |     | NULL              |                |
| USU_ALTERADO     | timestamp           | NO   |     | CURRENT_TIMESTAMP |                |
| USU_ALTERADO_POR | bigint(18) unsigned | NO   | MUL | NULL              |                |
+------------------+---------------------+------+-----+-------------------+----------------+

7 Respostas

lele_vader

Acredito que você terá que criar um ejb que chame os 2 daos, esse anotado com required. Daí quando der erro por exemplo na 2 transacao ele irá reverter os efeitos da 1

felipeguerra

o que está em debate é a atomicidade das transações.

Genericamente, você tem um processo que encapsula chamadas ao banco de dados. Não há importância se os métodos estão na mesma classe. Nesse caso o que conta são os mecanismos de controle transacional: as anotações do EJB + Persistence Manager da JPA.

Na parte arquitetural, é importante criar um método que seja o coordenador da transação dentro do EJB. Ela tem que ser anotada para que delimite que tipo de transação ela participa (se abre uma nova, se é requerido um escopo de transação para invocar o método, etc).

Outro ponto importante, são as exceções. Tem que se lançar uma exceção que herde de RuntimeException para que o rollback seja feito. Ou é possível customizar com as anotações do EJB.

Uma vez aprendi nesse fórum a não sair usando objetos do tipo EJB indiscriminadamente, pois num ambiente que necessite de escalabilidade, isso pode acarretar num problema futuro dentro do Container EJB.

abraço

G

lele_vader muito obrigado pela resposta.
Foi o que eu pensei que funcionaria no início. Se você olhar a classe UsuarioBussines ela tem a referência dos dois DAOs e no seu próprio método gravar ele executa o método gravar da UsuarioHistDAO e logo após executa o método gravar da UsuarioDAO, porém antes de executar o método gravar da UsuarioDAO eu seto null em um campo obrigatório do registro usuario para forçar um erro de gravação. E ao imprimir a exceção do método gravar da UsuarioDAO no console eu vou ao banco e o registro da UsuarioHistDAO ainda está la. Ou seja o rollback somente foi executado para UsuarioDAO, indicando que eles não estavam trabalhando na mesma transação.

G

felipeguerra muito obrigado pela ajuda.
Eu já tentei anotar o método gravar da classe UsuarioBusiness com REQUIRES_NEW porém não tive sucesso. Eu vou tentar novamente, mas desta vez vou fazer com que a exceção do método gravar da classe UsuarioDAO herde de RuntimeException.

lele_vader

Eu entendi o que quis dizer.
Realmente poderia-se criar um método que englobasse todas as chamadas, porém como elas são de classes diferentes eu criaria um ejb no serviço e lá colocaria @Required para dar rollback na transação inteira.

Sobre a questão de não se criar ejb’s indefinidamente concordo com você

Só cuidado com o @Requires_new, pois se usá-la por exemplo na 2 transação e ela desse erro a 1 não daria rollback, pois o requires_new cria uma outra transação se não estou enganado.

felipeguerra

nesse caso que há transações, é necessário construir um modelo conceitual, um diagrama de sequência, enfim, algum documento que dê uma visão de como deve ser coordenada a transação, ANTES de partir para a programação.

C

Não sei se ajuda, mas tenta fazer o seguinte teste :

esse mapeamento

@ManyToOne  
    @JoinColumn(name = "USH_USU_ID", nullable = false)  
    private Usuario             usuario;

para

@ManyToOne(cascade={CascadeType.ALL})
    @JoinColumn(name = "USH_USU_ID", nullable = false)  
    private Usuario             usuario;
Criado 30 de outubro de 2012
Ultima resposta 21 de nov. de 2012
Respostas 7
Participantes 4