高级查询
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 来自动解析为条件,进行数据查询,点击下一页了解