# activerecord-solon-plugin-demo
**Repository Path**: gollyhu/activerecord-solon-plugin-demo
## Basic Information
- **Project Name**: activerecord-solon-plugin-demo
- **Description**: 基于Solon框架(https://gitee.com/noear/solon)的 ActiveRecord 的框架适配插件activerecord-solon-plugin的Demo。
- **Primary Language**: Java
- **License**: Apache-2.0
- **Default Branch**: master
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 0
- **Forks**: 0
- **Created**: 2022-08-21
- **Last Updated**: 2023-03-03
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
# activerecord-solon-plugin-demo
#### 介绍
基于Solon框架( https://gitee.com/noear/solon )ActiveRecord 的框架适配插件activerecord-solon-plugin的Demo。
#### 安装教程
1. 引入插件
``` xml
io.github.gollyhu
activerecord-solon-plugin
xxx
```
#### 使用说明
1. 配置示例
```yaml
demo.db1:
schema: rock
jdbcUrl: jdbc:mysql://localhost:3306/jfinal_demo?useSSL=false
driverClassName: com.mysql.jdbc.Driver
username: root
password: password
demo.db2:
schema: rock
jdbcUrl: jdbc:mysql://localhost:3306/jfinal_demo?useSSL=false
driverClassName: com.mysql.jdbc.Driver
username: root
password: password
```
2. 启动示例
``` java
/**
* 启动应用
*/
public class DemoApp {
public static void main(String[] args) {
Solon.start(DemoApp.class, args, app -> {
app.onEvent(ActiveRecordPlugin.class, arp -> {
if (Solon.cfg().isDebugMode() || Solon.cfg().isFilesMode()) {
arp.setDevMode(true); // 启用开发模式
arp.setShowSql(true); // 启用SQL输出
}
});
});
}
}
```
3. 数据源配置示例
``` java
/**
* 配置数据源
*/
@Configuration
public class Config {
@Bean // 不指定数据源名称,则采用默认数据源
public DataSource db1(@Inject("${demo.db1}") HikariDataSource dataSource) {
return dataSource;
}
@Bean("db2") // 指定ActiveRecord数据源名称为db2
public DataSource db2(@Inject("${demo.db2}") HikariDataSource dataSource) {
return dataSource;
}
}
```
4. 应用示例
```java
@Controller
@Mapping("/blog")
public class BlogController {
@Inject
BlogService blogService;
@Mapping("/add")
public ModelAndView add(Context ctx) {
return new ModelAndView("/blog/add.html");
}
@Mapping("/delete/{id}")
public void delete(Context ctx, Integer id) {
this.blogService.deleteById(id);
ctx.redirect("/blog");
}
@Mapping("/edit/{id}")
public ModelAndView edit(Context ctx, Integer id) {
ModelAndView mav = new ModelAndView("/blog/edit.html");
mav.put("blog", this.blogService.findById(id));
return mav;
}
@Mapping
public ModelAndView index(Context ctx) {
return this.index(ctx, 1);
}
@Mapping("/{pageNumber}")
public ModelAndView index(Context ctx, Integer pageNumber) {
ModelAndView mav= new ModelAndView("/blog/blog.html");
mav.put("blogPage", this.blogService.paginate(pageNumber, 10));
return mav;
}
/**
* save 与 update 的业务逻辑在实际应用中也应该放在 serivce 之中,
* 并要对数据进正确性进行验证,在此仅为了偷懒
*/
@NotEmpty({"title", "content"})
@Mapping("/save")
public void save(Context ctx, @Validated Blog blog) {
this.blogService.save(blog);
ctx.redirect("/blog");
}
/**
* save 与 update 的业务逻辑在实际应用中也应该放在 serivce 之中,
* 并要对数据进正确性进行验证,在此仅为了偷懒
*/
@NotEmpty({"id", "title", "content"})
@Mapping("/update")
public void update(Context ctx, @Validated Blog blog) {
this.blogService.update(blog);
ctx.redirect("/blog");
}
}
```
#### 代码生成
1. 代码生成器
```java
/**
* 代码生成器
*/
public class _Generator {
public static void main(String[] args) {
// 获取数据源
DataSource ds1 = getDataSource1();
DataSource ds2 = getDataSource2();
System.out.println("Start generate...\n");
// 默认数据源
SolonGenerator generator = new SolonGenerator(ds1, "cn.hg.solon.plugin.activerecord.demo.common");
// 排除表
generator.addBlacklist("blog2");
// 指定自定义Model基类
generator.setBaseModelClassName(BaseModel.class.getName());
// 生成
generator.generate();
// ds2数据源
SolonGenerator generator2 = new SolonGenerator(ds2, "cn.hg.solon.plugin.activerecord.demo.common");
// 排除表
generator2.addBlacklist("blog");
// 非默认数据一定要指定数据源Bean名称
generator2.setDataSourceBeanName("db2");
// 指定自定义Model基类
generator2.setBaseModelClassName(BaseModel.class.getName());
// 生成
generator2.generate();
System.out.println("\nFinished generate.");
}
private static DataSource getDataSource1() {
HikariCpPlugin plugin = new HikariCpPlugin("jdbc:mysql://localhost/jfinal_demo?useSSL=false", "root", "123456");
plugin.start();
return plugin.getDataSource();
}
private static DataSource getDataSource2() {
HikariCpPlugin plugin = new HikariCpPlugin("jdbc:mysql://localhost/jfinal_demo?useSSL=false", "root", "123456");
plugin.start();
return plugin.getDataSource();
}
}
```
2. 代码结构


3. 代码示例
- Mapper
```java
@Namespace("BlogMapper")
public interface BlogMapper {
......
// isUpdate = true 表示接口为非查询接口
@Sql(value = "{DELETE FROM `blog` WHERE `id` = #para(id)}", isUpdate = true)
long deleteById(Object id);
......
@Sql("{ SELECT * FROM `blog` }")
List findAll();
@Sql("findBy")
List findBy(Blog model);
@Sql("findBy")
List findBy(Map map);
@Sql("findBy")
List findBy(Blog model, String orderColumn, String orderDirection);
@Sql("findBy")
List findBy(Map map, String orderColumn, String orderDirection);
......
}
@Namespace("Blog2Mapper")
public interface Blog2Mapper {
......
// isUpdate = true 表示接口为非查询接口
@Sql(value = "{DELETE FROM `blog2` WHERE `id` = #para(id)}", isUpdate = true)
long deleteById(Object id);
......
@Sql("{ SELECT * FROM `blog2` }")
List findAll();
@Sql("findByModel")
List findBy(Blog2 model);
@Sql("findByMap")
List findBy(Map map);
@Sql("findByModel")
List findBy(Blog2 model, String orderColumn, String orderDirection);
@Sql("findByMap")
List findBy(Map map, String orderColumn, String orderDirection);
......
}
```
- BaseModel
```java
public abstract class BaseBlog> extends Model implements IBean {
......
}
public abstract class BaseBlog2> extends Model implements IBean {
......
}
```
- Model
```java
@Db(DbKit.MAIN_CONFIG_NAME)
@Table(name = "blog", primaryKey = "id")
public class Blog extends BaseBlog {
public static final Blog DAO = new Blog().dao();
}
@Db("db2") // 生成此对象时指定了数据源名称为"db2"
@Table(name = "blog2", primaryKey = "id")
public class Blog2 extends BaseBlog2 {
public static final Blog2 DAO = new Blog2().dao();
}
```
- SQL
```sql
BlogMapper.sql 文件
#namespace("BlogMapper")
#sql("countBy") ### 统计满足条件的记录数
SELECT COUNT(*) FROM `blog` #@whereFor()
#end
#sql("deleteBy") ### 删除满足条件的记录
DELETE FROM `blog` #@whereFor()
#end
......
#sql("paginateBy") ### 查找满足条件的分页记录, 并按指定条件排序
SELECT * FROM `blog` #@whereFor()
#if(orderColumn)ORDER BY #(orderColumn)#if(orderDirection) #(orderDirection)#end#end
#end
.......
### ********************
### WHERE 条件,可在此处修改查询条件,如:`name` LIKE #para(name, "like")
### ********************
#define whereFor()
WHERE TRUE
#if(id)AND `id` = #para(id)#end
#if(title)AND `title` = #para(title)#end
#if(content)AND `content` = #para(content)#end
#end
#end
Blog2Mapper.sql 文件
#namespace("Blog2Mapper")
#sql("countBy") ### 统计满足条件的记录数
SELECT COUNT(*) FROM `blog2` #@whereFor()
#end
#sql("deleteBy") ### 删除满足条件的记录
DELETE FROM `blog2` #@whereFor()
#end
......
#sql("paginateBy") ### 查找满足条件的分页记录, 并按指定条件排序
SELECT * FROM `blog2` #@whereFor()
#if(orderColumn)ORDER BY #(orderColumn)#if(orderDirection) #(orderDirection)#end#end
#end
......
### ********************
### WHERE 条件,可在此处修改查询条件,如:`name` LIKE #para(name, "like")
### ********************
#define whereFor()
WHERE TRUE
#if(id)AND `id` = #para(id)#end
#if(title)AND `title` = #para(title)#end
#if(content)AND `content` = #para(content)#end
#end
#end
```
- Service
```java
public interface BlogService extends BaseService {
}
public interface Blog2Service extends BaseService {
}
public interface BaseService> {
long count();
long countBy(M model);
long countBy(Map, ?> map);
long deleteBy(M model);
long deleteBy(Map, ?> map);
boolean deleteById(Object id);
boolean deleteByIds(Object... ids);
List findAll();
List findBy(M model);
List findBy(Map, ?> map);
List findBy(M model, String orderColumn, String orderDirection);
List findBy(Map, ?> map, String orderColumn, String orderDirection);
M findById(Object id);
M findFirstBy(M model);
M findFirstBy(Map, ?> map);
M findFirstBy(M model, String orderColumn, String orderDirection);
M findFirstBy(Map, ?> map, String orderColumn, String orderDirection);
Page paginate(int pageNumber, int pageSize);
Page paginateBy(int pageNumber, int pageSize, M model);
Page paginateBy(int pageNumber, int pageSize, Map, ?> map);
Page paginateBy(int pageNumber, int pageSize, M model, String orderColumn, String orderDirection);
Page paginateBy(int pageNumber, int pageSize, Map, ?> map, String orderColumn, String orderDirection);
boolean save(M model);
long saveMore(List list);
boolean update(M model);
long updateMore(List list);
}
```
- Provider
``` java
@Service
public class BlogProvider extends BaseProvider implements BlogService {
public BlogProvider() {
super(DbKit.MAIN_CONFIG_NAME, "BlogMapper");
}
}
@Service
public class Blog2Provider extends BaseProvider implements Blog2Service {
public Blog2Provider() {
super("db2", "Blog2Mapper");
}
}
public abstract class BaseProvider> {
// 代码太多,请参看源码
}
```
#### 应用示例
1. Service用法(推荐)
```java
@Controller
@Mapping("/blog")
public class BlogController {
@Inject
BlogService blogService;
@Mapping("/findAll")
public List findAll(Context ctx){
return this.blogService.findAll();
}
@Mapping("/findBy")
public List findBy(Context ctx){
return this.blogService.findBy(ctx.paramMap(), "id", "desc");
}
@Mapping("/findBy")
public List findBy(Context ctx, Blog model){
return this.blogService.findBy(model, "id", "desc");
}
@Mapping("/paginate")
public Page paginate(Context ctx, @Param(defaultValue = "1") int pageNumber, @Param(defaultValue = "10") int pageSize){
return this.blogService.paginate(pageNumber, pageSize);
}
@Mapping("/paginateBy")
public Page paginateBy(Context ctx, @Param(defaultValue = "1") int pageNumber, @Param(defaultValue = "10") int pageSize){
return this.blogService.paginateBy(pageNumber, pageSize, ctx.paramMap());
}
......
}
```
2. Mapper用法
```java
@Controller
@Mapping("/blog2")
public class Blog2Controller {
@Db("db2")
Blog2Mapper blog2Mapper;
@Mapping("/count")
public Long count() {
return this.blog2Mapper.count();
}
......
}
```
Mapper只是接口类,它并没有实现,本插件实现了接口与SQL模板直接打通的功能。每一个接口方法上的@Sql注解都对应Sql模板中的同名查询,也可直接通过“{ }”包裹的语句来显示指定查询。
Mapper最适用的场景为 _**复杂查询**_、_**报表查询**_ 等,测试用例中,BlogMapperTest.java(调用方) --> BlogMapper.java(接口) --> BlogMapper.sql(SQL模板)形成了一个调用链。
Mapper的使用做了简化,也有一些约定:
1. @Sql中以“{ }”包裹的语句则为显示声明查询语句,不需要与SQL模板对应
```java
@Sql("{ SELECT COUNT(*) FROM blog }")
long count();
```
2. 非查询语句一定要使用指定isUpdate = true、
```java
@Sql(value = "{ DELETE FROM blog WHERE id IN #para(ids, 'in') }", isUpdate = true)
deleteByIds(Object... ids);
```
3. 自动识别返回类型:List<>,Page<>,Model,Record,Object,void
4. 分页参数的约定
ActiveRecord的分页口比较特殊,自动为不同的数据库自动拼装页号和页大小参数。此插件也做了相应的适配,只需要在返回类型为Page<>的接口定义pageNumber和pageSize即可。如果没有定义,则会以pageNumber=1和pageSize=10的默认值获取分页记录,因此一定要记得加上这两个参数。
```java
@Sql("paginateBy")
Page paginateBy(int pageNumber, int pageSize, Map map);
```