English | δΈζ
- π Online Demo: https://demo-bs.zhxu.cn/
Bean Searcher is the GraphQL of list retrieval β entities define search boundaries, parameters drive query logic. No special protocol required, just add one dependency.
Single-table entities are searchable with zero annotations. Multi-table joins, pagination, filtering, sorting, and stats β all in one line of code.
| Pain point | Traditional approach | Bean Searcher |
|---|---|---|
| Multi-condition list queries | if-else SQL concatenation / Specification | One line, parameter-driven |
| Multi-table joins | Manual JOIN / XML mapping | Annotation-declared, auto-generated SQL |
| Frontend dynamic filtering | Add fields, change backend APIs | Zero backend changes, frontend freely combines |
| Non-invasive integration | Spring Data REST: expose repositories, restructure URLs | One dependency, inject BeanSearcher, existing code untouched |
Get started in one minute:
implementation "cn.zhxu:bean-searcher-boot-starter:${latestVersion}"Single-table search works out-of-the-box with zero annotations on your existing entity.
MyBatis / Hibernate excel at CRUD, but when it comes to list retrieval with multi-condition filtering, table joins, sorting, and pagination, they often require piles of if-else condition stitching and VO conversion code.
Bean Searcher solves this with declarative search:
- Entity as Declaration β The SearchBean defines "what can be searched"; annotations are optional
- Parameters as Query β The frontend controls "what is searched"; one endpoint handles endless combinations
- Zero Protocol Burden β Works on standard HTTP parameters, no special protocol needed
Just as GraphQL lets clients freely specify which fields to return in one request, Bean Searcher lets clients freely specify filter conditions, sort order, pagination, and stats β without a dedicated schema, all in one line of code.
See π Comparison for concrete code-to-code comparison with MyBatis and Spring Data JPA implementations in the demo project.
Start with your existing domain/VO class β annotations are optional (zero-annotation for single-table, add a few for joins):
@SearchBean(tables="user u, role r", joinCond="u.role_id = r.id", autoMapTo="u")
public class User {
private long id;
private String username;
private int status;
private int age;
private String gender;
private Date joinDate;
private int roleId;
@DbField("r.name")
private String roleName;
// Getters and setters...
}Then you can complete the API with one line of code:
@RestController
@RequestMapping("/user")
public class UserController {
@Autowired
private BeanSearcher beanSearcher; // Inject BeanSearcher
@GetMapping("/index")
public SearchResult<User> index(HttpServletRequest request) {
// Only one line of code written here
return beanSearcher.search(User.class, MapUtils.flat(request.getParameterMap()), User::getAge);
}
}This line of code can achieve:
- Retrieval from multi tables
- Pagination by any field
- Combined filter by any field
- Sorting by any field
- Summary with
agefield
For example, this API can be requested as follows:
GET: /user/indexβ default paginationGET: /user/index? page=1 & size=10β specified paginationGET: /user/index? status=1β filterstatus = 1GET: /user/index? name=Jac & name-op=swβnamestarts withJacGET: /user/index? name=Jack & name-ic=trueβname = Jack(case ignored)GET: /user/index? sort=age & order=descβ sort byagedescendingGET: /user/index? onlySelect=username,ageβ returnusernameandageonlyGET: /user/index? selectExclude=joinDateβ excludejoinDatefield
The demo project includes implementations of the same API using MyBatis and Spring Data JPA β same tables, same data, same response β so you can compare side-by-side:
| Aspect | Bean Searcher | MyBatis | Spring Data JPA |
|---|---|---|---|
| Controller code | 1 line | ~280 lines | ~280 lines |
| Extra files | 0 | 1 Mapper + 1 XML | 2 Entities + 1 Repository |
| SQL generation | Declarative annotations | Manual XML | Criteria API |
| Source | backend-springboot4 | backend-vs-mybatis | backend-vs-jpa |
The Bean Searcher version:
return beanSearcher.search(User.class, User::getAge);The MyBatis / JPA versions need handwritten parameter parsing, dynamic condition assembly, three separate queries (list / count / sum), pagination, sorting, and CSV streaming β all handled by this one line in Bean Searcher.
π₯ Online Demo ο½ π» Run Locally β Bean Searcher, MyBatis, and JPA comparison implementations
For programmatic query building (not just HTTP parameters), use the type-safe builder API:
Map<String, Object> params = MapUtils.builder()
.selectExclude(User::getJoinDate) // Exclude joinDate field
.field(User::getStatus, 1) // Filter: status = 1
.field(User::getName, "Jack").ic() // Filter: name = 'Jack' (case ignored)
.field(User::getAge, 20, 30).op(Opetator.Between) // Filter: age between 20 and 30
.orderBy(User::getAge, "asc") // Sort by age ascending
.page(0, 15) // Pagination: page=0 and size=15
.build();
List<User> users = beanSearcher.searchList(User.class, params);Bean Searcher works with any Java Web framework, such as: SpringBoot, Spring MVC, Grails, Jfinal and so on.
All you need is to add a dependency:
implementation "cn.zhxu:bean-searcher-boot-starter:${latestVersion}"and then you can inject Searcher into a Controller or Service:
@Autowired
private MapSearcher mapSearcher; // Retrieved data as Map objects
@Autowired
private BeanSearcher beanSearcher; // Retrieved data as generic objectsAll you need is to add a dependency:
implementation "cn.zhxu:bean-searcher-solon-plugin:${latestVersion}"and then you can inject Searcher into a Controller or Service:
@Inject
private MapSearcher mapSearcher;
@Inject
private BeanSearcher beanSearcher;Adding this dependency:
implementation "cn.zhxu:bean-searcher:${latestVersion}"then you can build a Searcher with SearcherBuilder:
DataSource dataSource = ... // Get the dataSource of the application
// DefaultSqlExecutor supports multi datasources
SqlExecutor sqlExecutor = new DefaultSqlExecutor(dataSource);
// Build a MapSearcher
MapSearcher mapSearcher = SearcherBuilder.mapSearcher()
.sqlExecutor(sqlExecutor)
.build();
// Build a BeanSearcher
BeanSearcher beanSearcher = SearcherBuilder.beanSearcher()
.sqlExecutor(sqlExecutor)
.build();You can customize and extend any component in Bean Searcher.
Available extension points (click to expand)
- Customizing
FieldOpto support other field operators - Customizing
DbMappingto support other ORM annotations - Customizing
ParamResolverto support JSON query params - Customizing
FieldConvertorto support any type of field - Customizing
Dialectto support more databases - and so on
- [ Sa-Token ]: A lightweight Java permission authentication framework that makes authorization simple and elegant!
- [ Fluent MyBatis ]: MyBatis syntax enhancement framework, combining features and advantages of MyBatisPlus, DynamicSql, Jpa etc., generating code with annotation processors
- [ OkHttps ]: Lightweight yet powerful HTTP client, universal for front-end and back-end, supporting WebSocket and Stomp protocols
- [ hrun4j ]: API automation testing solution
- [ JsonKit ]: Ultra-lightweight JSON facade, simple to use, independent of specific implementation, decoupling business code from Jackson, Gson, Fastjson etc.!
- [ Free UI ]: Based on Vue3 + TypeScript, a very lightweight and cool UI component library!
- Fork the code!
- Create your own branch:
git checkout -b feat/xxxx - Submit your changes:
git commit -am 'feat(function): add xxxxx' - Push your branch:
git push origin feat/xxxx - Submit
pull request