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

[Dúvida] Estou tendo um problema na hora de fazer uma inserção no banco de dados

Estou refazendo um projeto que tinha feito com java e JPA, agora com Spring. Só que estou sem saber o que está acontecendo, salvo os dados do endereço na tabela de endereco, quando vou salvar os dados do membro e passar o endereço para o membro da um erro que diz que está em estado detched e não permite que seja salvo, se alguém puder me ajudar, vou deixar o codigo a baixo:

Membro:

@Entity
@Table(name = "tb_membros")
@Getter
@Setter
@NoArgsConstructor
public class Membro {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id", nullable = false)
    private Long id;
    @CPF
    @Column(name = "cpf", unique = true, nullable = false, length = 14)
    private String cpf;
    private String nome;
    private Integer idade;
    private LocalDate dataDeNascimento;
    private Genero sexo;
    private String email;
    private Integer senha;
    private Boolean ativo;
    @OneToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
    private Endereco endereco;
    @OneToOne(fetch = FetchType.LAZY,mappedBy = "membro")
    private ContaMembro contaMembro;


    public Membro(String nome, String cpf, LocalDate dataDeNascimento, Genero sexo, Integer senha, String email) {
        this.nome = nome;
        this.cpf = cpf;
        this.dataDeNascimento = dataDeNascimento;
        this.sexo = sexo;
        this.senha = senha;
        this.email = email;
        this.ativo = true;
    }

Endereco:

@Entity
@Table(name = "tb_enderecos")
@Getter
@Setter
@NoArgsConstructor
public class Endereco {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String rua;
    private Integer numero;
    private String bairro;
    private String cidade;
    private Estado estado;

    public Endereco(String rua, Integer numero, String bairro, String cidade, Estado estado) {
        this.rua = rua;
        this.numero = numero;
        this.bairro = bairro;
        this.cidade = cidade;
        this.estado = estado;
    }

EnderecoService:

@Service
public class EnderecoService {

    @Autowired
    private EnderecoRepository repository;

    @Transactional
    public void salvarEndereco(Endereco endereco) {
        repository.save(endereco);
    }

    public Optional<Endereco> listarEnderecoPorID(Long id) {
        return repository.findById(id);
    }
}

MembroService:

@Service
public class MembroService {
    @Autowired
    private MembroRepository repository;
    @Autowired
    private List<IValidar<Membro>> validador;

    @Transactional
    public void salvarMembro(Membro membro) {
        calcularIdade(membro);
        validador.forEach(v -> v.validar(membro));
        repository.save(membro);
    }

    private void calcularIdade(Membro membro) {
        LocalDate dataDeNasc = membro.getDataDeNascimento();
        LocalDate dataAtual = LocalDate.now();
        Integer idade = (int) ChronoUnit.YEARS.between(dataDeNasc, dataAtual);
        membro.setIdade(idade);
    }
}
3 respostas

Classe Main:

@SpringBootApplication
public class DizimoApplication implements CommandLineRunner {

    private static Scanner scanner = new Scanner(System.in);
    @Autowired
    private EnderecoService enderecoService;
    @Autowired
    private MembroService membroService;

    public static void main(String[] args) {
        SpringApplication.run(DizimoApplication.class, args);
    }

    @Override
    public void run(String... args) throws Exception {
        System.out.println("Digite o nome da rua: ");
        String rua = scanner.nextLine();

        System.out.println("Digite o numero da sua residencia: ");
        Integer numero = scanner.nextInt();

        System.out.println("Digite o nome do seu bairro: ");
        String bairro = scanner.nextLine();
        bairro = scanner.nextLine();

        System.out.println("Digite o nome da sua cidade: ");
        String cidade = scanner.nextLine();

        System.out.println("Digite a sigla do seu estado (ex: SP): ");
        String estado = scanner.nextLine();
        Estado estado1 = Estado.acronym(estado);

        Endereco endereco = new Endereco(rua, numero, bairro, cidade, estado1);

        enderecoService.salvarEndereco(endereco);

        System.out.println("Digite seu nome completo: ");
        String nome = scanner.nextLine();

        System.out.println("Digite seu CPF: ");
        String cpf = scanner.nextLine();

        System.out.print("Digite a data de nascimento (no formato YYYY-MM-DD): ");
        String dataNascimentoStr = scanner.nextLine();
        LocalDate dataNascimento = LocalDate.parse(dataNascimentoStr);

        System.out.print("Digite o sexo (M/F): ");
        char sexoChar = scanner.nextLine().charAt(0);
        Genero sexo = Genero.fromChar(sexoChar);

        System.out.print("Digite o email do membro: ");
        String email = scanner.nextLine();

        System.out.print("Digite a senha do membro: ");
        Integer senha = scanner.nextInt();

        Optional<Endereco> enderecoEncontrado = enderecoService.listarEnderecoPorID(endereco.getId());

        if (enderecoEncontrado.isPresent()) {
            Membro membro = new Membro(nome,cpf,dataNascimento,sexo,senha,email);
            membro.setEndereco(enderecoEncontrado.get());
            membroService.salvarMembro(membro);
        } else {
            throw new Exception("Não foi possivel cadastrar o membro");
        }


    }
}

Erro:

Insira aqui a descrição dessa imagem para ajudar na acessibilidade

solução!

Olá, Bruno!

O erro que você está encontrando é comum quando se trabalha com o JPA. O estado "detached" significa que a entidade foi persistida e depois fechada em uma sessão anterior do Hibernate, e agora está desconectada de qualquer sessão do Hibernate.

No seu caso, parece que você está salvando o Endereco e depois tentando associá-lo a um Membro sem carregá-lo novamente na sessão atual do Hibernate.

Uma possível solução seria carregar o Endereco novamente antes de salvá-lo junto com o Membro. No seu MembroService, você poderia fazer algo assim:

@Service
public class MembroService {
    @Autowired
    private MembroRepository repository;
    @Autowired
    private EnderecoService enderecoService;
    @Autowired
    private List<IValidar<Membro>> validador;

    @Transactional
    public void salvarMembro(Membro membro) {
        calcularIdade(membro);
        validador.forEach(v -> v.validar(membro));
        Endereco endereco = enderecoService.listarEnderecoPorID(membro.getEndereco().getId()).orElseThrow(() -> new RuntimeException("Endereço não encontrado"));
        membro.setEndereco(endereco);
        repository.save(membro);
    }

    private void calcularIdade(Membro membro) {
        LocalDate dataDeNasc = membro.getDataDeNascimento();
        LocalDate dataAtual = LocalDate.now();
        Integer idade = (int) ChronoUnit.YEARS.between(dataDeNasc, dataAtual);
        membro.setIdade(idade);
    }
}

Dessa forma, você garante que o Endereco que está sendo associado ao Membro está em estado "persisted" e não "detached".

Por favor, note que este é apenas um exemplo e pode não se encaixar perfeitamente no seu caso, mas espero que te dê uma ideia de como resolver o problema.

Espero ter ajudado e bons estudos!