infobip / infobip-spring-data-querydsl

Infobip Spring Data Querydsl provides new functionality that enables the user to leverage the full power of Querydsl API on top of Spring Data repository infrastructure.
Apache License 2.0
277 stars 57 forks source link

Projections.constructor cannot be nested when using r2dbc-querydsl associated queries #96

Closed zhoachenIt closed 11 months ago

zhoachenIt commented 1 year ago

r2dbc can't map nested class data when doing associative queries, but it can in jpa mode. Here's my code

<dependency>
    <groupId>io.asyncer</groupId>
    <artifactId>r2dbc-mysql</artifactId>
    <version>0.9.3</version>
</dependency>

<dependency>
    <groupId>com.mysql</groupId>
    <artifactId>mysql-connector-j</artifactId>
    <scope>runtime</scope>
</dependency>

<dependency>
    <groupId>com.infobip</groupId>
    <artifactId>infobip-spring-data-r2dbc-querydsl-boot-starter</artifactId>
    <version>7.2.4</version>
</dependency>

pojo

@Getter
@Setter
public class SysUserDTO {

    private Long id;

    private String username;

    private String name;

    private Long deptId;

    private SysDeptDTO sysDeptDTO;

    public SysUserDTO(Long id, String username, String name, Long deptId) {
        this.id = id;
        this.username = username;
        this.name = name;
        this.deptId = deptId;
    }

    public SysUserDTO(Long id, String username, String name, Long deptId, SysDeptDTO sysDeptDTO) {
        this.id = id;
        this.username = username;
        this.name = name;
        this.deptId = deptId;
        this.sysDeptDTO = sysDeptDTO;
    }

    public SysUserDTO() {
    }
}

@Getter
@Setter
public class SysDeptDTO {

    private String deptName;

    public SysDeptDTO(String deptName) {
        this.deptName = deptName;
    }

    public SysDeptDTO() {
    }

}

repository

public interface UserRepository extends QuerydslR2dbcRepository<SysUser, Long> {

    Mono<SysUser> findByUsername(String username);

}

service

import com.example.webflux.model.SysDeptDTO;
import com.example.webflux.model.SysUser;
import com.example.webflux.model.SysUserDTO;
import com.example.webflux.repository.UserRepository;
import com.querydsl.core.types.Projections;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import static com.example.webflux.model.QSysDept.sysDept;
import static com.example.webflux.model.QSysUser.sysUser;

@Service
@RequiredArgsConstructor
public class UserService {

    private final UserRepository userRepository;

    @Transactional(readOnly = true)
    public Flux<SysUserDTO> findByName(String name) {
        return userRepository.query(query -> query
                .select(
                        Projections.constructor(SysUserDTO.class,
                                sysUser.id,
                                sysUser.name,
                                sysUser.username,
                                sysUser.deptId.as("deptId"),
                                Projections.constructor(SysDeptDTO.class, sysDept.name)
                        )
                )
                .from(sysUser)
                .innerJoin(sysDept)
                .on(sysUser.deptId.eq(sysDept.id))
                .where(sysUser.name.like("%" + name + "%"))
        ).all();
    }

}

controller

@RestController
@RequestMapping("/users")
@RequiredArgsConstructor
public class WebFluxController {

    private final UserService userService;

    @GetMapping("/name/{name}")
    public Flux<SysUserDTO> getName(@PathVariable String name) {
        return this.userService.findByName(name);
    }

}

Request response result, sysDeptDTO is null

[{"id":1,"username":"admin","name":"zhangsan","deptId":1,"sysDeptDTO":null}]

I wrote the same code using jpa mode, but sysDeptDTO successfully assigns the value

[{"id":1,"username":"admin","name":"zhangsan","deptId":1,"sysDeptDTO":{"deptName":"it"}}]
lpandzic commented 11 months ago

Can you please try with latest version of infobip-spring-data-querydsl?

zhoachenIt commented 11 months ago

Can you please try with latest version of infobip-spring-data-querydsl?

Do I have to upgrade springboot3 if using the latest version?

lpandzic commented 11 months ago

Yes, since 8.0.0 SB3 is mandatory.

zhoachenIt commented 11 months ago

Yes, since 8.0.0 SB3 is mandatory.

But our project will be very troublesome to upgrade, so the problems before springboot3.0 will not be maintained?

lpandzic commented 11 months ago

It will be until End of Support for SB2 in November - https://spring.io/projects/spring-boot#support. I'll take a detailed look into the issue when I get some time.

lpandzic commented 11 months ago

Can you try putting @PersistenceCreator on second constructor that has sysDeptDTO property?

zhoachenIt commented 11 months ago

Can you try putting @PersistenceCreator on second constructor that has sysDeptDTO property?

Thanks, we tried @PersistenceCreator and still have the same issues

lpandzic commented 11 months ago

Can you share sql used to create the tables for those entities?

lpandzic commented 11 months ago

I assume from the code that SysUserDTO is Embeddable. https://github.com/spring-projects/spring-data-r2dbc/issues/288 Spring Data R2DBC doesn't support embeddable yet.

lpandzic commented 11 months ago

If this is not an embeddable case feel free to reopen the issue.