Bidirecional simples @OneToOne store null

Nov 07 2020

Tento seguir o exemplo bidrecional @OneToOne da documentação, mas sempre tenho uma referência nula na coluna PhoneDetails phone_id e obtein um Phone com detalhes nulos quando faço um get.

Eu sigo a documentação criando classes Phone e PhoneDetails como está escrito.

Mas como a documentação não especifica como testar e preciso desenvolver um aplicativo Spring Boot, crio um para testar e talvez, em qualquer etapa deste, tenha o erro.

Aqui estão minhas etapas e o código que atualizo para o github, esperando que alguém possa revisá-lo

1 - Crie um aplicativo Spring Boot no Eclipse com conjunto de ferramentas Spring com Java Versão 8

2 - Adicionar dependências MySqlDriver e Spring Web

3 - Adicionar dependência no pom para spring-boot-starter-data-jpa

4 - Crie pacotes no pacote principal (com.onetoone) para modelos, controladores e repositório

5 - Crie no pacote do modelo as classes Phone e PhoneDetails conforme digamos no pacote de documentação com.onetoone.models;

@Entity(name = "Phone")
public class Phone {

    @Id
    @GeneratedValue
    private Long id;

    @Column(name = "`number`")
    private String number;

    @OneToOne(
        mappedBy = "phone",
        cascade = CascadeType.ALL,
        orphanRemoval = true,
        fetch = FetchType.LAZY
    )
    private PhoneDetails details;

    //Getters and setters are omitted for brevity

    public void addDetails(PhoneDetails details) {
        details.setPhone( this );
        this.details = details;
    }

    public void removeDetails() {
        if ( details != null ) {
            details.setPhone( null );
            this.details = null;
        }
    }
}

@Entity(name = "PhoneDetails")
public class PhoneDetails {

    @Id
    @GeneratedValue
    private Long id;

    private String provider;

    private String technology;

    @OneToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "phone_id")
    private Phone phone;

    //Getters and setters are omitted for brevity

}

Na documentação, eles criam essas classes como estáticas, mas tenho que removê-las porque o Eclipse diz: "Modificador ilegal para a classe Phone; apenas público, abstrato e final são permitidos". Eu importo todas as anotações (@Entity, @Id ...) de javax.persistence.

6 - Crie a interface PhoneController em seu pacote

package com.onetoone.repository;
import org.springframework.data.jpa.repository.JpaRepository;   
import com.onetoone.models.Phone;

public interface PhoneRepository extends JpaRepository<Phone, Long> {
}

7 - Criar PhoneController

package com.onetoone.controllers;

import java.util.Optional;  
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;  
import com.onetoone.models.Phone;
import com.onetoone.repository.PhoneRepository;

@RestController
@RequestMapping("/phone")
public class PhoneController {
    
    @Autowired
    PhoneRepository phoneRepository;
    
    @PostMapping("/save")
    public  ResponseEntity<Boolean> savePhone(@RequestBody Phone phone) {
        phoneRepository.save(phone);
        return ResponseEntity.ok(true);
    }
    
    @PostMapping("/get")
    public ResponseEntity<Optional<Phone>> getPhone(@RequestBody int i) {

        Long id = Long.valueOf(i);
        Optional<Phone> phone = phoneRepository.findById(id);

        return ResponseEntity.ok(phone);
    }

}

8 - Defina o application.properties em seu arquivo em src / main / resources

spring.datasource.url= jdbc:mysql://localhost:3306/test?serverTimezone=UTC
spring.datasource.username= root
spring.jpa.properties.hibernate.dialect= org.hibernate.dialect.MySQL5Dialect
spring.jpa.hibernate.ddl-auto= update

Tenho que adicionar? ServerTimezone = UTC ao url do banco de dados para evitar um erro.

9 - Crie um esquema em meu banco de dados local chamado teste

10 - Inicie o aplicativo com Spring Boot Dashboard e crie as tabelas 'phone' e 'phone_details' (também outra chamada 'hibernate_sequence')

11- Eu uso o carteiro para mandar um resto para este url http: // localhost: 8080 / telefone / salvar neste corpo

{
  "number": "123-456-7890",
  "details": {
    "provider": "T-Mobile",
    "technology": "GSM"
  }
}

12 - E aqui está o problema. Ele cria uma entrada na tabela do telefone (id, número)

'1', '123-456-7890'

e outro na tabela phone_details (id, provedor, tecnologia, phone_id)

'2', 'T-Mobile', 'GSM', NULL

13 - Claro, se eu postar na url http: // localhost: 8080 / phone / get with body 1 eu recibo

{
    "id": 1,
    "number": "123-456-7890",
    "details": null
}

Espero que alguém possa me ajudar porque tento reduzir o código ao mínimo para seguir a documentação e ainda tenho o problema :-(

A solução dada por @JavaMan é suficiente para o exemplo, mas não para um objeto complexo. Espero que mais alguém possa me ajudar !!!

Respostas

JavaMan Nov 08 2020 at 09:04

No seu PhoneController, você deve adicionar a linha phone.getDetails (). SetPhone (phone); caso contrário, o objeto de detalhes não terá uma referência ao telefone.

Assim assim

@PostMapping("/save")
public ResponseEntity<Boolean> savePhone(@RequestBody Phone phone) {
    phone.getDetails().setPhone(phone); 
    phoneRepository.save(phone);
    return ResponseEntity.ok(true);
}

Além disso, para obter o código em execução, adicione a @JsonIgnoreanotação ao campo Phone na classe PhoneDetails.