Solucionado (ver solução)
Solucionado
(ver solução)
4
respostas

Salvando entidades separadamente em uma unica transação com spring data

Uma noite pessoal,

tenho as seguintes entitidades:

@Entity
public class Pessoa {

    @Id
    private Integer id;
    @Column
    private String nome;
    @OneToOne
    private Usuario usuario;
}

@Entity
public class Usuario {
    @Id
    private Integer id;
    @Column
    private String login;
    @OneToOne(mappedBy="usuario")
    private Pessoa pessoa;
    @ManyToMany
    private List<Perfil> perfis;
}

@Entity
public class Perfil {
    @Id
    private Integer id;
    @Column
    private String descricao;
    @ManyToMany
    private List<Usuario> usuarios;
}

tenho os seguintes repositorios:

public interface PessoaRepository extends JPARepository<Pessoa, Integer>{
    ...
}

public interface Usuario extends JPARepository<Usuario, Integer>{
    ...
}

Eu gostaria de salvar as entidades Pessoa e Usuario junto com seus perfis de acesso de uma só vez, no meu service PessoaService. Como eu poderia fazer isso em apenas uma unica transação para que eu possa ir somente uma vez ao banco de dados?

Obrigado a todos.

4 respostas

E ae João, Beleza

O Spring-Data faz muita coisa automática e não sei se você precise se preocupar tanto com as transações, a menos que você precise muito resolver um problema especifico.

Mas caso queira saber como resolver esse problema tem na documentação na sessão 1.3 Custom implementations for Spring Data repositories explicando tudo direitinho.

Mesmo assim segue como ficaria mais ou menos o que você perguntou, importante seguir as convenções do spring-data, não mudando os sufixos Repository, RepositoryCustom, RepositoryImpl, até onde eu lembro da um trabalhão mudar isso

public PessoaRepository interface extends JpaRepository<Pessoa, Integer>, PessoaRepositoryCustom{
  //aqui você adiciona os methodQueries do spring data
}
public interface PessoaRepositoryCustom{
   void cadastrar(Pessoa t);
}
public class PessoaRepositoryImpl implements PessoaRepositoryCustom{
    @PersistenceContext
    @Autowired
    private EntityManager manager;

    @Override
    public void cadastrar(Pessoa t) {
//aqui você faz a festa com o hibernate
        manager.persist(t);
//codigo qualquer
        manager.merge(t);
    }
}

obs: só tomar cuidado pra não colocar regra de negocio aqui.

Bom dia Vinicíus,

na verdade eu queria aproveitar os repositórios PessoaRepository e UsuarioRepository já criados, sem necessidade de implementar customRepository. Bem eu queria algo do tipo:

@Service
public class PessoaService {
    //Atributos ...

    @Autowired
    public PessoaService(PessoaRepository pessoaRepository, UsuarioRepository usuarioRepository) {
        this.pessoaRepository = pessoaRepository;
        this.usuarioRepository = usuarioRepository;
    }

    // Aqui eu salvo a pessoa, usuário e os perfis tudo em uma única transação
    @Transactional
    public void salvarPessoaComUsuario(Pessoa pessoa, Usuario usuario) {
        pessoaRepository.save(pessoa);
        usuarioRepository.save(usuario); //Nesse usuario eu já adicionei os perfis
    }
}

Porque eu estou tentando salvar os dois em uma única transação? Porque eu quero garantir que sempre haverá um usuário para uma pessoa, ou seja, se o persist de pessoa falhar, o usuário não será salvo.

Será que desse jeito que eu estou fazendo está correto ou tem uma forma melhor?

solução!

E ae João,

Entendi o problema, :) Quanto a isso nem precisa se preocupar tanto, se estiver configurado corretamente , ao importar e anotar sua classe/método com@org.springframework.transaction.annotation.Transactional (Coloquei a pacote pra não errar o import), o spring-data se encarrega de controlar as transações.

Mesmo assim é fácil de simular um erro, e descobrir se a transação esta ligada.

No seu código você pode estourara uma RuntimeException

Por exemplo:

@Service
public class PessoaService {
    //seus codigo aqui ...

    @org.springframework.transaction.annotation.Transactional
    public void salvarPessoaComUsuario(Pessoa pessoa, Usuario usuario) {
        pessoaRepository.save(pessoa);
        this.getException(); // se coloca o throws aqui o compilador falha
        usuarioRepository.save(usuario); //Nesse usuario eu já adicionei os perfis
    }
    private void getException(){
        throw new RuntimeException();
    }
}

Com isso ao verificar seu banco, não terá criado nenhuma nova linha no banco de dados nem paraUsuario e nem parapessoa

Vinícius eu segui o seu conselho passado uma exeception propositalmente para testar e realmente, quando eu coloco o @Transaction ele não salva os nenhuma das entidade que seriam salvas nesse método.

Obrigado.