@@ -2,27 +2,36 @@ package com.lanyuanxiaoyao.service.template.database.jpa;
import com.blinkfox.fenix.EnableFenix ;
import com.lanyuanxiaoyao.service.template.database.common.test.AbstractTestApplication ;
import com.lanyuanxiaoyao.service.template.database.common.test.entity.Industry ;
import com.lanyuanxiaoyao.service.template.database.common.test.entity.Level ;
import com.lanyuanxiaoyao.service.template.database.jpa.entity.Address ;
import com.lanyuanxiaoyao.service.template.database.jpa.entity.Address_ ;
import com.lanyuanxiaoyao.service.template.database.jpa.entity.Company ;
import com.lanyuanxiaoyao.service.template.database.jpa.entity.Company_ ;
import com.lanyuanxiaoyao.service.template.database.jpa.entity.Employee ;
import com.lanyuanxiaoyao.service.template.database.jpa.entity.Employee_ ;
import com.lanyuanxiaoyao.service.template.database.jpa.entity.QEmployee ;
import com.lanyuanxiaoyao.service.template.database.jpa.entity.Report ;
import com.lanyuanxiaoyao.service.template.database.jpa.entity.Skill ;
import com.lanyuanxiaoyao.service.template.database.jpa.entity.Skill_ ;
import com.lanyuanxiaoyao.service.template.database.jpa.repository.CompanyRepository ;
import com.lanyuanxiaoyao.service.template.database.jpa.repository.EmployeeRepository ;
import com.lanyuanxiaoyao.service.template.database.jpa.repository.ReportRepository ;
import com.querydsl.core.types.dsl.CaseBuilder ;
import com.querydsl.jpa.impl.JPAQueryFactory ;
import java.math.BigDecimal ;
import java.util.List ;
import java.util.Map ;
import java.util.Set ;
import lombok.RequiredArgsConstructor ;
import lombok.extern.slf4j.Slf4j ;
import org.hibernate.Session ;
import org.springframework.boot.SpringApplication ;
import org.springframework.boot.autoconfigure.SpringBootApplication ;
import org.springframework.boot.context.event.ApplicationReadyEvent ;
import org.springframework.context.event.EventListener ;
import org.springframework.data.domain.PageRequest ;
import org.springframework.data.domain.Sort ;
import org.springframework.data.jpa.domain.Specification ;
import org.springframework.data.jpa.repository.config.EnableJpaAuditing ;
import org.springframework.transaction.annotation.Transactional ;
import org.springframework.util.Assert ;
@Slf4j
@@ -30,10 +39,12 @@ import org.springframework.util.Assert;
@SpringBootApplication
@EnableFenix
@EnableJpaAuditing
@Transactional
public class TestApplication extends AbstractTestApplication {
private final CompanyRepository companyRepository ;
private final EmployeeRepository employeeRepository ;
private final ReportRepository reportRepository ;
private final Session session ;
public static void main ( String [ ] args ) {
SpringApplication . run ( TestApplication . class , args ) ;
@@ -43,7 +54,7 @@ public class TestApplication extends AbstractTestApplication {
public void runTests ( ) {
testCrud ( ) ;
testDelete ( ) ;
testSpecification ( ) ;
testQuery ( ) ;
testNative ( ) ;
System . exit ( 0 ) ;
@@ -76,110 +87,825 @@ public class TestApplication extends AbstractTestApplication {
companyRepository . deleteBatchByIds ( List . of ( cid1 , cid2 ) ) ;
}
private void testSpecification ( ) {
formatLog ( " Added " ) ;
var company1 = companyRepository . save ( Company . builder ( ) . name ( randomString ( 5 ) ) . members ( randomInt ( 100 ) ) . industries ( Set . of ( Industry . MEDIA , Industry . SERVICE ) ) . build ( ) ) ;
var company2 = companyRepository . save ( Company . builder ( ) . name ( randomString ( 5 ) ) . members ( randomInt ( 100 ) ) . industries ( Set . of ( Industry . MEDIA , Industry . SERVICE ) ) . build ( ) ) ;
var employee1 = employeeRepository . save (
Employee . builder ( )
. name ( " Tom " )
. age ( randomInt ( 100 ) )
. role ( Employee . Role . USER )
. company ( company1 )
. connections ( Map . of (
Employee . ConnectionType . ADDRESS , randomString ( 50 ) ,
Employee . ConnectionType . EMAIL , randomString ( 20 )
) )
. build ( )
) ;
var employee2 = employeeRepository . save (
Employee . builder ( )
. name ( randomString ( 10 ) )
. age ( randomInt ( 100 ) )
. role ( Employee . Role . USER )
. company ( company2 )
. connections ( Map . of (
Employee . ConnectionType . ADDRESS , randomString ( 50 ) ,
Employee . ConnectionType . EMAIL , randomString ( 20 )
) )
. build ( )
) ;
var report1 = reportRepository . save ( Report . builder ( ) . score ( randomDouble ( 50 ) ) . level ( Level . B ) . employeeId ( employee1 . getId ( ) ) . build ( ) ) ;
var report2 = reportRepository . save ( Report . builder ( ) . score ( randomDouble ( 50 ) ) . level ( Level . E ) . employeeId ( employee2 . getId ( ) ) . build ( ) ) ;
private void testQuery ( ) {
var factory = new JPAQueryFactory ( session ) ;
formatLog ( " Query " ) ;
var employees 1 = employee Repository. findAll (
builder - > builder
. andIsNotNull ( Employee . Fields . name )
. andEquals ( Employee . Fields . name , " Tom " )
. andLike ( Employee . Fields . name , " To " )
. andStartsWith ( Employee . Fields . name , " To " )
. andEndsWith ( Employee . Fields . name , " om " )
. andLessThan ( Employee . Fields . age , 200 )
. andGreaterThanEqual ( Employee . Fields . age , 0 )
. andIn ( Employee . Fields . name , List . of ( " Tom " , " Mike " ) )
. andBetween ( Employee . Fields . age , 0 , 200 )
formatLog ( " 准备 Specification 查询的测试数据 " ) ;
var company 1 = company Repository. save ( Company . builder ( ) . name ( " TechCorp " ) . members ( 100 ) . build ( ) ) ;
var company2 = companyRepository . save ( Company . builder ( ) . name ( " DataInc " ) . members ( 50 ) . build ( ) ) ;
var company3 = companyRepository . save ( Company . builder ( ) . name ( " CloudSys " ) . members ( 150 ) . build ( ) ) ;
// 准备 Skills 数据
var skill1 = Skill . builder ( ) . name ( " Java " ) . description ( " Java 编程语言 " ) . build ( ) ;
var skill2 = Skill . builder ( ) . name ( " Python " ) . description ( " Python 编程语言 " ) . build ( ) ;
var skill3 = Skill . builder ( ) . name ( " Spring " ) . description ( " Spring 框架 " ) . build ( ) ;
var skill4 = Skill . builder ( ) . name ( " MySQL " ) . description ( " MySQL 数据库 " ) . build ( ) ;
employeeRepository . save ( Employee . builder ( )
. name ( " Alice " ) . age ( 30 ) . role ( Employee . Role . ADMIN ) . code ( " E001 " )
. salary ( new BigDecimal ( " 50000.00 " ) ) . bonus ( new BigDecimal ( " 5000.00 " ) )
. active ( true ) . company ( company1 )
. address ( Address . builder ( ) . street ( " Main St " ) . city ( " Beijing " ) . state ( " Beijing " ) . zipCode ( " 100000 " ) . country ( " China " ) . build ( ) )
. skills ( Set . of ( skill1 , skill3 ) )
. hobbies ( List . of ( " Reading " , " Swimming " ) )
. properties ( Map . of ( " department " , " Engineering " , " level " , " Senior " ) )
. connections ( Map . of ( Employee . ConnectionType . EMAIL , " alice@example.com " ) )
. build ( ) ) ;
employeeRepository . save ( Employee . builder ( )
. name ( " Bob " ) . age ( 25 ) . role ( Employee . Role . USER ) . code ( " E002 " )
. salary ( new BigDecimal ( " 40000.00 " ) ) . bonus ( new BigDecimal ( " 4000.00 " ) )
. active ( true ) . company ( company2 )
. address ( Address . builder ( ) . street ( " Oak Ave " ) . city ( " Shanghai " ) . state ( " Shanghai " ) . zipCode ( " 200000 " ) . country ( " China " ) . build ( ) )
. skills ( Set . of ( skill2 ) )
. hobbies ( List . of ( " Gaming " ) )
. properties ( Map . of ( " department " , " Marketing " , " level " , " Junior " ) )
. connections ( Map . of ( Employee . ConnectionType . PHONE , " 1234567890 " ) )
. build ( ) ) ;
employeeRepository . save ( Employee . builder ( )
. name ( " Charlie " ) . age ( 35 ) . role ( Employee . Role . ADMIN ) . code ( " E003 " )
. salary ( new BigDecimal ( " 60000.00 " ) ) . bonus ( new BigDecimal ( " 6000.00 " ) )
. active ( false ) . company ( company1 )
. address ( Address . builder ( ) . street ( " Pine Rd " ) . city ( " Shenzhen " ) . state ( " Guangdong " ) . zipCode ( " 518000 " ) . country ( " China " ) . build ( ) )
. skills ( Set . of ( skill1 , skill2 , skill3 ) )
. hobbies ( List . of ( " Reading " , " Gaming " , " Music " ) )
. properties ( Map . of ( " department " , " Engineering " , " level " , " Lead " ) )
. connections ( Map . of ( Employee . ConnectionType . EMAIL , " charlie@example.com " , Employee . ConnectionType . PHONE , " 0987654321 " ) )
. build ( ) ) ;
employeeRepository . save ( Employee . builder ( )
. name ( " David " ) . age ( 28 ) . role ( Employee . Role . USER ) . code ( " E004 " )
. salary ( new BigDecimal ( " 45000.00 " ) ) . bonus ( null )
. active ( true ) . company ( company3 )
. address ( Address . builder ( ) . street ( " Elm Ln " ) . city ( " Guangzhou " ) . state ( " Guangdong " ) . zipCode ( " 510000 " ) . country ( " China " ) . build ( ) )
. skills ( Set . of ( skill4 ) )
. hobbies ( List . of ( ) )
. properties ( Map . of ( ) )
. connections ( Map . of ( ) )
. build ( ) ) ;
employeeRepository . save ( Employee . builder ( )
. name ( " Alice Smith " ) . age ( 32 ) . role ( Employee . Role . USER ) . code ( " E005 " )
. salary ( new BigDecimal ( " 55000.00 " ) ) . bonus ( new BigDecimal ( " 5500.00 " ) )
. active ( true ) . company ( company2 )
. address ( Address . builder ( ) . street ( " Maple Dr " ) . city ( " Hangzhou " ) . state ( " Zhejiang " ) . zipCode ( " 310000 " ) . country ( " China " ) . build ( ) )
. skills ( Set . of ( skill1 , skill4 ) )
. hobbies ( List . of ( " Swimming " , " Music " ) )
. properties ( Map . of ( " department " , " Sales " , " level " , " Middle " ) )
. connections ( Map . of ( Employee . ConnectionType . EMAIL , " alicesmith@example.com " ) )
. build ( ) ) ;
formatLog ( " 1. 基本比较操作符查询 JPA " ) ;
// 查找姓名为"Bob"、角色不是ADMIN、年龄在20-30之间、薪资在40000-45000之间的员工
var result1_jpa = employeeRepository . findAll ( ( root , query , cb ) - > cb . and (
cb . equal ( root . get ( Employee_ . name ) , " Bob " ) ,
cb . notEqual ( root . get ( Employee_ . role ) , Employee . Role . ADMIN ) ,
cb . greaterThan ( root . get ( Employee_ . age ) , 20 ) ,
cb . lessThan ( root . get ( Employee_ . age ) , 30 ) ,
cb . greaterThanOrEqualTo ( root . get ( Employee_ . salary ) , new BigDecimal ( " 40000.00 " ) ) ,
cb . lessThanOrEqualTo ( root . get ( Employee_ . salary ) , new BigDecimal ( " 45000.00 " ) )
) ) ;
Assert . isTrue ( result1_jpa . size ( ) = = 1 , " 基本比较操作符查询失败 ( %d) " . formatted ( result1_jpa . size ( ) ) ) ;
formatLog ( " 1. 基本比较操作符查询 Fenix " ) ;
var result1_fenix = employeeRepository . findAll (
builder - > builder . andEquals ( Employee . Fields . name , " Bob " )
. andNotEquals ( Employee . Fields . role , Employee . Role . ADMIN )
. andGreaterThan ( Employee . Fields . age , 20 )
. andLessThan ( Employee . Fields . age , 30 )
. andGreaterThanEqual ( Employee . Fields . salary , new BigDecimal ( " 40000.00 " ) )
. andLessThanEqual ( Employee . Fields . salary , new BigDecimal ( " 45000.00 " ) )
. build ( )
) ;
Assert . isTrue ( employees1 . size ( ) = = 1 , " 查询数量错误 " ) ;
Assert . isTrue ( result1_fenix . size ( ) = = 1 , " 基本比较操作符查询失败 ( %d) " . formatted ( result1_fenix . size ( ) ) ) ;
var employees2 = employeeRepository . findAll (
( root , query , builder ) - >
builder . and (
builder . isNotNull ( root . get ( Employee_ . name ) ) ,
builder . equal ( root . get ( E mployee_ . name ) , " Tom " ) ,
builder . like ( root . get ( E mployee_ . name ) , " To% " ) ,
builder . lessTh an ( root . get ( E mployee_ . age ) , 200 ) ,
builder . greaterThanOrEqualTo ( root . get ( E mployee_ . age ) , 0 ) ,
builder . in ( root . get ( Employee_ . NAME ) ) . value ( List . of ( " Tom " , " Mike " ) ) ,
build er . between ( root . get ( Employee_ . age ) , 0 , 200 ) ,
builder . isNotEmpty ( root . get ( Employee_ . company ) . get ( Company_ . employees ) ) ,
builder . isMember ( Industry . MEDIA , root . get ( Employee_ . company ) . get ( Company_ . industries ) )
formatLog ( " 1. 基本比较操作符查询 QueryDSL " ) ;
var result1_querydsl = employeeRepository . findAll (
QEmployee . employee . name . eq ( " Bob " )
. and ( QEmployee . employee . role . ne ( Employee . Role . ADMIN ) )
. and ( QEmployee . e mployee . age . gt ( 20 ) )
. and ( QEmployee . e mployee. age . lt ( 30 ) )
. and ( QEmployee . e mployee. salary . goe ( new BigDecimal ( " 40000.00 " ) ) )
. and ( QEmployee . e mployee . salary . loe ( new BigDecimal ( " 45000.00 " ) ) )
) ;
Ass ert . isTrue ( result1_querydsl . size ( ) = = 1 , " 基本比较操作符查询失败 ( %d) " . formatted ( result1_querydsl . size ( ) ) ) ;
formatLog ( " 1. 基本比较操作符查询 HQL " ) ;
var result1_hql = session . createQuery (
"""
from Employee employee
where employee.name = 'Bob'
and employee.role != com.lanyuanxiaoyao.service.template.database.jpa.entity.Employee.Role.ADMIN
and employee.age > 20
and employee.age < 30
and employee.salary >= 40000.00
and employee.salary <= 45000.00
""" ,
Employee . class
) . list ( ) ;
Assert . isTrue ( result1_hql . size ( ) = = 1 , " 基本比较操作符查询失败 ( %d) " . formatted ( result1_hql . size ( ) ) ) ;
formatLog ( " 2. 区间和集合操作符查询 JPA " ) ;
// 查找年龄在25-30之间、年龄不在40-50之间、年龄在25/30/35中、角色是USER或ADMIN、姓名不在Charlie/David中的员工
var result2_jpa = employeeRepository . findAll ( ( root , query , cb ) - > cb . and (
cb . between ( root . get ( Employee_ . age ) , 25 , 30 ) ,
cb . between ( root . get ( Employee_ . age ) , 40 , 50 ) . not ( ) ,
root . get ( Employee_ . age ) . in ( 25 , 30 , 35 ) ,
root . get ( Employee_ . role ) . in ( Employee . Role . USER , Employee . Role . ADMIN ) ,
cb . not ( root . get ( Employee_ . name ) . in ( " Charlie " , " David " ) ) ,
root . get ( Employee_ . name ) . in ( " Charlie " , " David " ) . not ( )
) ) ;
Assert . isTrue ( result2_jpa . size ( ) = = 2 , " 区间和集合操作符查询失败 ( %d) " . formatted ( result2_jpa . size ( ) ) ) ;
formatLog ( " 2. 区间和集合操作符查询 Fenix " ) ;
var result2_fenix = employeeRepository . findAll (
builder - > builder . andBetween ( Employee . Fields . age , 25 , 30 )
. andNotBetween ( Employee . Fields . age , 40 , 50 )
. andIn ( Employee . Fields . age , List . of ( 25 , 30 , 35 ) )
. andIn ( Employee . Fields . role , List . of ( Employee . Role . USER , Employee . Role . ADMIN ) )
. andNotIn ( Employee . Fields . name , List . of ( " Charlie " , " David " ) )
. build ( )
) ;
Assert . isTrue ( result2_fenix . size ( ) = = 2 , " 区间和集合操作符查询失败 ( %d) " . formatted ( result2_fenix . size ( ) ) ) ;
formatLog ( " 2. 区间和集合操作符查询 QueryDSL " ) ;
var result2_querydsl = employeeRepository . findAll (
QEmployee . employee . age . between ( 25 , 30 )
. and ( QEmployee . employee . age . between ( 40 , 50 ) . not ( ) )
. and ( QEmployee . employee . age . in ( 25 , 30 , 35 ) )
. and ( QEmployee . employee . role . in ( Employee . Role . USER , Employee . Role . ADMIN ) )
. and ( QEmployee . employee . name . in ( " Charlie " , " David " ) . not ( ) )
. and ( QEmployee . employee . name . in ( List . of ( " Charlie " , " David " ) ) . not ( ) )
) ;
Assert . isTrue ( result2_querydsl . size ( ) = = 2 , " 区间和集合操作符查询失败 ( %d) " . formatted ( result2_querydsl . size ( ) ) ) ;
formatLog ( " 2. 区间和集合操作符查询 HQL " ) ;
var result2_hql = session . createQuery (
"""
from Employee employee
where employee.age between 25 and 30
and not (employee.age between 40 and 50)
and employee.age in (25, 30, 35)
and employee.role in (com.lanyuanxiaoyao.service.template.database.jpa.entity.Employee.Role.USER, com.lanyuanxiaoyao.service.template.database.jpa.entity.Employee.Role.ADMIN)
and employee.name not in ('Charlie', 'David')
""" ,
Employee . class
) . list ( ) ;
Assert . isTrue ( result2_hql . size ( ) = = 2 , " 区间和集合操作符查询失败 ( %d) " . formatted ( result2_hql . size ( ) ) ) ;
formatLog ( " 3. 字符串操作符查询 JPA " ) ;
// 查找以A开头、不以C开头、包含"ali"( 忽略大小写) 、名称长度在4-10之间、前3个字符为"Ali"的员工
var result3_jpa = employeeRepository . findAll ( ( root , query , cb ) - > cb . and (
cb . like ( root . get ( Employee_ . name ) , " A% " ) ,
cb . notLike ( root . get ( Employee_ . name ) , " C% " ) ,
cb . like ( cb . lower ( root . get ( Employee_ . name ) ) , " %ali% " ) ,
cb . like ( cb . upper ( root . get ( Employee_ . name ) ) , " %ALI% " ) ,
cb . greaterThan ( cb . length ( root . get ( Employee_ . name ) ) , 4 ) ,
cb . lessThanOrEqualTo ( cb . length ( root . get ( Employee_ . name ) ) , 10 ) ,
cb . equal ( cb . substring ( root . get ( Employee_ . name ) , 0 , 3 ) , " Ali " )
) ) ;
Assert . isTrue ( result3_jpa . size ( ) = = 1 , " 字符串操作符查询失败 ( %d) " . formatted ( result3_jpa . size ( ) ) ) ;
formatLog ( " 3. 字符串操作符查询 Fenix " ) ;
log . info ( " Fenix框架当前版本不支持以下字符串操作符: " ) ;
log . info ( " - cb.length() - 字符串长度函数 " ) ;
log . info ( " - cb.substring() - 子字符串提取函数 " ) ;
log . info ( " - cb.lower() / cb.upper() - 大小写转换函数 (不支持直接在条件中使用) " ) ;
log . info ( " Fenix支持的部分实现: " ) ;
var result3_fenix = employeeRepository . findAll (
builder - > builder . andStartsWith ( Employee . Fields . name , " A " )
. andNotStartsWith ( Employee . Fields . name , " C " )
. build ( )
) ;
log . info ( " Fenix查询结果: {} 条记录(仅支持部分条件) " , result3_fenix . size ( ) ) ;
formatLog ( " 3. 字符串操作符查询 QueryDSL " ) ;
var result3_querydsl = employeeRepository . findAll (
QEmployee . employee . name . startsWith ( " A " )
. and ( QEmployee . employee . name . startsWith ( " C " ) . not ( ) )
. and ( QEmployee . employee . name . toLowerCase ( ) . contains ( " ali " ) )
. and ( QEmployee . employee . name . toUpperCase ( ) . contains ( " ALI " ) )
. and ( QEmployee . employee . name . length ( ) . gt ( 4 ) )
. and ( QEmployee . employee . name . length ( ) . loe ( 10 ) )
. and ( QEmployee . employee . name . substring ( 0 , 3 ) . eq ( " Ali " ) )
) ;
Assert . isTrue ( result3_querydsl . size ( ) = = 1 , " 字符串操作符查询失败 ( %d) " . formatted ( result3_querydsl . size ( ) ) ) ;
formatLog ( " 3. 字符串操作符查询 HQL " ) ;
var result3_hql = session . createQuery (
"""
from Employee employee
where employee.name like 'A%'
and employee.name not like 'C%'
and lower(employee.name) like '%ali%'
and upper(employee.name) like '%ALI%'
and length(employee.name) > 4
and length(employee.name) <= 10
and substring(employee.name, 1, 3) = 'Ali'
""" ,
Employee . class
) . list ( ) ;
Assert . isTrue ( result3_hql . size ( ) = = 1 , " 字符串操作符查询失败 ( %d) " . formatted ( result3_hql . size ( ) ) ) ;
formatLog ( " 4. NULL 和布尔操作符查询 JPA " ) ;
// 查找激活状态为true、bonus不为null、code不在E999中的员工
var result4_jpa = employeeRepository . findAll ( ( root , query , cb ) - > cb . and (
cb . isTrue ( root . get ( Employee_ . active ) ) ,
cb . isNotNull ( root . get ( Employee_ . bonus ) ) ,
cb . not ( root . get ( Employee_ . code ) . in ( " E999 " ) )
) ) ;
Assert . isTrue ( ! result4_jpa . isEmpty ( ) , " NULL 和布尔操作符查询失败 ( %d) " . formatted ( result4_jpa . size ( ) ) ) ;
formatLog ( " 4. NULL 和布尔操作符查询 Fenix " ) ;
log . info ( " Fenix框架当前版本不支持以下布尔操作符: " ) ;
log . info ( " - cb.isTrue() / cb.isFalse() - 专用布尔判断函数 " ) ;
log . info ( " Fenix通过equals/notEquals处理布尔字段 " ) ;
var result4_fenix = employeeRepository . findAll (
builder - > builder . andEquals ( Employee . Fields . active , true )
. andIsNotNull ( Employee . Fields . bonus )
. andNotIn ( Employee . Fields . code , List . of ( " E999 " ) )
. build ( )
) ;
Assert . isTrue ( ! result4_fenix . isEmpty ( ) , " NULL 和布尔操作符查询失败 ( %d) " . formatted ( result4_fenix . size ( ) ) ) ;
formatLog ( " 4. NULL 和布尔操作符查询 QueryDSL " ) ;
var result4_querydsl = employeeRepository . findAll (
QEmployee . employee . active . isTrue ( )
. and ( QEmployee . employee . bonus . isNotNull ( ) )
. and ( QEmployee . employee . code . in ( " E999 " ) . not ( ) )
) ;
Assert . isTrue ( ! result4_querydsl . isEmpty ( ) , " NULL 和布尔操作符查询失败 ( %d) " . formatted ( result4_querydsl . size ( ) ) ) ;
formatLog ( " 4. NULL 和布尔操作符查询 HQL " ) ;
var result4_hql = session . createQuery (
"""
from Employee employee
where employee.active is true
and employee.bonus is not null
and employee.code not in ('E999')
""" ,
Employee . class
) . list ( ) ;
Assert . isTrue ( ! result4_hql . isEmpty ( ) , " NULL 和布尔操作符查询失败 ( %d) " . formatted ( result4_hql . size ( ) ) ) ;
formatLog ( " 5. 集合操作符查询 JPA " ) ;
// 查找技能集合非空、爱好集合非空、包含"Reading"爱好、不包含"Riding"爱好、爱好数量大于1、技能数量小于4的员工
var result5_jpa = employeeRepository . findAll ( ( root , query , cb ) - > cb . and (
cb . isNotEmpty ( root . get ( Employee_ . skills ) ) ,
cb . isEmpty ( root . get ( Employee_ . hobbies ) ) . not ( ) ,
cb . isMember ( " Reading " , root . get ( Employee_ . hobbies ) ) ,
cb . isNotMember ( " Riding " , root . get ( Employee_ . hobbies ) ) ,
cb . greaterThan ( cb . size ( root . get ( Employee_ . hobbies ) ) , 1 ) ,
cb . lessThan ( cb . size ( root . get ( Employee_ . skills ) ) , 4 )
) ) ;
Assert . isTrue ( result5_jpa . size ( ) = = 2 , " 集合操作符查询失败 ( %d) " . formatted ( result5_jpa . size ( ) ) ) ;
formatLog ( " 5. 集合操作符查询 Fenix " ) ;
log . info ( " Fenix框架当前版本不支持以下集合操作符: " ) ;
log . info ( " - cb.isNotEmpty() / cb.isEmpty() - 集合非空/空判断 " ) ;
log . info ( " - cb.isMember() / cb.isNotMember() - 集合成员判断 " ) ;
log . info ( " - cb.size() - 集合大小函数 " ) ;
log . info ( " 这些集合操作在JPA Criteria中需要复杂的join处理, Fenix当前不支持 " ) ;
formatLog ( " 5. 集合操作符查询 QueryDSL " ) ;
var result5_querydsl = employeeRepository . findAll (
QEmployee . employee . skills . isNotEmpty ( )
. and ( QEmployee . employee . hobbies . isNotEmpty ( ) )
. and ( QEmployee . employee . hobbies . contains ( " Reading " ) )
. and ( QEmployee . employee . hobbies . contains ( " Riding " ) . not ( ) )
. and ( QEmployee . employee . hobbies . size ( ) . gt ( 1 ) )
. and ( QEmployee . employee . skills . size ( ) . lt ( 4 ) )
) ;
Assert . isTrue ( result5_querydsl . size ( ) = = 2 , " 集合操作符查询失败 ( %d) " . formatted ( result5_querydsl . size ( ) ) ) ;
formatLog ( " 5. 集合操作符查询 HQL " ) ;
var result5_hql = session . createQuery (
"""
from Employee employee
where employee.skills is not empty
and employee.hobbies is not empty
and 'Reading' member of employee.hobbies
and 'Riding' not member of employee.hobbies
and size(employee.hobbies) > 1
and size(employee.skills) < 4
""" ,
Employee . class
) . list ( ) ;
Assert . isTrue ( result5_hql . size ( ) = = 2 , " 集合操作符查询失败 ( %d) " . formatted ( result5_hql . size ( ) ) ) ;
formatLog ( " 6. 逻辑操作符查询 JPA " ) ;
// 查找姓名为Alice或Bob、且姓名不为Charlie或David的员工
var result6_jpa = employeeRepository . findAll ( ( root , query , cb ) - > cb . and (
cb . or (
cb . equal ( root . get ( Employee_ . name ) , " Alice " ) ,
cb . equal ( root . get ( Employee_ . name ) , " Bob " )
) ,
cb . not (
cb . or (
cb . equal ( root . get ( Employee_ . name ) , " Charlie " ) ,
cb . equal ( root . get ( Employee_ . name ) , " David " )
)
);
Assert . isTrue ( employees2 . size ( ) = = 1 , " 查询数量错误 " ) ;
)
) ) ;
Assert . isTrue ( result6_jpa . size ( ) = = 2 , " 逻辑操作符查询失败 ( %d) " . formatted ( result6_jpa . size ( ) ) ) ;
var employees3 = employeeRepository . findAll (
QEmployee . employee . name . isNotNull ( )
. and ( QEmployee . employee . name . eq ( " Tom " ) )
. and ( QEmployee . employee . name . like ( " To% " ) )
. and ( Q Employee. employee . name . startsWith ( " To " ) )
. and ( Q Employee. employee . name . endsWith ( " om " ) )
. and ( Q Employee. employee . age . lt ( 200 ) )
. and ( QEmployee . employee . age . goe ( 0 ) )
. and ( QEmployee . employee . name . in ( " Tom " , " Mike " ) )
. and ( QEmployee . employee . age . between ( 0 , 200 ) )
. and ( QEmployee . employee . company ( ) . employees . isNotEmpty ( ) )
. and ( QEmployee . employee . company ( ) . industries . contains ( Industry . MEDIA ) )
. and ( QEmployee . employee . connections . containsKey ( Employee . ConnectionType . EMAIL ) )
formatLog ( " 6. 逻辑操作符查询 Fenix " ) ;
log . info ( " Fenix框架当前版本不支持复杂的嵌套OR和NOT组合逻辑 " ) ;
log . info ( " Fenix支持简单的orEquals, 但不支持嵌套的or + not组合 " ) ;
var result6_fenix = employeeRepository . findAll (
builder - > builder . orEquals ( Employee . Fields . name , " Alice " )
. orEquals ( Employee . Fields . name , " Bob " )
. andNotIn ( Employee . Fields . name , List . of ( " Charlie " , " David " ) )
. build ( )
) ;
Assert . isTrue ( employees3 . size ( ) = = 1 , " 查询数量错误 " ) ;
Assert . isTrue ( result6_fenix . size ( ) = = 3 , " 逻辑操作符查询失败 ( %d) " . formatted ( result6_fenix . size ( ) ) ) ;
formatLog ( " Clean " ) ;
reportRepository . deleteAllInBatch ( ) ;
formatLog ( " 6. 逻辑操作符查询 QueryDSL " ) ;
var result6_querydsl = employeeRepository . findAll (
QEmployee . employee . name . eq ( " Alice " ) . or ( QEmployee . employee . name . eq ( " Bob " ) )
. and ( QEmployee . employee . name . eq ( " Charlie " ) . or ( QEmployee . employee . name . eq ( " David " ) ) . not ( ) )
) ;
Assert . isTrue ( result6_querydsl . size ( ) = = 2 , " 逻辑操作符查询失败 ( %d) " . formatted ( result6_querydsl . size ( ) ) ) ;
formatLog ( " 6. 逻辑操作符查询 HQL " ) ;
var result6_hql = session . createQuery (
"""
from Employee employee
where (employee.name = 'Alice' or employee.name = 'Bob')
and not (employee.name = 'Charlie' or employee.name = 'David')
""" ,
Employee . class
) . list ( ) ;
Assert . isTrue ( result6_hql . size ( ) = = 2 , " 逻辑操作符查询失败 ( %d) " . formatted ( result6_hql . size ( ) ) ) ;
formatLog ( " 7. Specification 链式调用查询 JPA " ) ;
// 链式组合: 激活状态为true、年龄大于25、角色不是ADMIN、或姓名为Charlie、且姓名不为Alice Smith
var result7_jpa = employeeRepository . findAll (
Specification . < Employee > where ( ( root , query , cb ) - > cb . isTrue ( root . get ( Employee_ . active ) ) )
. and ( ( root , query , cb ) - > cb . greaterThan ( root . get ( Employee_ . age ) , 25 ) )
. and ( ( root , query , cb ) - > cb . notEqual ( root . get ( Employee_ . role ) , Employee . Role . ADMIN ) )
. or ( ( root , query , cb ) - > cb . equal ( root . get ( Employee_ . name ) , " Charlie " ) )
. and ( ( root , query , cb ) - > cb . notEqual ( root . get ( Employee_ . name ) , " Alice Smith " ) )
) ;
Assert . isTrue ( result7_jpa . size ( ) = = 2 , " Specification 链式调用失败 ( %d) " . formatted ( result7_jpa . size ( ) ) ) ;
formatLog ( " 7. Specification 链式调用查询 Fenix " ) ;
var result7_fenix = employeeRepository . findAll (
builder - > builder . andEquals ( Employee . Fields . active , true )
. andGreaterThan ( Employee . Fields . age , 25 )
. andNotEquals ( Employee . Fields . role , Employee . Role . ADMIN )
. orEquals ( Employee . Fields . name , " Charlie " )
. andNotEquals ( Employee . Fields . name , " Alice Smith " )
. build ( )
) ;
Assert . isTrue ( result7_fenix . size ( ) = = 2 , " Specification 链式调用失败 ( %d) " . formatted ( result7_fenix . size ( ) ) ) ;
formatLog ( " 7. Specification 链式调用查询 QueryDSL " ) ;
var result7_querydsl = employeeRepository . findAll (
QEmployee . employee . active . isTrue ( )
. and ( QEmployee . employee . age . gt ( 25 ) )
. and ( QEmployee . employee . role . ne ( Employee . Role . ADMIN ) )
. or ( QEmployee . employee . name . eq ( " Charlie " ) )
. and ( QEmployee . employee . name . ne ( " Alice Smith " ) )
) ;
Assert . isTrue ( result7_querydsl . size ( ) = = 2 , " Specification 链式调用失败 ( %d) " . formatted ( result7_querydsl . size ( ) ) ) ;
formatLog ( " 7. Specification 链式调用查询 HQL " ) ;
var result7_hql = session . createQuery (
"""
from Employee employee
where employee.active is true
and employee.age > 25
and employee.role != com.lanyuanxiaoyao.service.template.database.jpa.entity.Employee.Role.ADMIN
and employee.name != 'Alice Smith'
or employee.name = 'Charlie'
""" ,
Employee . class
) . list ( ) ;
Assert . isTrue ( result7_hql . size ( ) = = 2 , " Specification 链式调用失败 ( %d) " . formatted ( result7_hql . size ( ) ) ) ;
formatLog ( " 8. Join 操作查询 JPA " ) ;
// 查找公司名为TechCorp、技能包含Java、属性值为Senior的员工( 使用join、fetch、集合join、map join)
var result8_jpa = employeeRepository . findAll ( ( root , query , cb ) - > {
return cb . and (
// Company Join 条件
cb . equal ( root . join ( Employee_ . company ) . get ( Company_ . name ) , " TechCorp " ) ,
cb . notEqual ( root . join ( Employee_ . company ) . get ( Company_ . name ) , " DataInc " ) ,
// Skills Join 条件
cb . equal ( root . join ( Employee_ . skills ) . get ( Skill_ . name ) , " Java " ) ,
cb . notEqual ( root . join ( Employee_ . skills ) . get ( Skill_ . name ) , " MySQL " ) ,
// Map Join 条件
cb . equal ( root . join ( Employee_ . properties ) . value ( ) , " Senior " ) ,
cb . notEqual ( root . join ( Employee_ . properties ) . value ( ) , " Junior " )
) ;
} ) ;
Assert . isTrue ( result8_jpa . size ( ) = = 1 , " Join 操作查询失败 ( %d) " . formatted ( result8_jpa . size ( ) ) ) ;
formatLog ( " 8. Join 操作查询 Fenix " ) ;
log . info ( " Fenix框架当前版本不支持显式的join操作: " ) ;
log . info ( " - root.join() - 显式关联查询 " ) ;
log . info ( " - root.fetch() - 显式抓取查询 " ) ;
log . info ( " - Map join (cb.equal(root.join().value(), ...)) " ) ;
log . info ( " Fenix主要用于单表条件查询, 复杂join操作建议使用JPA Specification原生方式或QueryDSL " ) ;
log . info ( " 可以通过doAny使用原生CriteriaBuilder实现join操作 " ) ;
log . info ( " 注意: 由于类型系统的限制, doAny中使用join可能会有类型推断问题 " ) ;
log . info ( " 建议: 对于join等复杂查询, 直接使用JPA Specification原生方式 " ) ;
formatLog ( " 8. Join 操作查询 QueryDSL " ) ;
var result8_querydsl = employeeRepository . findAll (
QEmployee . employee . company ( ) . name . eq ( " TechCorp " )
. and ( QEmployee . employee . company ( ) . name . ne ( " DataInc " ) )
. and ( QEmployee . employee . skills . any ( ) . name . eq ( " Java " ) )
. and ( QEmployee . employee . skills . any ( ) . name . ne ( " MySQL " ) )
. and ( QEmployee . employee . properties . containsValue ( " Senior " ) )
. and ( QEmployee . employee . properties . containsValue ( " Junior " ) . not ( ) )
) ;
Assert . isTrue ( result8_querydsl . size ( ) = = 1 , " Join 操作查询失败 ( %d) " . formatted ( result8_querydsl . size ( ) ) ) ;
formatLog ( " 8. Join 操作查询 HQL " ) ;
var result8_hql = session . createQuery (
"""
from Employee employee
join employee.company as company
join employee.skills as skill
join employee.properties as prop
where company.name = 'TechCorp'
and company.name != 'DataInc'
and skill.name = 'Java'
and skill.name != 'MySQL'
and value(prop) = 'Senior'
and value(prop) != 'Junior'
""" ,
Employee . class
) . list ( ) ;
Assert . isTrue ( result8_hql . size ( ) = = 1 , " Join 操作查询失败 ( %d) " . formatted ( result8_hql . size ( ) ) ) ;
formatLog ( " 9. 子查询和聚合函数查询 JPA " ) ;
// 查找薪资高于平均薪资、总记录数不为5、总薪酬在55000-70000之间、激活状态为true、姓名不为David、年龄大于28、角色不是USER的员工
var result9_jpa = employeeRepository . findAll ( ( root , query , cb ) - > {
var avgSalarySubquery = query . subquery ( Double . class ) ;
var avgSubRoot = avgSalarySubquery . from ( Employee . class ) ;
avgSalarySubquery . select ( cb . avg ( avgSubRoot . get ( Employee_ . salary ) ) ) ;
var countSubquery = query . subquery ( Long . class ) ;
var countSubRoot = countSubquery . from ( Employee . class ) ;
countSubquery . select ( cb . count ( countSubRoot ) ) ;
var salary = root . get ( Employee_ . salary ) . as ( BigDecimal . class ) ;
var bonus = root . get ( Employee_ . bonus ) . as ( BigDecimal . class ) ;
var totalCompensation = cb . sum ( salary , cb . coalesce ( bonus , cb . literal ( new BigDecimal ( " 0.00 " ) ) ) ) ;
return cb . and (
cb . greaterThan ( root . get ( Employee_ . salary ) . as ( Double . class ) , avgSalarySubquery ) ,
cb . notEqual ( cb . literal ( 5L ) , countSubquery ) ,
cb . greaterThan ( totalCompensation , cb . literal ( new BigDecimal ( " 55000.00 " ) ) ) ,
cb . lessThan ( totalCompensation , cb . literal ( new BigDecimal ( " 70000.00 " ) ) ) ,
cb . isTrue ( root . get ( Employee_ . active ) ) ,
cb . notEqual ( root . get ( Employee_ . name ) , " David " ) ,
cb . greaterThan ( root . get ( Employee_ . age ) , 28 ) ,
cb . notEqual ( root . get ( Employee_ . role ) , Employee . Role . USER )
) ;
} ) ;
Assert . isTrue ( result9_jpa . isEmpty ( ) , " 子查询(聚合函数)+ 数学运算失败 ( %d) " . formatted ( result9_jpa . size ( ) ) ) ;
formatLog ( " 9. 子查询和聚合函数查询 Fenix " ) ;
log . info ( " Fenix框架当前版本不支持以下高级查询特性: " ) ;
log . info ( " - query.subquery() - 子查询 " ) ;
log . info ( " - cb.avg(), cb.count(), cb.sum() - 聚合函数 " ) ;
log . info ( " - cb.coalesce() - 空值替换函数 " ) ;
log . info ( " - cb.sum() - 数值加法运算 " ) ;
log . info ( " 这些是SQL级别的复杂查询, Fenix主要用于动态条件构建 " ) ;
log . info ( " 可以通过doAny使用原生CriteriaBuilder实现部分聚合操作 " ) ;
formatLog ( " 9. 子查询和聚合函数查询 QueryDSL " ) ;
var avgQuery = factory . select ( QEmployee . employee . salary . avg ( ) ) ;
var countQuery = factory . select ( QEmployee . employee . count ( ) ) ;
var result9_querydsl = employeeRepository . findAll (
QEmployee . employee . salary . gt ( avgQuery )
. and ( countQuery . ne ( 5L ) )
. and ( QEmployee . employee . salary . add ( QEmployee . employee . bonus . coalesce ( new BigDecimal ( " 0.00 " ) ) ) . gt ( new BigDecimal ( " 55000.00 " ) ) )
. and ( QEmployee . employee . salary . add ( QEmployee . employee . bonus . coalesce ( new BigDecimal ( " 0.00 " ) ) ) . lt ( new BigDecimal ( " 70000.00 " ) ) )
. and ( QEmployee . employee . active . isTrue ( ) )
. and ( QEmployee . employee . name . ne ( " David " ) )
. and ( QEmployee . employee . age . gt ( 28 ) )
. and ( QEmployee . employee . role . ne ( Employee . Role . USER ) )
) ;
Assert . isTrue ( result9_querydsl . isEmpty ( ) , " 子查询(聚合函数)+ 数学运算失败 ( %d) " . formatted ( result9_querydsl . size ( ) ) ) ;
formatLog ( " 9. 子查询和聚合函数查询 HQL " ) ;
var result9_hql = session . createQuery (
"""
from Employee employee
where employee.salary > (select avg(e.salary) from Employee e)
and 5 <> (select count(e.id) from Employee e)
and employee.salary + coalesce(employee.bonus, 0.00) > 55000.00
and employee.salary + coalesce(employee.bonus, 0.00) < 70000.00
and employee.active is true
and employee.name != 'David'
and employee.age > 28
and employee.role != com.lanyuanxiaoyao.service.template.database.jpa.entity.Employee.Role.USER
""" ,
Employee . class
) . list ( ) ;
Assert . isTrue ( result9_hql . isEmpty ( ) , " 子查询(聚合函数)+ 数学运算失败 ( %d) " . formatted ( result9_hql . size ( ) ) ) ;
formatLog ( " 10. 排序查询 JPA " ) ;
// 查找激活状态为true、角色不是ADMIN、年龄大于20、薪资小于60000的员工, 按年龄降序、姓名升序排序
var result10_jpa = employeeRepository . findAll (
( root , query , cb ) - > cb . and (
cb . isTrue ( root . get ( Employee_ . active ) ) ,
cb . notEqual ( root . get ( Employee_ . role ) , Employee . Role . ADMIN ) ,
cb . greaterThan ( root . get ( Employee_ . age ) , 20 ) ,
cb . lessThan ( root . get ( Employee_ . salary ) , new BigDecimal ( " 60000.00 " ) )
) ,
Sort . by (
Sort . Order . desc ( Employee_ . AGE ) ,
Sort . Order . asc ( Employee_ . NAME )
)
) ;
Assert . isTrue ( result10_jpa . size ( ) = = 3 , " 排序查询失败 ( %d) " . formatted ( result10_jpa . size ( ) ) ) ;
formatLog ( " 10. 排序查询 Fenix " ) ;
log . info ( " Fenix框架使用Spring Data JPA原生的Sort对象进行排序 " ) ;
log . info ( " Fenix构建查询条件, Sort对象通过repository.findAll()的第二个参数传入 " ) ;
var result10_fenix = employeeRepository . findAll (
builder - > builder . andEquals ( Employee . Fields . active , true )
. andNotEquals ( Employee . Fields . role , Employee . Role . ADMIN )
. andGreaterThan ( Employee . Fields . age , 20 )
. andLessThan ( Employee . Fields . salary , new BigDecimal ( " 60000.00 " ) )
. build ( ) ,
Sort . by (
Sort . Order . desc ( Employee . Fields . age ) ,
Sort . Order . asc ( Employee . Fields . name )
)
) ;
Assert . isTrue ( result10_fenix . size ( ) = = 3 , " 排序查询失败 ( %d) " . formatted ( result10_fenix . size ( ) ) ) ;
formatLog ( " 10. 排序查询 QueryDSL " ) ;
var result10_querydsl = employeeRepository . findAll (
QEmployee . employee . active . isTrue ( )
. and ( QEmployee . employee . role . ne ( Employee . Role . ADMIN ) )
. and ( QEmployee . employee . age . gt ( 20 ) )
. and ( QEmployee . employee . salary . lt ( new BigDecimal ( " 60000.00 " ) ) ) ,
QEmployee . employee . age . desc ( ) ,
QEmployee . employee . name . asc ( )
) ;
Assert . isTrue ( result10_querydsl . size ( ) = = 3 , " 排序查询失败 ( %d) " . formatted ( result10_querydsl . size ( ) ) ) ;
formatLog ( " 10. 排序查询 HQL " ) ;
var result10_hql = session . createQuery (
"""
from Employee employee
where employee.active is true
and employee.role != com.lanyuanxiaoyao.service.template.database.jpa.entity.Employee.Role.ADMIN
and employee.age > 20
and employee.salary < 60000.00
order by employee.age desc, employee.name asc
""" ,
Employee . class
) . list ( ) ;
Assert . isTrue ( result10_hql . size ( ) = = 3 , " 排序查询失败 ( %d) " . formatted ( result10_hql . size ( ) ) ) ;
formatLog ( " 11. 分页查询 JPA " ) ;
// 分页查找激活状态为true、角色不是ADMIN、年龄大于20、薪资小于60000的员工, 每页2条, 按年龄排序
var page11_jpa = employeeRepository . findAll (
( root , query , cb ) - > cb . and (
cb . isTrue ( root . get ( Employee_ . active ) ) ,
cb . notEqual ( root . get ( Employee_ . role ) , Employee . Role . ADMIN ) ,
cb . greaterThan ( root . get ( Employee_ . age ) , 20 ) ,
cb . lessThan ( root . get ( Employee_ . salary ) , new BigDecimal ( " 60000.00 " ) )
) ,
PageRequest . of ( 0 , 2 , Sort . by ( Employee_ . AGE ) )
) ;
Assert . isTrue ( page11_jpa . getContent ( ) . size ( ) = = 2 , " 分页大小不正确 ( %d) " . formatted ( page11_jpa . getContent ( ) . size ( ) ) ) ;
Assert . isTrue ( page11_jpa . getTotalElements ( ) = = 3 , " 总元素数不正确 ( %d) " . formatted ( page11_jpa . getTotalElements ( ) ) ) ;
formatLog ( " 11. 分页查询 Fenix " ) ;
log . info ( " Fenix框架使用Spring Data JPA原生的Pageable对象进行分页 " ) ;
log . info ( " Fenix构建查询条件, Pageable对象通过repository.findAll()的第二个参数传入 " ) ;
var page11_fenix = employeeRepository . findAll (
builder - > builder . andEquals ( Employee . Fields . active , true )
. andNotEquals ( Employee . Fields . role , Employee . Role . ADMIN )
. andGreaterThan ( Employee . Fields . age , 20 )
. andLessThan ( Employee . Fields . salary , new BigDecimal ( " 60000.00 " ) )
. build ( ) ,
PageRequest . of ( 0 , 2 , Sort . by ( Employee . Fields . age ) )
) ;
Assert . isTrue ( page11_fenix . getContent ( ) . size ( ) = = 2 , " 分页大小不正确 ( %d) " . formatted ( page11_fenix . getContent ( ) . size ( ) ) ) ;
Assert . isTrue ( page11_fenix . getTotalElements ( ) = = 3 , " 总元素数不正确 ( %d) " . formatted ( page11_fenix . getTotalElements ( ) ) ) ;
formatLog ( " 11. 分页查询 QueryDSL " ) ;
log . info ( " QueryDSL支持分页查询: " ) ;
log . info ( " - offset() - 跳过记录数 " ) ;
log . info ( " - limit() - 限制记录数 " ) ;
log . info ( " - 也可以结合Spring Data JPA的Pageable对象 " ) ;
var page11_querydsl = employeeRepository . findAll (
QEmployee . employee . active . isTrue ( )
. and ( QEmployee . employee . role . ne ( Employee . Role . ADMIN ) )
. and ( QEmployee . employee . age . gt ( 20 ) )
. and ( QEmployee . employee . salary . lt ( new BigDecimal ( " 60000.00 " ) ) ) ,
PageRequest . of ( 0 , 2 , Sort . by ( QEmployee . employee . age . getMetadata ( ) . getName ( ) ) )
) ;
Assert . isTrue ( page11_querydsl . getContent ( ) . size ( ) = = 2 , " 分页大小不正确 ( %d) " . formatted ( page11_querydsl . getContent ( ) . size ( ) ) ) ;
Assert . isTrue ( page11_querydsl . getTotalElements ( ) = = 3 , " 总元素数不正确 ( %d) " . formatted ( page11_querydsl . getTotalElements ( ) ) ) ;
formatLog ( " 11. 分页查询 HQL " ) ;
var page11_hql = session . createQuery (
"""
from Employee employee
where employee.active is true
and employee.role != com.lanyuanxiaoyao.service.template.database.jpa.entity.Employee.Role.ADMIN
and employee.age > 20
and employee.salary < 60000.00
order by employee.age
""" ,
Employee . class
) . setFirstResult ( 0 ) . setMaxResults ( 2 ) . list ( ) ;
var total11_hql = session . createQuery (
"""
from Employee employee
where employee.active is true
and employee.role != com.lanyuanxiaoyao.service.template.database.jpa.entity.Employee.Role.ADMIN
and employee.age > 20
and employee.salary < 60000.00
""" ,
Employee . class
) . list ( ) . size ( ) ;
Assert . isTrue ( page11_hql . size ( ) = = 2 , " 分页大小不正确 ( %d) " . formatted ( page11_hql . size ( ) ) ) ;
Assert . isTrue ( total11_hql = = 3 , " 总元素数不正确 ( %d) " . formatted ( total11_hql ) ) ;
formatLog ( " 12. CASE WHEN 条件表达式查询 JPA " ) ;
// 查找年龄大于30( Senior) 或年龄在25-30之间( Middle) 的员工, 排除Junior级别的员工
var result12_jpa = employeeRepository . findAll ( ( root , query , cb ) - > cb . and (
cb . equal (
cb . selectCase ( )
. when ( cb . greaterThan ( root . get ( Employee_ . age ) , 30 ) , " Senior " )
. when ( cb . between ( root . get ( Employee_ . age ) , 25 , 30 ) , " Middle " )
. otherwise ( " Junior " ) ,
" Senior "
) ,
cb . notEqual (
cb . selectCase ( )
. when ( cb . greaterThan ( root . get ( Employee_ . age ) , 30 ) , " Senior " )
. when ( cb . between ( root . get ( Employee_ . age ) , 25 , 30 ) , " Middle " )
. otherwise ( " Junior " ) ,
" Junior "
)
) ) ;
Assert . isTrue ( result12_jpa . size ( ) = = 2 , " CASE WHEN 查询失败 ( %d) " . formatted ( result12_jpa . size ( ) ) ) ;
formatLog ( " 12. CASE WHEN 条件表达式查询 Fenix " ) ;
log . info ( " Fenix框架当前版本不支持CASE WHEN条件表达式: " ) ;
log . info ( " - cb.selectCase() - SQL CASE WHEN表达式 " ) ;
log . info ( " - .when() - CASE WHEN条件分支 " ) ;
log . info ( " - .otherwise() - CASE ELSE分支 " ) ;
log . info ( " CASE WHEN是SQL级别的条件表达式, Fenix主要用于动态条件构建 " ) ;
log . info ( " 可以通过doAny使用原生CriteriaBuilder实现CASE WHEN操作 " ) ;
log . info ( " 注意: 由于类型系统的限制, doAny中使用CriteriaBuilder的复杂表达式可能会有类型推断问题 " ) ;
log . info ( " 建议: 对于CASE WHEN等复杂查询, 直接使用JPA Specification原生方式 " ) ;
var result12_fenix = employeeRepository . findAll ( builder - > {
return builder . doAny ( null , null , ( cb , root , fieldName , value ) - > {
// 使用原生JPA Criteria实现CASE WHEN( 简化版本)
var caseExpr = cb . selectCase ( )
. when ( cb . greaterThan ( root . get ( " age " ) , 30 ) , " Senior " )
. when ( cb . between ( root . get ( " age " ) , 25 , 30 ) , " Middle " )
. otherwise ( " Junior " ) ;
return cb . and (
cb . equal ( caseExpr , " Senior " ) ,
cb . notEqual ( caseExpr , " Junior " )
) ;
} ) . build ( ) ;
} ) ;
Assert . isTrue ( result12_fenix . size ( ) = = 2 , " CASE WHEN 查询失败 ( %d) " . formatted ( result12_fenix . size ( ) ) ) ;
formatLog ( " 12. CASE WHEN 条件表达式查询 QueryDSL " ) ;
var caseExpr = new CaseBuilder ( )
. when ( QEmployee . employee . age . gt ( 30 ) ) . then ( " Senior " )
. when ( QEmployee . employee . age . between ( 25 , 30 ) ) . then ( " Middle " )
. otherwise ( " Junior " ) ;
var result12_querydsl = employeeRepository . findAll (
caseExpr . eq ( " Senior " ) . and ( caseExpr . ne ( " Junior " ) )
) ;
Assert . isTrue ( result12_querydsl . size ( ) = = 2 , " CASE WHEN 查询失败 ( %d) " . formatted ( result12_querydsl . size ( ) ) ) ;
formatLog ( " 12. CASE WHEN 条件表达式查询 HQL " ) ;
var result12_hql = session . createQuery (
"""
from Employee employee
where (case when employee.age > 30 then 'Senior'
when employee.age between 25 and 30 then 'Middle'
else 'Junior'
end) = 'Senior'
and (case when employee.age > 30 then 'Senior'
when employee.age between 25 and 30 then 'Middle'
else 'Junior'
end) != 'Junior'
""" ,
Employee . class
) . list ( ) ;
Assert . isTrue ( result12_hql . size ( ) = = 2 , " CASE WHEN 查询失败 ( %d) " . formatted ( result12_hql . size ( ) ) ) ;
formatLog ( " 13. 综合多条件查询 JPA " ) ;
// 综合查询: 公司名为TechCorp、技能包含Java、城市为Beijing、技能和爱好非空、属性非空、创建和修改时间不为null、激活状态为true、姓名不为Alice Smith、年龄大于25、角色不是USER
var result13_jpa = employeeRepository . findAll ( ( root , query , cb ) - > {
query . distinct ( true ) ;
return cb . and (
// Company Join 条件
cb . equal ( root . join ( Employee_ . company ) . get ( Company_ . name ) , " TechCorp " ) ,
cb . notEqual ( root . join ( Employee_ . company ) . get ( Company_ . name ) , " DataInc " ) ,
// Skills Join 条件
cb . equal ( root . join ( Employee_ . skills ) . get ( Skill_ . name ) , " Java " ) ,
cb . notEqual ( root . join ( Employee_ . skills ) . get ( Skill_ . name ) , " MySQL " ) ,
// Embedded 对象条件
cb . equal ( root . get ( Employee_ . address ) . get ( Address_ . city ) , " Beijing " ) ,
cb . notEqual ( root . get ( Employee_ . address ) . get ( Address_ . city ) , " Shanghai " ) ,
// 集合条件
cb . isNotEmpty ( root . get ( Employee_ . skills ) ) ,
cb . not ( cb . isEmpty ( root . get ( Employee_ . hobbies ) ) ) ,
// Map 条件
cb . isNotEmpty ( root . get ( Employee_ . properties ) ) ,
// 日期时间字段查询
cb . isNotNull ( root . get ( Employee_ . createdTime ) ) ,
cb . isNotNull ( root . get ( Employee_ . modifiedTime ) ) ,
// 其他条件
cb . isTrue ( root . get ( Employee_ . active ) ) ,
cb . notEqual ( root . get ( Employee_ . name ) , " Alice Smith " ) ,
cb . greaterThan ( root . get ( Employee_ . age ) , 25 ) ,
cb . notEqual ( root . get ( Employee_ . role ) , Employee . Role . USER )
) ;
} ) ;
Assert . isTrue ( result13_jpa . size ( ) = = 1 & & result13_jpa . get ( 0 ) . getName ( ) . equals ( " Alice " ) , " 综合多条件查询失败 ( %d) " . formatted ( result13_jpa . size ( ) ) ) ;
formatLog ( " 13. 综合多条件查询 Fenix " ) ;
log . info ( " Fenix框架不支持综合多条件查询中的复杂join和集合操作: " ) ;
log . info ( " - 显式join操作( company, skills, properties) " ) ;
log . info ( " - 集合isEmpty/isNotEmpty/isMember操作 " ) ;
log . info ( " - Map join操作 " ) ;
log . info ( " - 嵌入式对象查询( address.city) " ) ;
log . info ( " Fenix主要支持简单的单表字段查询 " ) ;
log . info ( " 可以通过doAny使用原生CriteriaBuilder实现复杂综合查询 " ) ;
formatLog ( " 13. 综合多条件查询 QueryDSL " ) ;
var result13_querydsl = employeeRepository . findAll (
// Company Join 条件
QEmployee . employee . company ( ) . name . eq ( " TechCorp " )
. and ( QEmployee . employee . company ( ) . name . ne ( " DataInc " ) )
// Skills Join 条件
. and ( QEmployee . employee . skills . any ( ) . name . eq ( " Java " ) )
. and ( QEmployee . employee . skills . any ( ) . name . ne ( " MySQL " ) )
// Embedded 对象条件
. and ( QEmployee . employee . address ( ) . city . eq ( " Beijing " ) )
. and ( QEmployee . employee . address ( ) . city . ne ( " Shanghai " ) )
// 集合条件
. and ( QEmployee . employee . skills . isNotEmpty ( ) )
. and ( QEmployee . employee . hobbies . isNotEmpty ( ) )
// Map 条件
// .and(QEmployee.employee.properties.isNotEmpty())
// 日期时间字段查询
. and ( QEmployee . employee . createdTime . isNotNull ( ) )
. and ( QEmployee . employee . modifiedTime . isNotNull ( ) )
// 其他条件
. and ( QEmployee . employee . active . isTrue ( ) )
. and ( QEmployee . employee . name . ne ( " Alice Smith " ) )
. and ( QEmployee . employee . age . gt ( 25 ) )
. and ( QEmployee . employee . role . ne ( Employee . Role . USER ) )
) ;
Assert . isTrue ( result13_querydsl . size ( ) = = 1 & & result13_querydsl . get ( 0 ) . getName ( ) . equals ( " Alice " ) , " 综合多条件查询失败 ( %d) " . formatted ( result13_querydsl . size ( ) ) ) ;
formatLog ( " 13. 综合多条件查询 HQL " ) ;
var result13_hql = session . createQuery (
"""
select distinct employee
from Employee employee
join employee.company as company
join employee.skills as skill
where company.name = 'TechCorp'
and company.name != 'DataInc'
and skill.name = 'Java'
and skill.name != 'MySQL'
and employee.address.city = 'Beijing'
and employee.address.city != 'Shanghai'
and employee.skills is not empty
and employee.hobbies is not empty
and employee.properties is not empty
and employee.createdTime is not null
and employee.modifiedTime is not null
and employee.active is true
and employee.name != 'Alice Smith'
and employee.age > 25
and employee.role != com.lanyuanxiaoyao.service.template.database.jpa.entity.Employee.Role.USER
""" ,
Employee . class
) . list ( ) ;
Assert . isTrue ( result13_hql . size ( ) = = 1 & & result13_hql . get ( 0 ) . getName ( ) . equals ( " Alice " ) , " 综合多条件查询失败 ( %d) " . formatted ( result13_hql . size ( ) ) ) ;
formatLog ( " 清理测试数据 " ) ;
employeeRepository . deleteAllInBatch ( ) ;
companyRepository . deleteAllInBatch ( ) ;
}
private void testNative ( ) {
formatLog ( " Added " ) ;
var company1 = companyRepository . save ( Company . builder ( ) . name ( randomString ( 5 ) ) . members ( randomInt ( 100 ) ) . build ( ) ) ;
var company2 = companyRepository . save ( Company . builder ( ) . name ( randomString ( 5 ) ) . members ( randomInt ( 100 ) ) . build ( ) ) ;
var company3 = companyRepository . save ( Company . builder ( ) . name ( randomString ( 5 ) ) . members ( randomInt ( 100 ) ) . build ( ) ) ;
var employee1 = employeeRepository . save ( Employee . builder ( ) . name ( randomString ( 10 ) ) . age ( randomInt ( 100 ) ) . role ( Employee . Role . USER ) . company ( company1 ) . build ( ) ) ;
var employee2 = employeeRepository . save ( Employee . builder ( ) . name ( randomString ( 10 ) ) . age ( randomInt ( 100 ) ) . role ( Employee . Role . USER ) . company ( company2 ) . build ( ) ) ;
var employee3 = employeeRepository . save ( Employee . builder ( ) . name ( randomString ( 10 ) ) . age ( randomInt ( 100 ) ) . role ( Employee . Role . USER ) . company ( company3 ) . build ( ) ) ;
formatLog ( " HQL Query " ) ;
var list = employeeRepository . findAllEmployeeWithCompanyName ( ) ;
Assert . isTrue ( list . size ( ) = = 3 , " 数量错误 " ) ;
formatLog ( " SQL Query " ) ;
var list_native = employeeRepository . findAllEmployeeWithCompanyNameNative ( ) ;
Assert . isTrue ( list_native . size ( ) = = 3 , " 数量错误 " ) ;
formatLog ( " Clean " ) ;
employeeRepository . deleteAllInBatch ( ) ;
companyRepository . deleteAllInBatch ( ) ;
}
}