简体   繁体   中英

JPA doesn't get OneToMany mapped entity

I made a service which has saving function:

ClientService.java

@Transactional
public ClientDTO signUp(ClientSignupDTO clientSignupDTO) {

    Client client = Client.builder()
            .clientID(clientSignupDTO.getClientID())
            .clientPassword(passwordEncoder.encode(clientSignupDTO.getClientPassword()))
            .clientNickname(clientSignupDTO.getClientNickname())
            .clientActivated(true)
            .build();

    client = clientRepository.save(client);
    clientAuthorityRepository.save(new ClientAuthority(client, authorityRepository.getById("ROLE_CLIENT")));

    client = clientRepository.findOneWithAuthoritiesByClientID(client.getClientID()).orElseThrow(() -> new IllegalStateException("Client does not exist"));

    log.debug("{}", client.getAuthorities()); //This line throws NullPointerException

    return new ClientDTO(client);
}

I saved Client entity and ClientAuthority entity with saved Client 's ID for M:N mapping.
But when I try to find Client in same function, JPA returns Client without ClientAuthority .

Client.java

@Entity
@Table(name = "client")
@Getter
@Setter
@Builder
@NoArgsConstructor
public class Client {

    @Id
    @Column(name = "id")
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(name = "client_id", length = 50)
    private String clientID;

    @Column(name = "client_password")
    private String clientPassword;

    @Column(name = "client_nickname", length = 50)
    private String clientNickname;

    @Column(name = "client_since", nullable = false, updatable = false, insertable = false, columnDefinition = "TIMESTAMP DEFAULT CURRENT_TIMESTAMP")
    private Date clientSince;

    @Column(name = "client_activated")
    private boolean clientActivated;

    @JsonIgnore
    @OneToMany(mappedBy = "client")
    private Set<ClientAuthority> clientAuthorities;

    @JsonProperty("authorities")
    public Set<Authority> getAuthorities() {
        return clientAuthorities.stream().map(ClientAuthority::getAuthority).collect(Collectors.toSet());
    }

    public Client(Long id, String clientID, String clientPassword, String clientNickname, Date clientSince, boolean clientActivated, Set<ClientAuthority> clientAuthorities) {
        this.id = id;
        this.clientID = clientID;
        this.clientPassword = clientPassword;
        this.clientNickname = clientNickname;
        this.clientSince = clientSince;
        this.clientActivated = clientActivated;
        this.clientAuthorities = clientAuthorities;
    }
}

ClientAuthority.java

@Entity
@Table(name = "client_authority")
@Getter
@Setter
@Builder
@NoArgsConstructor
public class ClientAuthority {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "client_authority_id")
    private Long clientAuthorityID;

    @ManyToOne
    @JoinColumn(name = "client_id")
    private Client client;

    @ManyToOne
    @JoinColumn(name = "authority_name")
    private Authority authority;

    public ClientAuthority(Long clientAuthorityID, Client client, Authority authority) {
        this.clientAuthorityID = clientAuthorityID;
        this.client = client;
        this.authority = authority;
    }

    public ClientAuthority(Client client, Authority authority) {
        this.client = client;
        this.authority = authority;
    }
}

Repository that I used to find client is Spring Data JPA repository.

ClientRepository.java

@Repository
public interface ClientRepository extends JpaRepository<Client, Long> {
    Optional<Client> findOneWithAuthoritiesByClientID(String clientID);
}

Since clientAuthorities inside Client that returned by findOneWithAuthoritiesByClientID is null, Client.getAuthorities returns NullPointerException .
Other properties such as clientID , clientNickname , etc. works fine but only clientAuthorities is null.

How can I get Client with Set<ClientAuthority> after save them?

Thanks.


Edit #1

Edited all save into saveAndFlush in function signUp . But it keeps throwing NullPointerException .

@Transactional
public ClientDTO signUp(ClientSignupDTO clientSignupDTO) {

    Client client = clientRepository.saveAndFlush(Client.builder()
            .clientID(clientSignupDTO.getClientID())
            .clientPassword(passwordEncoder.encode(clientSignupDTO.getClientPassword()))
            .clientNickname(clientSignupDTO.getClientNickname())
            .clientActivated(true)
            .build());

    log.debug("{}, {}, {}, {}", client.getId(), client.getClientID(), client.getClientNickname(), client.getClientSince());

    clientAuthorityRepository.saveAndFlush(new ClientAuthority(client, authorityRepository.getById("ROLE_CLIENT")));

    client = clientRepository.findOneWithAuthoritiesByClientID(client.getClientID()).orElseThrow(() -> new IllegalStateException("Client does not exist"));

    log.debug("{}, {}, {}, {}", client.getClientID(), client.getClientNickname(), client.getClientSince(), client.getAuthorities());

    return new ClientDTO(client);
}

The log is:

Hibernate: insert into client (client_activated, client_id, client_nickname, client_password) values (?, ?, ?, ?)
2022-02-25 14:22:07.986 DEBUG 16684 --- [nio-8080-exec-3] c.d.s.service.ClientService              : 4, test, test, null
Hibernate: insert into client_authority (authority_name, client_id) values (?, ?)
Hibernate: select client0_.id as id1_1_, client0_.client_activated as client_a2_1_, client0_.client_id as client_i3_1_, client0_.client_nickname as client_n4_1_, client0_.client_password as client_p5_1_, client0_.client_since as client_s6_1_ from client client0_ where client0_.client_id=?
2022-02-25 14:22:08.071 ERROR 16684 --- [nio-8080-exec-3] o.a.c.c.C.[.[.[/].[dispatcherServlet]    : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is java.lang.NullPointerException] with root cause

java.lang.NullPointerException: null
    at com.devjaewoo.springtest.entity.Client.getAuthorities(Client.java:46) ~[classes/:na]

When I test findOneWithAuthoritiesByClientID function with sample data (with data.sql file) outside of signUp function, It works fine.

Why doesn't it work properly only inside of signUp function?

you may need to saveAndFlush

but I would recommend

final var client = clientRepository
  .saveAndFlush(Client
    .builder()
    .clientID(clientSignupDTO.getClientID())
    ...
    .build());

So you immediately get the managed copy.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM