connectionLocal = new ThreadLocal<>();
-
/**
*
* String sql = "SELECT * FROM datasource_config WHERE dc_id=${id}";
@@ -63,7 +51,7 @@ public class SqlHelper {
DataSource dataSource = DataSourceManager.getDataSource(generatorConfig);
String runSql = buildSqlWithParams(dataSource, sql, params);
String[] sqls = runSql.split(";");
- Connection conn = null;
+ Connection conn;
try {
conn = DataSourceManager.getConnection(generatorConfig);
SqlRunner runner = buildSqlRunner(conn);
@@ -82,34 +70,14 @@ public class SqlHelper {
}
}
-
- public static Connection getConnection(GeneratorConfig generatorConfig) {
- Connection connection = connectionLocal.get();
- if (connection == null) {
- try {
- connection = getDataSource(generatorConfig).getConnection();
- connectionLocal.set(connection);
- } catch (SQLException e) {
- logger.error("获取Connection失败, jdbcUrl:{}", generatorConfig.getJdbcUrl(), e);
- throw new RuntimeException("获取Connection失败", e);
- }
- }
- return connection;
- }
-
- public static void closeConnection() {
- Connection connection = connectionLocal.get();
- if (connection != null) {
- try {
- connection.close();
- } catch (SQLException e) {
- e.printStackTrace();
- }
- }
- connectionLocal.remove();
- }
-
- // 参数绑定
+ /**
+ * 参数绑定
+ *
+ * @param dataSource 数据源
+ * @param sql sql模板
+ * @param params 参数
+ * @return 构建好的耍起莱
+ */
private static String buildSqlWithParams(DataSource dataSource, String sql, Map params) {
Configuration configuration = buildConfiguration(dataSource);
TextSqlNode node = new TextSqlNode(sql);
@@ -126,21 +94,6 @@ public class SqlHelper {
return new SqlRunner(connection);
}
-
- private static DataSource getDataSource(GeneratorConfig generatorConfig) {
- String jdbcUrl = generatorConfig.getJdbcUrl();
- return dataSourceMap.computeIfAbsent(jdbcUrl, key -> {
- Properties properties = new Properties();
- properties.setProperty(DRIVER, generatorConfig.getDriverClass());
- properties.setProperty(URL, jdbcUrl);
- properties.setProperty(USERNAME, generatorConfig.getUsername());
- properties.setProperty(PASSWORD, generatorConfig.getPassword());
- PooledDataSourceFactory pooledDataSourceFactory = new PooledDataSourceFactory();
- pooledDataSourceFactory.setProperties(properties);
- return pooledDataSourceFactory.getDataSource();
- });
- }
-
private static Configuration buildConfiguration(DataSource dataSource) {
TransactionFactory transactionFactory = new JdbcTransactionFactory();
Environment environment = new Environment("development",
diff --git a/gen/src/main/java/com/gitee/gen/gen/TableDefinition.java b/gen/src/main/java/com/gitee/gen/gen/TableDefinition.java
index e8bac0537268e6fdb0ee95e9da7f5e40f0276285..6cf9c61e34c9f8129a989fe36bd79fd6f686c0be 100644
--- a/gen/src/main/java/com/gitee/gen/gen/TableDefinition.java
+++ b/gen/src/main/java/com/gitee/gen/gen/TableDefinition.java
@@ -39,6 +39,15 @@ public class TableDefinition {
this.tableName = tableName;
}
+ /**
+ * 返回字段数量
+ *
+ * @return 字段数量,>=0
+ */
+ public int getColumnCount() {
+ return columnDefinitions.size();
+ }
+
/**
* 是否有时间字段
* @return true:有
diff --git a/gen/src/main/java/com/gitee/gen/service/DatasourceConfigService.java b/gen/src/main/java/com/gitee/gen/service/DatasourceConfigService.java
index e2d30af6f259b2ae5c16b8be36fd6e45c0fa260d..f11a00a9a91973a6865066627d4215bf0a2a1d38 100644
--- a/gen/src/main/java/com/gitee/gen/service/DatasourceConfigService.java
+++ b/gen/src/main/java/com/gitee/gen/service/DatasourceConfigService.java
@@ -1,7 +1,8 @@
package com.gitee.gen.service;
+import com.gitee.gen.config.ConnectConfig;
+import com.gitee.gen.config.DbTypeConfig;
import com.gitee.gen.entity.DatasourceConfig;
-import com.gitee.gen.gen.DbType;
import com.gitee.gen.mapper.DatasourceConfigMapper;
import org.apache.ibatis.solon.annotation.Db;
import org.noear.solon.annotation.Component;
@@ -27,9 +28,9 @@ public class DatasourceConfigService {
public void insert(DatasourceConfig templateConfig) {
templateConfig.setIsDeleted(0);
- DbType dbType = DbType.of(templateConfig.getDbType());
- if (dbType != null) {
- templateConfig.setDriverClass(dbType.getDriverClass());
+ ConnectConfig connectConfig = DbTypeConfig.getInstance().getConnectConfig(templateConfig.getDbType());
+ if (connectConfig != null) {
+ templateConfig.setDriverClass(connectConfig.getDriver());
}
datasourceConfigMapper.insert(templateConfig);
}
diff --git a/gen/src/main/java/com/gitee/gen/service/TemplateConfigService.java b/gen/src/main/java/com/gitee/gen/service/TemplateConfigService.java
index 591244ce6c11933df2bff07fa628ea59323345bf..e4b73b8e7580a2bdba52cffca9f40e0e84b1bee3 100644
--- a/gen/src/main/java/com/gitee/gen/service/TemplateConfigService.java
+++ b/gen/src/main/java/com/gitee/gen/service/TemplateConfigService.java
@@ -4,10 +4,10 @@ import com.gitee.gen.entity.TemplateConfig;
import com.gitee.gen.mapper.TemplateConfigMapper;
import com.gitee.gen.util.StringUtil;
import com.gitee.gen.util.TemplateMetaUtils;
+import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.ibatis.solon.annotation.Db;
import org.noear.solon.annotation.Component;
-import org.smartboot.http.common.utils.CollectionUtils;
import java.util.Collections;
import java.util.List;
diff --git a/gen/src/main/java/com/gitee/gen/service/UpgradeService.java b/gen/src/main/java/com/gitee/gen/service/UpgradeService.java
index 65f6aa10fa837ae2afae0b17be3b9b005471d6a6..79f8a79eb33694cd6b688ee61ccd94db6e8da2d4 100644
--- a/gen/src/main/java/com/gitee/gen/service/UpgradeService.java
+++ b/gen/src/main/java/com/gitee/gen/service/UpgradeService.java
@@ -50,10 +50,13 @@ public class UpgradeService {
@Inject
private SystemConfigService systemConfigService;
- @Inject("${gen.db1.driverClassName}")
+ @Inject("${dbms.enable:false}")
+ private boolean useDbms;
+
+ @Inject("${gen.db2.driverClassName}")
private String driverClassName;
- @Inject("${gen.db-name:gen}")
+ @Inject("${dbms.database:gen}")
private String dbName;
public void init() {
@@ -63,6 +66,10 @@ public class UpgradeService {
}
public void initDatabase() {
+ if (useDbms) {
+ log.info("使用DBMS,跳过sqlit3文件初始化");
+ return;
+ }
File dbFile = getDbFile();
if (!dbFile.exists()) {
try {
@@ -212,12 +219,17 @@ public class UpgradeService {
*/
public boolean addColumn(String tableName, String columnName, String type) {
if (!isColumnExist(tableName, columnName)) {
- if (isMysql()) {
- upgradeMapper.addColumnMysql(tableName, columnName, type);
- } else if (isDm()) {
- upgradeMapper.addColumnDm(tableName, columnName, type);
- } else {
- upgradeMapper.addColumn(tableName, columnName, type);
+ try {
+ if (isMysql()) {
+ upgradeMapper.addColumnMysql(tableName, columnName, type);
+ } else if (isDm()) {
+ upgradeMapper.addColumnDm(tableName, columnName, type);
+ } else {
+ upgradeMapper.addColumn(tableName, columnName, type);
+ }
+ } catch (Exception e) {
+ log.error("add column error, tableName={}, columnName={}, type={}",
+ tableName, columnName, type, e);
}
return true;
}
@@ -298,11 +310,11 @@ public class UpgradeService {
}
private boolean isMysql() {
- return this.driverClassName.contains("mysql");
+ return useDbms && this.driverClassName.contains("mysql");
}
private boolean isDm() {
- return this.driverClassName.contains("dm");
+ return useDbms && this.driverClassName.contains("dm");
}
}
diff --git a/gen/src/main/java/com/gitee/gen/util/TemplateMetaUtils.java b/gen/src/main/java/com/gitee/gen/util/TemplateMetaUtils.java
index 7dc53848417a3e20223b14c4b74445da717ea9fb..feba0d4f7ba8089f16802c93f7a63b57a3c9fcf1 100644
--- a/gen/src/main/java/com/gitee/gen/util/TemplateMetaUtils.java
+++ b/gen/src/main/java/com/gitee/gen/util/TemplateMetaUtils.java
@@ -31,11 +31,46 @@ public final class TemplateMetaUtils {
* 解析元数据信息
*/
public static Map parseMetaRow(String row) {
+ char[] charArray = row.toCharArray();
+
Map data = new HashMap<>();
- String[] paris = row.split("\\s*,\\s*");
- for (String item : paris) {
- String[] kv = item.split("=");
- data.put(kv[0].trim(), kv.length == 1 ? null : kv[1].trim());
+
+ StringBuilder kvBuilder = new StringBuilder();
+ // 剩余未闭合表达式数量
+ int leftExpr = 0;
+ for (int i = 0, len = charArray.length, end = len - 1; i < len; i++) {
+ char c = charArray[i];
+ boolean kvEnd = false;
+ if (i == end) {
+ kvBuilder.append(c);
+ kvEnd = true;
+ } else if (',' == c && leftExpr == 0) {
+ kvEnd = true;
+ }
+
+ if (kvEnd) {
+ String[] kv = kvBuilder.toString().trim().split("=");
+ data.put(kv[0].trim(), kv.length == 1 ? null : kv[1].trim());
+ kvBuilder = new StringBuilder();
+ continue;
+ }
+
+ kvBuilder.append(c);
+ switch (c) {
+ case '{':
+ case '(':
+ leftExpr++;
+ break;
+ case '}':
+ case ')':
+ leftExpr--;
+ break;
+ default:
+ break;
+ }
+ }
+ if (leftExpr > 0) {
+ throw new RuntimeException("读取元数据失败,有" + leftExpr + "个表达式未闭合");
}
return data;
}
diff --git a/gen/src/main/resources/app.yml b/gen/src/main/resources/app.yml
index 1909894913bebcb179e864719a5876c040475161..ac3e261d5c2083497ecf82385e2ce4ebb61b08b4 100644
--- a/gen/src/main/resources/app.yml
+++ b/gen/src/main/resources/app.yml
@@ -4,26 +4,27 @@ server:
port: 6969
-
# sqlite3
#LOCAL_DB: ${user.home}/gen.db
-DATASOURCE_URL: "jdbc:sqlite:"
-DATASOURCE_DRIVE: org.sqlite.JDBC
-DATASOURCE_USERNAME:
-DATASOURCE_PASSWORD:
+gen.db1:
+ url: "jdbc:sqlite:"
+ driverClassName: org.sqlite.JDBC
+ userName:
+ password:
# mysql
-#DATASOURCE_URL: jdbc:mysql://localhost:3306/gen?useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&serverTimezone=Asia/Shanghai
-#DATASOURCE_DRIVE: com.mysql.cj.jdbc.Driver
-#DATASOURCE_USERNAME: root
-#DATASOURCE_PASSWORD: root
+dbms:
+ enable: false
+# host: localhost:3306
+# database: gen
+# username: root
+# password: root
-# 配置数据源
-gen.db1:
- url: ${DATASOURCE_URL}
- driverClassName: ${DATASOURCE_DRIVE}
- userName: ${DATASOURCE_USERNAME}
- password: ${DATASOURCE_PASSWORD}
+gen.db2:
+ url: jdbc:mysql://${dbms.host:localhost:3306}/${dbms.database:gen}?useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&serverTimezone=Asia/Shanghai
+ driverClassName: com.mysql.cj.jdbc.Driver
+ userName: ${dbms.username:gen}
+ password: ${dbms.password:}
# 配置数据源对应的 mybatis 信息(要与 DataSource bean 的名字对上)
mybatis.db1:
@@ -55,3 +56,33 @@ solon.logging.appender:
level: INFO
enable: false #是否启用
+
+# 2.0.3新增
+# 连接方式管理, {HOST},{PORT},{DB_NAME} 表示占位符
+connect:
+ 1:
+ name: MySQL
+ driver: com.mysql.cj.jdbc.Driver
+ url: jdbc:mysql://{HOST}:{PORT}/{DB_NAME}?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai
+ serviceName: com.gitee.gen.gen.mysql.MySqlService
+ 2:
+ name: Oracle
+ driver: oracle.jdbc.driver.OracleDriver
+ url: jdbc:oracle:thin:@{HOST}:{PORT}{DB_NAME}
+ serviceName: com.gitee.gen.gen.oracle.OracleService
+ 3:
+ name: "SQL Server"
+ driver: com.microsoft.sqlserver.jdbc.SQLServerDriver
+ url: jdbc:sqlserver://{HOST}:{PORT};DatabaseName={DB_NAME};trustServerCertificate=true
+ serviceName: com.gitee.gen.gen.sqlserver.SqlServerService
+ 4:
+ name: PostgreSQL
+ driver: org.postgresql.Driver
+ url: jdbc:postgresql://{HOST}:{PORT}/{DB_NAME}
+ serviceName: com.gitee.gen.gen.postgresql.PostgreSqlService
+ 5:
+ name: "达梦"
+ driver: dm.jdbc.driver.DmDriver
+ url: jdbc:dm://{HOST}:{PORT}/{DB_NAME}
+ serviceName: com.gitee.gen.gen.dm.DmService
+
diff --git a/gen/src/main/resources/gen_init.db b/gen/src/main/resources/gen_init.db
index 2f69f8bd55d0313d70041b6be613986ecfeb7281..795f8f18ac3765db61be902861cd92d74cccccf6 100644
Binary files a/gen/src/main/resources/gen_init.db and b/gen/src/main/resources/gen_init.db differ
diff --git a/gen/src/main/resources/gen_init_v1.db b/gen/src/main/resources/gen_init_v1.db
new file mode 100644
index 0000000000000000000000000000000000000000..2f69f8bd55d0313d70041b6be613986ecfeb7281
Binary files /dev/null and b/gen/src/main/resources/gen_init_v1.db differ
diff --git a/readme.md b/readme.md
index 0933a84ff39c534837e444dfc6e12691f1eb35cf..8d4372386bc8045d274dfa9135c87a77b6db02a4 100644
--- a/readme.md
+++ b/readme.md
@@ -26,10 +26,6 @@
> 升级:后续升级覆盖gen.jar和dist文件夹即可
-- 数据库支持
-
-默认支持mysql数据库,如果要支持其他数据库将驱动包放到`gen/lib`下
-
### 方式2:docker运行
- 下载公共镜像
@@ -41,15 +37,14 @@
```shell
docker run --name gen --restart=always \
-p 6969:6969 \
- -e JAVA_OPTS="-server -Xms64m -Xmx64m -DLOCAL_DB=/opt/gen/gen.db" \
-v /opt/gen/:/opt/gen/ \
+ -v /opt/gen/conf/:/gen/conf/ \
+ -v /opt/gen/ext:/gen/ext \
-d registry.cn-hangzhou.aliyuncs.com/tanghc/gen:latest
```
浏览器访问`http://ip:6969/`
-后续更新替换jar文件和dist文件夹即可。
-
### 本地构建镜像
@@ -61,11 +56,56 @@ clone代码,然后执行`docker-build.sh`脚本
```shell
docker run --name gen --restart=always \
-p 6969:6969 \
- -e JAVA_OPTS="-server -Xms64m -Xmx64m -DLOCAL_DB=/opt/gen/gen.db" \
-v /opt/gen/:/opt/gen/ \
+ -v /opt/gen/conf/:/gen/conf/ \
+ -v /opt/gen/ext:/gen/ext \
-d <镜像ID>
```
+## 其它数据库支持
+
+
+默认支持mysql数据库,如果要支持其它数据库,如Oracle,步骤如下:
+
+- docker
+
+1. 将数据库驱动放到`/opt/gen/ext`下
+2. 重启docker
+
+- 本地运行
+
+1. 将数据库驱动放到`gen/ext`下
+2. 设置环境变量JAVA_HOME,指向java安装目录
+3. 编辑`run.sh`文件,添加启动参数:`-Djava.ext.dirs=$JAVA_HOME/jre/lib/ext:./ext`
+
+添加后如下:
+
+`java -Djava.ext.dirs=$JAVA_HOME/jre/lib/ext:./ext -Dsolon.config.add=./conf/app.yml -Duser.timezone=Asia/Shanghai -jar -Xms64m -Xmx64m gen.jar`
+
+执行`sh run.sh`启动
+
+
+## 使用Mysql存储
+
+默认使用SQLITE3存储,如果要使用mysql存储,需要做如下配置
+
+- 创建数据库, [SQL文件](https://gitee.com/durcframework/code-gen/blob/master/db/mysql.sql)
+- 打开app.yml文件,docker环境下在`/opt/gen/conf`下新建一个`app.yml`文件
+- 添加/修改如下配置
+
+```yaml
+dbms:
+ # 设置为true
+ enable: true
+ # 设置数据库地址,库名,连接账号
+ host: localhost:3306
+ database: gen
+ username: root
+ password: root
+```
+
+重启服务
+
## 其它
- [快速搭建SpringBoot+Mybatis应用](https://gitee.com/durcframework/code-gen/wikis/pages?sort_id=2478942&doc_id=27724)
@@ -84,6 +124,7 @@ docker run --name gen --restart=always \
- 运行`gen`下的`com.gitee.gen.App`(solon-web工程)
- 运行`front`下的前端项目,详见:[readme](./front/README.md)
+
## 参与贡献
欢迎贡献代码,完善功能,PR请提交到`pr`分支
diff --git a/templates/fastmybatis-v3.0/controller.vm b/templates/fastmybatis-v3.0/controller.vm
new file mode 100644
index 0000000000000000000000000000000000000000..fdf16790a9874482649a37939f1f67d453298a8c
--- /dev/null
+++ b/templates/fastmybatis-v3.0/controller.vm
@@ -0,0 +1,98 @@
+## filename=${context.javaBeanName}Controller.java, folder=controller
+#set($entityClass="${context.classNamePascal}")
+#set($entityObj="${context.classNameCamel}")
+#set($serviceClass="${context.classNamePascal}Service")
+#set($serviceObj="${context.classNameCamel}Service")
+#set($pkg="${context.packageName}")
+package ${pkg}.web.controller;
+
+import com.gitee.fastmybatis.core.PageInfo;
+import com.gitee.fastmybatis.core.query.Query;
+import com.gitee.fastmybatis.core.query.param.PageParam;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.DeleteMapping;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.PutMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import ${pkg}.dao.entity.${entityClass};
+import ${pkg}.service.${serviceClass}Service;
+
+import javax.validation.Valid;
+
+/**
+ * @author ${context.author}
+ */
+@RestController
+@RequestMapping("${table.tableName}")
+public class ${entityClass}Controller {
+
+ @Autowired
+ private ${serviceClass} ${serviceObj};
+
+ /**
+ * 分页查询
+ *
+ * @param param 请求参数
+ * @return 返回分页信息
+ */
+ @GetMapping("/page")
+ public Result> page(PageParam param) {
+ Query query = param.toQuery();
+ PageInfo<${entityClass}> pageInfo = ${serviceObj}.page(query);
+ return Result.ok(pageInfo);
+ }
+
+ /**
+ * 新增
+ *
+ * @param $entityObj
+ * @return 返回主键id
+ */
+ @PostMapping("/save")
+ public Result<${pk.javaTypeBox}> save(@Valid @RequestBody ${entityClass} $entityObj) {
+ ${serviceObj}.save($entityObj);
+ // 返回添加后的主键值
+ return Result.ok(${entityObj}.getId());
+ }
+
+ /**
+ * 详情
+ *
+ * @param id
+ * @return 返回详情
+ */
+ @GetMapping("/detail")
+ public Result<${entityClass}> detail(${pk.javaTypeBox} id) {
+ ${entityClass} record = ${serviceObj}.getById(id);
+ return Result.ok(record);
+ }
+
+ /**
+ * 修改
+ *
+ * @param $entityObj 表单数据
+ * @return
+ */
+ @PutMapping("/update")
+ public Result> update(@Valid @RequestBody ${entityClass} $entityObj) {
+ ${serviceObj}.update($entityObj);
+ return Result.ok();
+ }
+
+ /**
+ * 删除
+ *
+ * @param id 主键id
+ * @return
+ */
+ @DeleteMapping("/delete")
+ public Result> delete(${pk.javaTypeBox} id) {
+ ${serviceObj}.deleteById(id);
+ return Result.ok();
+ }
+
+}
diff --git a/templates/fastmybatis-v3.0/entity.vm b/templates/fastmybatis-v3.0/entity.vm
new file mode 100644
index 0000000000000000000000000000000000000000..ba1b9b7c79d65a38d8f9c614665b295618f2d16b
--- /dev/null
+++ b/templates/fastmybatis-v3.0/entity.vm
@@ -0,0 +1,58 @@
+## filename=${context.javaBeanName}.java, folder=entity
+#set($entityClass="${context.classNamePascal}")
+package ${context.packageName}.dao.entity;
+
+import com.gitee.fastmybatis.annotation.Pk;
+import com.gitee.fastmybatis.annotation.PkStrategy;
+import com.gitee.fastmybatis.annotation.Table;
+import com.gitee.fastmybatis.core.query.LambdaQuery;
+import lombok.Data;
+
+#if(${table.hasDateField})
+import java.util.Date;
+#end
+#if(${table.hasLocalDateField})
+import java.time.LocalDate;
+#end
+#if(${table.hasLocalDateTimeField})
+import java.time.LocalDateTime;
+#end
+#if(${table.hasBigDecimalField})
+import java.math.BigDecimal;
+#end
+
+/**
+ * 表名:${table.tableName}
+#if("$!{table.comment}" != "")
+ * 备注:${table.comment}
+#end
+ *
+ * @author ${context.author}
+ */
+@Table(name = "${table.tableName}", pk = @Pk(name = "${pk.columnName}", strategy = PkStrategy.INCREMENT))
+@Data
+public class ${entityClass} {
+
+#foreach($column in $columns)
+#if("$!{column.comment}" != "")
+ /**
+ * ${column.comment}
+ */
+#end
+#if(${column.columnName} == "is_deleted")
+ @com.gitee.fastmybatis.annotation.Column(logicDelete = true)
+#end
+ private ${column.javaTypeBox} ${column.javaFieldName};
+
+#end
+
+ /**
+ * 创建LambdaQuery对象
+ *
+ * @return 返回LambdaQuery对象
+ */
+ public static LambdaQuery<${entityClass}> query() {
+ return new LambdaQuery<>(${entityClass}.class);
+ }
+
+}
\ No newline at end of file
diff --git a/templates/fastmybatis-v3.0/mapper.vm b/templates/fastmybatis-v3.0/mapper.vm
new file mode 100644
index 0000000000000000000000000000000000000000..25a7fe51d446dd00ee19f095021616b49c8fb0cd
--- /dev/null
+++ b/templates/fastmybatis-v3.0/mapper.vm
@@ -0,0 +1,14 @@
+## filename=${context.javaBeanName}Mapper.java, folder=mapper
+#set($entityClass="${context.classNamePascal}")
+#set($pkg="${context.packageName}")
+package ${pkg}.dao.mapper;
+
+import com.gitee.fastmybatis.core.mapper.BaseMapper;
+import ${pkg}.dao.entity.${entityClass};
+
+/**
+ * @author ${context.author}
+ */
+public interface ${entityClass}Mapper extends BaseMapper<${entityClass}> {
+
+}
\ No newline at end of file
diff --git a/templates/fastmybatis-v3.0/service.vm b/templates/fastmybatis-v3.0/service.vm
new file mode 100644
index 0000000000000000000000000000000000000000..9c5fe1f0fd4dccb6f7336ded6741329d4fa98d05
--- /dev/null
+++ b/templates/fastmybatis-v3.0/service.vm
@@ -0,0 +1,19 @@
+## filename=${context.javaBeanName}Service.java, folder=service
+#set($entityClass="${context.classNamePascal}")
+#set($mapperClass="${context.classNamePascal}Mapper")
+#set($pkg="${context.packageName}")
+package ${pkg}.mapper;
+
+import com.gitee.fastmybatis.core.support.BaseLambdaService;
+import ${pkg}.dao.entity.${entityClass};
+import ${pkg}.dao.mapper.${mapperClass};
+import org.springframework.stereotype.Service;
+
+
+/**
+ * @author ${context.author}
+ */
+@Service
+public class ${entityClass}Service extends BaseLambdaService<${entityClass}, ${mapperClass}> {
+
+}
\ No newline at end of file