Skip to content

高级查询

huiche的dao支持使用Consumer<Select> customizer参数,来自定义查询,来完成一些稍微高级的查询操作,如关联表,分组等

首先定义部分实体类和EntityDao,用于后面演示

java
// 歌曲
public record Song(Long id,String title,Long duration,Long artistId,Long albumId,Integer releaseYear,String lyrics){}
// 艺人
public record Artist(Long id,String name){}
// 专辑
public record Album(Long id,String title){}

// 歌曲DAO
@Repository
public class SongDao EntityDao<Song> {}

查询多个指定列为VO

指定查询的列, 不再是*

java

@Autowired
private SongDao dao;

public List<Song> helloworld() {
    return dao.list(select->select.select(Song::id,Song::title));
}

实际执行的SQL

sql
SELECT song.id,song.title FROM song

关联查询为VO

java
// 查询VO,包含歌曲id,歌名,歌手
public record SongVO(Long id,String title,String artistName){}

@Autowired
private SongDao dao;

public List<SongVO> helloworld() {
    return dao.listAs(SongVO.class,select->select
        // 使用lambda时,,受制于java语法规则,同一select内,只能选择同一类下的列
        .select(Song::id,Song::title)
        // 如果想要select同时选择多个类的列,且想使用方法引用,可以使用Column.of() 包裹
        // .select(Column.of(Song::id),Column.of(Song::title),Column.of(Artist::name).as("artist"))
        // 也可多次进行select,累加到实际SQL
        .select(Column.of(Artist::name).as("artistName"))
        .join(Artist.class)
        .on(Column.of(Song::artistId).eq(Column.of(Artist::id)))
    );
}

实际执行的SQL

sql
SELECT song.id,song.title,artist.name AS artist_name FROM song JOIN artist ON song.astistId = artist.id

分组查询

java
// 查询VO,包含歌手id,歌手名称,歌曲数量
public record ArtistSongStatVO(Long id,String name,Long songCount){}

@Autowired
private SongDao dao;

public List<SongVO> helloworld() {
    // 查询所有歌手的歌曲数量并按数量排序
    return dao.listAs(ArtistSongStatVO.class,select->select
        .select(Artist::id,Artist::name)
        .select(Sql.count().as("songCount"))
        .join(Artist.class)
        .on(Column.of(Song::artistId).eq(Column.of(Artist::id)))
        .groupBy(Song::artistId)
        .orderBy(Column.of("songCount").desc()) //orderBy中可以直接使用selections中的别名
    );
}

实际执行的SQL

sql
SELECT artist.id, artist.name,count(*) AS song_count
FROM song
JOIN artist ON song.astistId = artist.id
GROUP BY song.astistId
ORDER BY song_count DESC

同名字段处理

你可能注意到, 在上方示例用,多表关联查询时,使用了SQL别名来跟VO中的属性匹配,其实还可以使用@Ref 也可以帮你处理这种情况

java
// 查询VO,包含歌曲id,歌名,专辑名,歌手
public record SongVO(Long id,
    @Ref(entity=Song.class) String title,
    @Ref(entity=Album.class,field="title") String albumTitle,
    @Ref(entity=Artist.class,field="name") String artistName){}

@Autowired
private SongDao dao;

public List<SongVO> helloworld() {
    return dao.listAs(SongVO.class,select->select
        .select(Song::id,Song::title)
        .select(Album::title)
        .select(Artist::name)
        .join(Album.class)
        .on(Column.of(Song::albumId).eq(Column.of(Album::id)))
        .join(Artist.class)
        .on(Column.of(Song::artistId).eq(Column.of(Artist::id)))
    );
}

实际执行的SQL

sql
SELECT song.id,song.title,album.title,artist.name
FROM song
JOIN album on song.album_id = album.id
JOIN artist on song.artist_id = artist.id

嵌套查询

有时可能希望返回的数据是对象化的, 而不是扁平的, 这时候可以利用嵌套查询来实现

java
// 查询VO,包含歌曲id,歌名,专辑名,歌手
public record SongVO(
    Long id, // 歌曲ID
    String title, // 歌名
    @Ref(nested=true)Album album, // 所属专辑
    @Ref(nested=true)Artist artist // 所属艺人
    ){}

@Autowired
private SongDao dao;

public List<SongVO> helloworld() {
    return dao.listAs(SongVO.class,select->select
        .select(Song::id,Song::title)
        .select(Album.class)//select传入Entity.class等价于table.*
        .select(Artist.class)
        .join(Album.class)
        .on(Column.of(Song::albumId).eq(Column.of(Album::id)))
        .join(Artist.class)
        .on(Column.of(Song::artistId).eq(Column.of(Artist::id)))
    );
}

实际执行的SQL

sql
SELECT song.id,song.title,album.*,artist.*
FROM song
JOIN album on song.album_id = album.id
JOIN artist on song.artist_id = artist.id

以上示例, album和artist是嵌套的,而歌曲id和歌名还是分别读取的字段,也可以全部嵌套查询

java
// 查询VO,包含歌曲id,歌名,专辑名,歌手
public record SongVO(
    @Ref(nested=true)Song song, // 歌曲
    @Ref(nested=true)Album album, // 所属专辑
    @Ref(nested=true)Artist artist // 所属艺人
    ){}

@Autowired
private SongDao dao;

public List<SongVO> helloworld() {
    return dao.listAs(SongVO.class,select->select
        // Song.class不能忽略,因为只要在Consumer中select了字段,便不会默认进行'*'的选择
        // 如果没有调用select(),dao语句会在最终执行时,添加SELECT table.*,比如SongDao会是 SELECT song.*
        .select(Song.class,Album.class,Artist.class)
        .join(Album.class)
        .on(Column.of(Song::albumId).eq(Column.of(Album::id)))
        .join(Artist.class)
        .on(Column.of(Song::artistId).eq(Column.of(Artist::id)))
    );
}

实际执行的SQL

sql
SELECT song.*,album.*,artist.*
FROM song
JOIN album on song.album_id = album.id
JOIN artist on song.artist_id = artist.id

如果dao的高级查询仍然满足不了你的需求,你可以使用自定义查询,使用JdbcExecutor来进行查询操作,了解自定义查询

你可能注意到SELECT的列(selection)支持多种传入方式,了解查询字段

有时候需要把一些查询条件包装为查询对象使用,方便前端进行传递,在dao的查询类方法中,也允许通过传一个SearchObject 来自动解析为条件,进行数据查询,点击下一页了解