@@ -0,0 +1,27 @@ | |||
# ---> Java | |||
# Compiled class file | |||
*.class | |||
# Log file | |||
*.log | |||
# BlueJ files | |||
*.ctxt | |||
# Mobile Tools for Java (J2ME) | |||
.mtj.tmp/ | |||
# Package Files # | |||
*.jar | |||
*.war | |||
*.nar | |||
*.ear | |||
*.zip | |||
*.tar.gz | |||
*.rar | |||
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml | |||
hs_err_pid* | |||
.idea | |||
*.iml |
@@ -0,0 +1,34 @@ | |||
<?xml version="1.0" encoding="UTF-8"?> | |||
<project xmlns="http://maven.apache.org/POM/4.0.0" | |||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | |||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> | |||
<modelVersion>4.0.0</modelVersion> | |||
<groupId>cc.smtweb</groupId> | |||
<artifactId>smtweb-framework</artifactId> | |||
<packaging>pom</packaging> | |||
<version>2.2.0-SNAPSHOT</version> | |||
<parent> | |||
<groupId>org.springframework.boot</groupId> | |||
<artifactId>spring-boot-starter-parent</artifactId> | |||
<version>2.3.1.RELEASE</version> | |||
<relativePath/> <!-- lookup parent from repository --> | |||
</parent> | |||
<properties> | |||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> | |||
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> | |||
<java.version>1.8</java.version> | |||
<maven.compiler.source>1.8</maven.compiler.source> | |||
<maven.compiler.target>1.8</maven.compiler.target> | |||
<skipTests>true</skipTests> | |||
</properties> | |||
<modules> | |||
<module>sw-framework-core</module> | |||
<module>sw-framework-auth</module> | |||
<module>sw-framework-file</module> | |||
<module>sw-framework-web</module> | |||
<module>../smtweb-system/sw-system-bpm</module> | |||
</modules> | |||
</project> |
@@ -0,0 +1,132 @@ | |||
<?xml version="1.0" encoding="UTF-8"?> | |||
<project xmlns="http://maven.apache.org/POM/4.0.0" | |||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | |||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> | |||
<groupId>cc.smtweb</groupId> | |||
<artifactId>sw-framework-auth</artifactId> | |||
<version>2.2.0-SNAPSHOT</version> | |||
<parent> | |||
<groupId>org.springframework.boot</groupId> | |||
<artifactId>spring-boot-starter-parent</artifactId> | |||
<version>2.5.6</version> | |||
<relativePath/> <!-- lookup parent from repository --> | |||
</parent> | |||
<modelVersion>4.0.0</modelVersion> | |||
<dependencies> | |||
<dependency> | |||
<groupId>org.springframework.boot</groupId> | |||
<artifactId>spring-boot-starter-web</artifactId> | |||
</dependency> | |||
<dependency> | |||
<groupId>cc.smtweb</groupId> | |||
<artifactId>sw-framework-core</artifactId> | |||
<version>2.2.0-SNAPSHOT</version> | |||
</dependency> | |||
<dependency> | |||
<groupId>org.springframework.boot</groupId> | |||
<artifactId>spring-boot-test</artifactId> | |||
<scope>test</scope> | |||
</dependency> | |||
<!-- https://mvnrepository.com/artifact/org.junit.platform/junit-platform-launcher --> | |||
<dependency> | |||
<groupId>org.junit.platform</groupId> | |||
<artifactId>junit-platform-launcher</artifactId> | |||
<version>1.6.2</version> | |||
<scope>test</scope> | |||
</dependency> | |||
<!-- https://mvnrepository.com/artifact/org.junit.jupiter/junit-jupiter-api --> | |||
<dependency> | |||
<groupId>org.junit.jupiter</groupId> | |||
<artifactId>junit-jupiter-api</artifactId> | |||
<version>5.6.2</version> | |||
<scope>test</scope> | |||
</dependency> | |||
<!-- https://mvnrepository.com/artifact/org.junit.jupiter/junit-jupiter-engine --> | |||
<dependency> | |||
<groupId>org.junit.jupiter</groupId> | |||
<artifactId>junit-jupiter-engine</artifactId> | |||
<version>5.6.2</version> | |||
<scope>test</scope> | |||
</dependency> | |||
<dependency> | |||
<groupId>org.junit.vintage</groupId> | |||
<artifactId>junit-vintage-engine</artifactId> | |||
<version>5.6.2</version> | |||
<scope>test</scope> | |||
</dependency> | |||
<dependency> | |||
<groupId>org.junit.jupiter</groupId> | |||
<artifactId>junit-jupiter-params</artifactId> | |||
<version>5.6.2</version> | |||
<scope>test</scope> | |||
</dependency> | |||
<dependency> | |||
<groupId>org.springframework</groupId> | |||
<artifactId>spring-test</artifactId> | |||
<version>5.2.7.RELEASE</version> | |||
<scope>test</scope> | |||
</dependency> | |||
<!-- https://mvnrepository.com/artifact/org.mockito/mockito-all --> | |||
<dependency> | |||
<groupId>org.mockito</groupId> | |||
<artifactId>mockito-all</artifactId> | |||
<version>1.10.19</version> | |||
<scope>test</scope> | |||
</dependency> | |||
</dependencies> | |||
<build> | |||
<!-- <finalName>user</finalName>--> | |||
<plugins> | |||
<plugin> | |||
<groupId>org.apache.maven.plugins</groupId> | |||
<artifactId>maven-source-plugin</artifactId> | |||
<executions> | |||
<execution> | |||
<id>attach-sources</id> | |||
<phase>verify</phase> | |||
<goals> | |||
<goal>jar-no-fork</goal> | |||
</goals> | |||
</execution> | |||
</executions> | |||
</plugin> | |||
<!-- 用于生成jar包的plugin --> | |||
<plugin> | |||
<groupId>org.apache.maven.plugins</groupId> | |||
<artifactId>maven-jar-plugin</artifactId> | |||
<version>2.6</version> | |||
<configuration> | |||
<excludes> | |||
<exclude>config/*.yaml</exclude> | |||
</excludes> | |||
</configuration> | |||
</plugin> | |||
</plugins> | |||
<!-- <resources>--> | |||
<!-- <resource>--> | |||
<!-- <filtering>true</filtering>--> | |||
<!-- <directory>src/main/resources</directory>--> | |||
<!-- <excludes>--> | |||
<!-- <exclude>config/*.yaml</exclude>--> | |||
<!-- </excludes>--> | |||
<!-- </resource>--> | |||
<!-- </resources>--> | |||
</build> | |||
<distributionManagement> | |||
<repository> | |||
<id>nexus-releases</id> | |||
<name>Nexus Release Repository</name> | |||
<url>http://47.92.149.153:7000/repository/maven-releases/</url> | |||
</repository> | |||
<snapshotRepository> | |||
<id>nexus-snapshots</id> | |||
<name>Nexus Snapshot Repository</name> | |||
<url>http://47.92.149.153:7000/repository/maven-snapshots/</url> | |||
</snapshotRepository> | |||
</distributionManagement> | |||
</project> |
@@ -0,0 +1,66 @@ | |||
package cc.smtweb.framework.auth.captcha; | |||
import javax.imageio.ImageIO; | |||
import java.awt.*; | |||
import java.awt.image.BufferedImage; | |||
import java.io.File; | |||
import java.io.IOException; | |||
public class ImageMaker { | |||
private static final String signChar = "()+-*/="; | |||
// 生成png图片 | |||
public void make() throws IOException { | |||
int fontHeight = 34; | |||
int width = 19; | |||
int height = fontHeight * (62 + signChar.length()); | |||
// 创建BufferedImage对象 | |||
BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); | |||
// ---------- 增加下面的代码使得背景透明 ----------------- | |||
Graphics2D g2d = image.createGraphics(); | |||
image = g2d.getDeviceConfiguration().createCompatibleImage(width, height, Transparency.TRANSLUCENT); | |||
g2d.dispose(); | |||
// 画图 | |||
g2d = image.createGraphics(); | |||
g2d.setColor(new Color(0,0,0)); | |||
// 画图 | |||
Font font = new Font("Courier New", Font.BOLD, 30); | |||
g2d.setFont(font); | |||
System.out.println(font.getMaxCharBounds(g2d.getFontRenderContext())); | |||
int top = 25; | |||
for (int i = 0; i < 10; i++) { | |||
g2d.drawString(i + "", 0, top); | |||
top += fontHeight; | |||
} | |||
for (int i = 0; i < 26; i++) { | |||
g2d.drawString((char)(i + (int)'a') + "", 0, top); | |||
top += fontHeight; | |||
} | |||
for (int i = 0; i < 26; i++) { | |||
g2d.drawString((char)(i + (int)'A') + "", 0, top); | |||
top += fontHeight; | |||
} | |||
for (int i = 0; i < signChar.length(); i++) { | |||
g2d.drawString(signChar.charAt(i) + "", 0, top); | |||
top += fontHeight; | |||
} | |||
// 释放对象 | |||
g2d.dispose(); | |||
// 保存文件 | |||
ImageIO.write(image, "png", new File("/var/tmp/test.png")); | |||
} | |||
public static void main(String[] args) throws IOException { | |||
new ImageMaker().make(); | |||
} | |||
} |
@@ -0,0 +1,21 @@ | |||
package cc.smtweb.framework.auth.spring; | |||
import cc.smtweb.framework.core.mvc.config.ControllerConfig; | |||
import org.springframework.context.annotation.Bean; | |||
import org.springframework.context.annotation.ComponentScan; | |||
import org.springframework.context.annotation.Configuration; | |||
/** | |||
* @author kevin | |||
*/ | |||
@Configuration | |||
@ComponentScan | |||
public class AuthAutoConfiguration { | |||
/** 配置自定义service扫描路径 {module}/{service}/{method} */ | |||
@Bean | |||
public ControllerConfig authControllerConfig() { | |||
return new ControllerConfig("auth", "cc.smtweb.framework.auth.web"); | |||
} | |||
} |
@@ -0,0 +1,60 @@ | |||
package cc.smtweb.framework.auth.spring.cache; | |||
import cc.smtweb.framework.core.annotation.SwCache; | |||
import cc.smtweb.framework.core.cache.AbstractCache; | |||
import cc.smtweb.framework.core.db.jdbc.JdbcEngine; | |||
import cc.smtweb.framework.core.mvc.realm.service.PermChecker; | |||
import lombok.extern.log4j.Log4j2; | |||
import org.springframework.beans.factory.annotation.Autowired; | |||
import java.util.*; | |||
/** | |||
* 用户权限集合缓存器,@todo 权限部分需要重写 | |||
* @author xkliu | |||
*/ | |||
@Log4j2 | |||
@SwCache(ident = "PERM_CHECKER", title = "用户权限") | |||
public class RealmCache extends AbstractCache<PermChecker> { | |||
@Autowired | |||
private JdbcEngine dbEngine; | |||
private static final long ENTERPRISE_ADMIN_ID = 1; | |||
@Override | |||
protected String getId(PermChecker bean) { | |||
return "1";//todo | |||
} | |||
@Override | |||
protected List<PermChecker> loadAll() { | |||
return null; | |||
} | |||
protected PermChecker load(Long key) { | |||
// admin | |||
if (key == ENTERPRISE_ADMIN_ID) { | |||
return PermChecker.build(new HashSet<>(Collections.singletonList("*"))); | |||
} | |||
// TODO: 合并相同角色,自己到缓存里面获取 | |||
Set<String> permissions = new HashSet<>(); | |||
List<String> permList = dbEngine.queryStringList("SELECT menu_api_perm FROM sys_menu WHERE menu_id in\n" + | |||
"(SELECT rmp_menu_id from sys_role_menu_privilege WHERE rmp_role_id in\n" + | |||
"(SELECT role_id FROM sw_user.sys_role WHERE role_id IN" + | |||
"(SELECT ur_role_id FROM sw_user.sys_user_role WHERE ur_user_id=?)))", key); | |||
if (permList != null) { | |||
for (String perm: permList) { | |||
for (String item: perm.split(",")) { | |||
permissions.add(item.trim()); | |||
} | |||
} | |||
} | |||
return PermChecker.build(permissions); | |||
} | |||
} |
@@ -0,0 +1,11 @@ | |||
package cc.smtweb.framework.auth.web.entity; | |||
import lombok.Data; | |||
@Data | |||
public class LoginAckVO { | |||
private Long userId; | |||
private String userAvatar; | |||
private String userName; | |||
private String token; | |||
} |
@@ -0,0 +1,12 @@ | |||
package cc.smtweb.framework.auth.web.entity; | |||
import lombok.Data; | |||
import java.io.Serializable; | |||
@Data | |||
public class LoginVO implements Serializable { | |||
private String username; | |||
private String password; | |||
} |
@@ -0,0 +1,29 @@ | |||
package cc.smtweb.framework.auth.web.entity; | |||
import cc.smtweb.framework.core.annotation.SwColumn; | |||
import cc.smtweb.framework.core.annotation.SwTable; | |||
import lombok.Data; | |||
import java.io.Serializable; | |||
@Data | |||
@SwTable("sw_user.sys_menu") | |||
public class MenuPO implements Serializable { | |||
@SwColumn(type={SwColumn.Type.ID}) | |||
private Long menuId; | |||
@SwColumn(type={SwColumn.Type.PARENT_ID}) | |||
private Long menuParentId; | |||
private String menuName; | |||
private Long menuSiteId; | |||
private String menuPermiss; | |||
private String menuUrl; | |||
@SwColumn(type={SwColumn.Type.ORDER}) | |||
private Integer menuSort; | |||
} |
@@ -0,0 +1,28 @@ | |||
package cc.smtweb.framework.auth.web.entity; | |||
import cc.smtweb.framework.core.annotation.SwColumn; | |||
import cc.smtweb.framework.core.annotation.SwTable; | |||
import lombok.Data; | |||
import java.io.Serializable; | |||
@Data | |||
@SwTable("sw_user.sys_user") | |||
public class UserPO implements Serializable { | |||
@SwColumn(type={SwColumn.Type.ID}) | |||
private Long userId; | |||
private String userNickCode; | |||
private String userNickName; | |||
private Long userCreatePartyId; | |||
private String userPwd; | |||
private String userPhone; | |||
private Integer userStatus; | |||
private String userAvatar; | |||
} |
@@ -0,0 +1,89 @@ | |||
package cc.smtweb.framework.auth.web.service; | |||
import cc.smtweb.framework.auth.web.entity.LoginAckVO; | |||
import cc.smtweb.framework.auth.web.entity.LoginVO; | |||
import cc.smtweb.framework.auth.web.entity.UserPO; | |||
import cc.smtweb.framework.core.*; | |||
import cc.smtweb.framework.core.annotation.*; | |||
import cc.smtweb.framework.core.session.SessionManager; | |||
import cc.smtweb.framework.core.session.UserSession; | |||
import cc.smtweb.framework.core.db.DbEngine; | |||
import lombok.extern.slf4j.Slf4j; | |||
import org.apache.commons.codec.digest.DigestUtils; | |||
import org.apache.commons.lang3.StringUtils; | |||
@Slf4j | |||
@SwService | |||
public class AuthService { | |||
@SwParam | |||
private DbEngine dbEngine; | |||
@SwParam | |||
private SessionManager sessionManager; | |||
@SwPerm(SwPerm.NONE) | |||
public R login(@SwBody LoginVO loginPO) { | |||
if (StringUtils.isBlank(loginPO.getUsername())) { | |||
return R.error("账号不能为空"); | |||
} | |||
if (StringUtils.isBlank(loginPO.getPassword())) { | |||
return R.error("密码不能为空"); | |||
} | |||
UserPO user = dbEngine.queryEntity("select user_id,user_nick_name,user_nick_code,user_pwd,user_create_party_id from sw_user.sys_user where user_nick_code=?", UserPO.class, loginPO.getUsername()); | |||
if (user == null) { | |||
return R.error("账号不存在"); | |||
} | |||
// digest:md5("goodpj" + user.userId + password) | |||
String pass = DigestUtils.md5Hex("goodpj" + user.getUserId() + loginPO.getPassword()); | |||
if (!pass.equals(user.getUserPwd())) { | |||
return R.error("账号或者密码出错"); | |||
} | |||
UserSession userSession = new UserSession(); | |||
userSession.setUserId(user.getUserId()); | |||
userSession.setSiteId(user.getUserCreatePartyId()); | |||
String token = sessionManager.login(userSession); | |||
LoginAckVO data = new LoginAckVO(); | |||
data.setUserId(user.getUserId()); | |||
data.setUserName(user.getUserNickName()); | |||
data.setUserAvatar(user.getUserAvatar()); | |||
data.setToken(token); | |||
return R.success(data); | |||
} | |||
@SwPerm() | |||
public R ping(@SwParam("msg") String msg) { | |||
return R.success(msg); | |||
} | |||
@SwPerm("user:edit") | |||
public R config(@SwParam("username") String username) { | |||
return R.success("config: " + username); | |||
} | |||
/** | |||
* 退出登录 | |||
* @return code | |||
*/ | |||
public R logout() { | |||
sessionManager.logout(); | |||
return R.success(); | |||
} | |||
// defaultRun 命名的函数是默认函数 | |||
// @SwPerm(SwPerm.NONE) | |||
// public R defaultRun(@SwPathParam String path) { | |||
// return R.success(path).put("dao", authDao); | |||
// } | |||
} |
@@ -0,0 +1,2 @@ | |||
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ | |||
cc.smtweb.framework.auth.spring.AuthAutoConfiguration |
@@ -0,0 +1,64 @@ | |||
smtweb: | |||
machine-id: 1 | |||
file: | |||
local-path: /data/files/ | |||
host: http://127.0.0.1 | |||
url: http://127.0.0.1:8888/files/ | |||
db: | |||
default: | |||
rule: | |||
prefix: _smt_ | |||
replace: smt_ | |||
server: | |||
port: 8888 | |||
servlet: | |||
context-path: / | |||
feign: | |||
hystrix: | |||
enabled: false | |||
logging: | |||
level: | |||
root: INFO | |||
cc.smtweb: DEBUG | |||
spring: | |||
# 设置服务名 | |||
application: | |||
name: smtweb_user | |||
main: | |||
allow-bean-definition-overriding: true | |||
banner-mode: console | |||
mvc: | |||
static-path-pattern: /static/** | |||
redis: | |||
host: 127.0.0.1 | |||
port: 6379 | |||
password: | |||
datasource: | |||
driver-class-name: com.mysql.cj.jdbc.Driver | |||
url: jdbc:mysql://127.0.0.1:3306/smt_user?useUnicode=true&characterEncoding=utf-8&useTimezone=true&serverTimezone=CTT&allowMultiQueries=true | |||
username: root | |||
password: 1681860 | |||
# 连接池配置 | |||
hikari: | |||
pool-name: HikariPool | |||
minimum-idle: 10 | |||
maximum-pool-size: 100 | |||
connection-timeout: 60000 | |||
idle-timeout: 600000 | |||
max-lifetime: 1800000 | |||
servlet: | |||
multipart: | |||
max-file-size: 104857600000 | |||
max-request-size: 10485760000000 | |||
profiles: | |||
include: role | |||
cache: | |||
type: caffeine | |||
cache-names: | |||
- core | |||
caffeine: | |||
spec: maximumSize=1024,expireAfterWrite=2h | |||
@@ -0,0 +1,35 @@ | |||
sme: | |||
machine-id: 1 | |||
file-local-path: /data/files/smart/ | |||
file-host: http://auth.smtweb.cc | |||
file-url: ${sme.file-host}:${server.port}${server.servlet.context-path}/${sme.file-local-path} | |||
server: | |||
port: 10001 | |||
servlet: | |||
context-path: / | |||
logging: | |||
level: | |||
smtweb: DEBUG | |||
spring: | |||
main: | |||
allow-bean-definition-overriding: true | |||
mvc: | |||
static-path-pattern: /static/** | |||
redis: | |||
host: 127.0.0.1 | |||
port: 6379 | |||
datasource: | |||
user: | |||
driver-class-name: com.mysql.cj.jdbc.Driver | |||
url: jdbc:mysql://127.0.0.1:3306/smt_user?useUnicode=true&characterEncoding=utf-8&useTimezone=true&serverTimezone=CTT&allowMultiQueries=true | |||
username: smt | |||
password: smt_123456 | |||
servlet: | |||
multipart: | |||
max-file-size: 104857600000 | |||
max-request-size: 10485760000000 | |||
@@ -0,0 +1,3 @@ | |||
spring: | |||
profiles: | |||
active: dev |
@@ -0,0 +1,2 @@ | |||
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ | |||
cc.smtweb.framework.auth.spring.AuthAutoConfiguration |
@@ -0,0 +1,64 @@ | |||
smtweb: | |||
machine-id: 1 | |||
file: | |||
local-path: /data/files/ | |||
host: http://127.0.0.1 | |||
url: http://127.0.0.1:8888/files/ | |||
db: | |||
default: | |||
rule: | |||
prefix: _smt_ | |||
replace: smt_ | |||
server: | |||
port: 8888 | |||
servlet: | |||
context-path: / | |||
feign: | |||
hystrix: | |||
enabled: false | |||
logging: | |||
level: | |||
root: INFO | |||
cc.smtweb: DEBUG | |||
spring: | |||
# 设置服务名 | |||
application: | |||
name: smtweb_user | |||
main: | |||
allow-bean-definition-overriding: true | |||
banner-mode: console | |||
mvc: | |||
static-path-pattern: /static/** | |||
redis: | |||
host: 127.0.0.1 | |||
port: 6379 | |||
password: | |||
datasource: | |||
driver-class-name: com.mysql.cj.jdbc.Driver | |||
url: jdbc:mysql://127.0.0.1:3306/smt_user?useUnicode=true&characterEncoding=utf-8&useTimezone=true&serverTimezone=CTT&allowMultiQueries=true | |||
username: root | |||
password: 1681860 | |||
# 连接池配置 | |||
hikari: | |||
pool-name: HikariPool | |||
minimum-idle: 10 | |||
maximum-pool-size: 100 | |||
connection-timeout: 60000 | |||
idle-timeout: 600000 | |||
max-lifetime: 1800000 | |||
servlet: | |||
multipart: | |||
max-file-size: 104857600000 | |||
max-request-size: 10485760000000 | |||
profiles: | |||
include: role | |||
cache: | |||
type: caffeine | |||
cache-names: | |||
- core | |||
caffeine: | |||
spec: maximumSize=1024,expireAfterWrite=2h | |||
@@ -0,0 +1,35 @@ | |||
sme: | |||
machine-id: 1 | |||
file-local-path: /data/files/smart/ | |||
file-host: http://auth.smtweb.cc | |||
file-url: ${sme.file-host}:${server.port}${server.servlet.context-path}/${sme.file-local-path} | |||
server: | |||
port: 10001 | |||
servlet: | |||
context-path: / | |||
logging: | |||
level: | |||
smtweb: DEBUG | |||
spring: | |||
main: | |||
allow-bean-definition-overriding: true | |||
mvc: | |||
static-path-pattern: /static/** | |||
redis: | |||
host: 127.0.0.1 | |||
port: 6379 | |||
datasource: | |||
user: | |||
driver-class-name: com.mysql.cj.jdbc.Driver | |||
url: jdbc:mysql://127.0.0.1:3306/smt_user?useUnicode=true&characterEncoding=utf-8&useTimezone=true&serverTimezone=CTT&allowMultiQueries=true | |||
username: smt | |||
password: smt_123456 | |||
servlet: | |||
multipart: | |||
max-file-size: 104857600000 | |||
max-request-size: 10485760000000 | |||
@@ -0,0 +1,3 @@ | |||
spring: | |||
profiles: | |||
active: dev |
@@ -0,0 +1,5 @@ | |||
#Generated by Apache Maven | |||
#Tue Nov 02 19:04:59 CST 2021 | |||
version=2.1.0-SNAPSHOT | |||
groupId=cc.smtweb | |||
artifactId=sw-framework-auth |
@@ -0,0 +1,9 @@ | |||
E:\jujia\git\6.0\sw\smtweb-framework\sw-framework-auth\src\main\java\cc\smtweb\framework\auth\web\entity\LoginAckVO.java | |||
E:\jujia\git\6.0\sw\smtweb-framework\sw-framework-auth\src\main\java\cc\smtweb\framework\auth\web\entity\UserPO.java | |||
E:\jujia\git\6.0\sw\smtweb-framework\sw-framework-auth\src\main\java\cc\smtweb\framework\auth\spring\cache\RealmCache.java | |||
E:\jujia\git\6.0\sw\smtweb-framework\sw-framework-auth\src\main\java\cc\smtweb\framework\auth\AuthApplication.java | |||
E:\jujia\git\6.0\sw\smtweb-framework\sw-framework-auth\src\main\java\cc\smtweb\framework\auth\web\entity\MenuPO.java | |||
E:\jujia\git\6.0\sw\smtweb-framework\sw-framework-auth\src\main\java\cc\smtweb\framework\auth\web\entity\LoginVO.java | |||
E:\jujia\git\6.0\sw\smtweb-framework\sw-framework-auth\src\main\java\cc\smtweb\framework\auth\captcha\ImageMaker.java | |||
E:\jujia\git\6.0\sw\smtweb-framework\sw-framework-auth\src\main\java\cc\smtweb\framework\auth\spring\AuthAutoConfiguration.java | |||
E:\jujia\git\6.0\sw\smtweb-framework\sw-framework-auth\src\main\java\cc\smtweb\framework\auth\web\service\AuthService.java |
@@ -0,0 +1,8 @@ | |||
20210710 | |||
1. 定时器手动触发 (done) | |||
2. 优化BeanReadUtils的TableName预先进行转换 | |||
2. updateEntity支持悲观锁,并能抛出异常 | |||
3. 缓存支持自定义 key | |||
4. 序列号生成器 | |||
5. redis配置database(0~15) (done) |
@@ -0,0 +1,202 @@ | |||
<?xml version="1.0" encoding="UTF-8"?> | |||
<project xmlns="http://maven.apache.org/POM/4.0.0" | |||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | |||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> | |||
<groupId>cc.smtweb</groupId> | |||
<artifactId>sw-framework-core</artifactId> | |||
<version>2.2.0-SNAPSHOT</version> | |||
<parent> | |||
<groupId>org.springframework.boot</groupId> | |||
<artifactId>spring-boot-starter-parent</artifactId> | |||
<version>2.5.6</version> | |||
<relativePath/> <!-- lookup parent from repository --> | |||
</parent> | |||
<modelVersion>4.0.0</modelVersion> | |||
<properties> | |||
<!-- <maven.build.timestamp.format>MMddHH</maven.build.timestamp.format>--> | |||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> | |||
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> | |||
<java.version>1.8</java.version> | |||
<maven.compiler.source>1.8</maven.compiler.source> | |||
<maven.compiler.target>1.8</maven.compiler.target> | |||
<skipTests>true</skipTests> | |||
</properties> | |||
<dependencies> | |||
<dependency> | |||
<groupId>org.springframework.boot</groupId> | |||
<artifactId>spring-boot-starter</artifactId> | |||
</dependency> | |||
<dependency> | |||
<groupId>org.springframework.boot</groupId> | |||
<artifactId>spring-boot-starter-web</artifactId> | |||
</dependency> | |||
<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-core --> | |||
<dependency> | |||
<groupId>com.fasterxml.jackson.core</groupId> | |||
<artifactId>jackson-core</artifactId> | |||
</dependency> | |||
<!-- 引入jdbc 依赖 --> | |||
<dependency> | |||
<groupId>org.springframework.boot</groupId> | |||
<artifactId>spring-boot-starter-jdbc</artifactId> | |||
</dependency> | |||
<!--janino,经过测试,强项是动态脚本,作为表达式引擎,性能比不上jexl,暂舍弃 | |||
<dependency> | |||
<groupId>org.codehaus.janino</groupId> | |||
<artifactId>janino</artifactId> | |||
<version>3.0.11</version> | |||
</dependency>--> | |||
<!-- Redis Lettuce --> | |||
<dependency> | |||
<groupId>io.lettuce</groupId> | |||
<artifactId>lettuce-core</artifactId> | |||
<version>6.1.5.RELEASE</version> | |||
</dependency> | |||
<dependency> | |||
<groupId>org.apache.commons</groupId> | |||
<artifactId>commons-pool2</artifactId> | |||
<version>2.11.1</version> | |||
</dependency> | |||
<!-- https://mvnrepository.com/artifact/commons-codec/commons-codec --> | |||
<dependency> | |||
<groupId>commons-codec</groupId> | |||
<artifactId>commons-codec</artifactId> | |||
<version>1.15</version> | |||
</dependency> | |||
<!-- 将作为Redis对象序列化器 --> | |||
<dependency> | |||
<groupId>com.esotericsoftware</groupId> | |||
<artifactId>kryo</artifactId> | |||
<version>4.0.0</version> | |||
</dependency> | |||
<dependency> | |||
<groupId>org.projectlombok</groupId> | |||
<artifactId>lombok</artifactId> | |||
<version>1.18.22</version> | |||
</dependency> | |||
<!-- https://mvnrepository.com/artifact/org.apache.commons/commons-lang3 --> | |||
<dependency> | |||
<groupId>org.apache.commons</groupId> | |||
<artifactId>commons-lang3</artifactId> | |||
<version>3.12.0</version> | |||
</dependency> | |||
<!-- https://mvnrepository.com/artifact/org.apache.commons/commons-jexl3 --> | |||
<dependency> | |||
<groupId>org.apache.commons</groupId> | |||
<artifactId>commons-jexl3</artifactId> | |||
<version>3.2.1</version> | |||
</dependency> | |||
<!-- https://mvnrepository.com/artifact/org.apache.tika/tika-core --> | |||
<dependency> | |||
<groupId>org.apache.tika</groupId> | |||
<artifactId>tika-core</artifactId> | |||
<version>2.1.0</version> | |||
</dependency> | |||
<!-- 本地缓存 --> | |||
<dependency> | |||
<groupId>com.github.ben-manes.caffeine</groupId> | |||
<artifactId>caffeine</artifactId> | |||
</dependency> | |||
<!-- jdbc驱动 --> | |||
<dependency> | |||
<groupId>mysql</groupId> | |||
<artifactId>mysql-connector-java</artifactId> | |||
<version>8.0.27</version> | |||
<scope>runtime</scope> | |||
</dependency> | |||
<dependency> | |||
<groupId>commons-collections</groupId> | |||
<artifactId>commons-collections</artifactId> | |||
<version>3.2.2</version> | |||
<scope>compile</scope> | |||
</dependency> | |||
<!-- https://mvnrepository.com/artifact/org.junit.platform/junit-platform-launcher --> | |||
<dependency> | |||
<groupId>org.junit.platform</groupId> | |||
<artifactId>junit-platform-launcher</artifactId> | |||
<version>1.6.2</version> | |||
<scope>test</scope> | |||
</dependency> | |||
<!-- https://mvnrepository.com/artifact/org.junit.jupiter/junit-jupiter-api --> | |||
<dependency> | |||
<groupId>org.junit.jupiter</groupId> | |||
<artifactId>junit-jupiter-api</artifactId> | |||
<version>5.6.2</version> | |||
<scope>test</scope> | |||
</dependency> | |||
<!-- https://mvnrepository.com/artifact/org.junit.jupiter/junit-jupiter-engine --> | |||
<dependency> | |||
<groupId>org.junit.jupiter</groupId> | |||
<artifactId>junit-jupiter-engine</artifactId> | |||
<version>5.6.2</version> | |||
<scope>test</scope> | |||
</dependency> | |||
<dependency> | |||
<groupId>org.junit.vintage</groupId> | |||
<artifactId>junit-vintage-engine</artifactId> | |||
<version>5.6.2</version> | |||
<scope>test</scope> | |||
</dependency> | |||
<dependency> | |||
<groupId>org.junit.jupiter</groupId> | |||
<artifactId>junit-jupiter-params</artifactId> | |||
<version>5.6.2</version> | |||
<scope>test</scope> | |||
</dependency> | |||
</dependencies> | |||
<build> | |||
<plugins> | |||
<plugin> | |||
<groupId>org.apache.maven.plugins</groupId> | |||
<artifactId>maven-source-plugin</artifactId> | |||
<executions> | |||
<execution> | |||
<id>attach-sources</id> | |||
<phase>verify</phase> | |||
<goals> | |||
<goal>jar-no-fork</goal> | |||
</goals> | |||
</execution> | |||
</executions> | |||
</plugin> | |||
<!-- 用于生成jar包的plugin --> | |||
<plugin> | |||
<groupId>org.apache.maven.plugins</groupId> | |||
<artifactId>maven-jar-plugin</artifactId> | |||
<configuration> | |||
<excludes> | |||
<exclude>config/*.yaml</exclude> | |||
</excludes> | |||
</configuration> | |||
</plugin> | |||
</plugins> | |||
<!-- <resources>--> | |||
<!-- <resources>--> | |||
<!-- <resource>--> | |||
<!-- <filtering>true</filtering>--> | |||
<!-- <directory>src/main/resources</directory>--> | |||
<!-- <excludes>--> | |||
<!-- <exclude>config/*.yaml</exclude>--> | |||
<!-- </excludes>--> | |||
<!-- </resource>--> | |||
<!-- </resources>--> | |||
</build> | |||
<distributionManagement> | |||
<repository> | |||
<id>nexus-releases</id> | |||
<name>Nexus Release Repository</name> | |||
<url>http://47.92.149.153:7000/repository/maven-releases/</url> | |||
</repository> | |||
<snapshotRepository> | |||
<id>nexus-snapshots</id> | |||
<name>Nexus Snapshot Repository</name> | |||
<url>http://47.92.149.153:7000/repository/maven-snapshots/</url> | |||
</snapshotRepository> | |||
</distributionManagement> | |||
</project> |
@@ -0,0 +1,11 @@ | |||
package cc.smtweb.framework.core; | |||
import org.springframework.boot.SpringApplication; | |||
import org.springframework.boot.autoconfigure.SpringBootApplication; | |||
@SpringBootApplication | |||
public class CoreApplication { | |||
public static void main(String[] args) { | |||
SpringApplication.run(CoreApplication.class, args); | |||
} | |||
} |
@@ -0,0 +1,33 @@ | |||
package cc.smtweb.framework.core; | |||
import cc.smtweb.framework.core.cache.CacheManager; | |||
import cc.smtweb.framework.core.mvc.controller.scan.ApplicationScanner; | |||
import cc.smtweb.framework.core.systask.TaskStartEvent; | |||
import cc.smtweb.framework.core.systask.WebStartedEvent; | |||
import lombok.SneakyThrows; | |||
import org.springframework.boot.context.event.ApplicationStartedEvent; | |||
import org.springframework.context.ApplicationListener; | |||
import org.springframework.context.ConfigurableApplicationContext; | |||
import org.springframework.stereotype.Component; | |||
/** | |||
* 执行接口扫描任务 | |||
*/ | |||
@Component | |||
public class CoreApplicationStartedListener implements ApplicationListener<ApplicationStartedEvent> { | |||
@SneakyThrows | |||
@Override | |||
public void onApplicationEvent(ApplicationStartedEvent event) { | |||
System.out.println("onApplicationEvent============="); | |||
ConfigurableApplicationContext applicationContext = event.getApplicationContext(); | |||
applicationContext.publishEvent(new TaskStartEvent()); | |||
//包扫描 | |||
ApplicationScanner.scan(applicationContext); | |||
//初始化缓存 | |||
CacheManager.getIntance().init(); | |||
// 通知 controller 正式使用 | |||
applicationContext.publishEvent(new WebStartedEvent()); | |||
} | |||
} |
@@ -0,0 +1,33 @@ | |||
package cc.smtweb.framework.core; | |||
import cc.smtweb.framework.core.db.jdbc.IdGenerator; | |||
import cc.smtweb.framework.core.mvc.config.ControllerConfig; | |||
import org.springframework.beans.factory.annotation.Value; | |||
import org.springframework.context.annotation.Bean; | |||
import org.springframework.context.annotation.ComponentScan; | |||
import org.springframework.context.annotation.Configuration; | |||
import org.springframework.scheduling.annotation.EnableScheduling; | |||
/** | |||
* @author kevin | |||
*/ | |||
@Configuration | |||
@ComponentScan | |||
@EnableScheduling | |||
public class CoreAutoConfiguration { | |||
/** | |||
* ID生成器的分步式机器码(1-1023) | |||
*/ | |||
@Value("${smtweb.machine-id}") | |||
private int machineId; | |||
@Bean | |||
public IdGenerator idGenerator() { | |||
return new IdGenerator(machineId); | |||
} | |||
@Bean | |||
public ControllerConfig coreControllerConfig() { | |||
return new ControllerConfig("core", "cc.smtweb.framework.core"); | |||
} | |||
} |
@@ -0,0 +1,117 @@ | |||
package cc.smtweb.framework.core; | |||
import cc.smtweb.framework.core.exception.ExceptionMessage; | |||
import java.util.Date; | |||
/** | |||
* 〈返回结果集〉 | |||
* | |||
* @author kevin | |||
* @since 1.0.0 | |||
*/ | |||
public class R extends SwMap { | |||
protected R() { | |||
this(0); | |||
} | |||
protected R(int code) { | |||
put("now", new Date()); | |||
put("code", code); | |||
} | |||
public static R success() { | |||
return new R(); | |||
} | |||
public static R success(Object data) { | |||
return new R().put("data", data); | |||
} | |||
public static R success(String name, Object data) { | |||
return new R().put(name, data); | |||
} | |||
public static R error() { | |||
return error(ExceptionMessage.INNER_ERROR.getMsg()); | |||
} | |||
public static R error(String msg) { | |||
R r = new R(ExceptionMessage.INNER_ERROR.getCode()); | |||
r.put("msg", msg); | |||
return r; | |||
} | |||
public static R error(int code, String msg) { | |||
R r = new R(code); | |||
r.put("msg", msg); | |||
return r; | |||
} | |||
public static R errorf(String format, Object...args) { | |||
R r = new R(ExceptionMessage.INNER_ERROR.getCode()); | |||
r.put("msg", String.format(format, args)); | |||
return r; | |||
} | |||
// public static R error(ExceptionMessage cm) { | |||
// R r = new R(cm.getCode()); | |||
// r.put("msg", cm.getMsg()); | |||
// return r; | |||
// } | |||
// public static R error(ExceptionMessage cm, Throwable ex) { | |||
// R r = new R(cm.getCode()); | |||
// r.put("msg", cm.getMsg()); | |||
// r.put("exception", ex.getMessage()); | |||
// return r; | |||
// } | |||
public static R error(String msg, Throwable ex) { | |||
R r = new R(ExceptionMessage.INNER_ERROR.getCode()); | |||
r.put("msg", msg); | |||
r.put("exception", ex.getMessage()); | |||
return r; | |||
} | |||
public static R error(ExceptionMessage cm, String msg) { | |||
R r = new R(cm.getCode()); | |||
r.put("msg", msg); | |||
return r; | |||
} | |||
@Override | |||
public R put(String key, Object value) { | |||
super.put(key, value); | |||
return this; | |||
} | |||
public R putNotNull(String key, Object value) { | |||
if (value != null) { | |||
super.put(key, value); | |||
} | |||
return this; | |||
} | |||
public void setMessage(ExceptionMessage cm) { | |||
this.put("code", cm.getCode()); | |||
this.put("msg", cm.getMsg()); | |||
} | |||
public void setData(Object data) { | |||
this.put("data", data); | |||
} | |||
public int readCode() { | |||
return this.readInt("code"); | |||
} | |||
public String readMsg() { | |||
return this.readString("msg"); | |||
} | |||
public boolean readSuccess() { | |||
return this.readInt("code", -1) == 0; | |||
} | |||
} |
@@ -0,0 +1,53 @@ | |||
package cc.smtweb.framework.core; | |||
import cc.smtweb.framework.core.exception.ExceptionMessage; | |||
/** | |||
* 〈全局异常〉 | |||
* | |||
* @author kevin | |||
* @since 1.0.0 | |||
*/ | |||
public class SwException extends RuntimeException { | |||
private static final long serialVersionUID = 8096609992852791423L; | |||
// private ExceptionMessage cm; | |||
public SwException() { | |||
} | |||
public SwException(String msg) { | |||
super(msg); | |||
} | |||
public SwException(int code, String msg) { | |||
super(msg); | |||
} | |||
public SwException(String msg, Throwable e) { | |||
super(msg, e); | |||
} | |||
public SwException(Throwable e) { | |||
super(e.getMessage(), e); | |||
} | |||
public SwException(ExceptionMessage cm) { | |||
super(cm.getMsg()); | |||
} | |||
public SwException(ExceptionMessage cm, Throwable e) { | |||
super(cm.getMsg(), e); | |||
} | |||
public SwException(ExceptionMessage cm, String msg) { | |||
super(msg); | |||
} | |||
// public ExceptionMessage getCm() { | |||
// return cm; | |||
// } | |||
} |
@@ -0,0 +1,9 @@ | |||
package cc.smtweb.framework.core; | |||
import lombok.Data; | |||
@Data | |||
public class SwIpAddr { | |||
private String ip; | |||
private int port; | |||
} |
@@ -0,0 +1,93 @@ | |||
package cc.smtweb.framework.core; | |||
import cc.smtweb.framework.core.util.MapUtil; | |||
import java.util.HashMap; | |||
import java.util.Set; | |||
/** | |||
* 通用map对象,用于无具体类型的传值 | |||
* @author kevin | |||
*/ | |||
public class SwMap extends HashMap<String, Object> { | |||
public SwMap() {} | |||
public SwMap(int initialCapacity) { | |||
super(initialCapacity); | |||
} | |||
public String readString(String name) { | |||
return MapUtil.readString(this, name, null); | |||
} | |||
public String readString(String name, String defaultValue) { | |||
return MapUtil.readString(this, name, defaultValue); | |||
} | |||
public Long readLong(String name) { | |||
return MapUtil.readLong(this, name, null); | |||
} | |||
public Long readLong(String name, Long defaultValue) { | |||
return MapUtil.readLong(this, name, defaultValue); | |||
} | |||
public Long[] readLongArray(String name) { | |||
return MapUtil.readLongArray(this, name, null); | |||
} | |||
public Long[] readLongArray(String name, Long[] defaultValue) { | |||
return MapUtil.readLongArray(this, name, defaultValue); | |||
} | |||
public Set<Long> readLongSet(String name) { | |||
return MapUtil.readLongSet(this, name); | |||
} | |||
public Integer readInt(String name) { | |||
return MapUtil.readInt(this, name, null); | |||
} | |||
public Integer readInt(String name, Integer defaultValue) { | |||
return MapUtil.readInt(this, name, defaultValue); | |||
} | |||
public Float readFloat(String name) { | |||
return MapUtil.readFloat(this, name, null); | |||
} | |||
public Float readFloat(String name, Float defaultValue) { | |||
return MapUtil.readFloat(this, name, defaultValue); | |||
} | |||
public Double readDouble(String name) { | |||
return MapUtil.readDouble(this, name, null); | |||
} | |||
public Double readDouble(String name, Double defaultValue) { | |||
return MapUtil.readDouble(this, name, defaultValue); | |||
} | |||
public Boolean readBool(String name) { | |||
return MapUtil.readBool(this, name, null); | |||
} | |||
public Boolean readBool(String name, Boolean defaultValue) { | |||
return MapUtil.readBool(this, name, defaultValue); | |||
} | |||
@Override | |||
public SwMap put(String name, Object value) { | |||
if (value != null) { | |||
super.put(name, value); | |||
} else { | |||
super.remove(name); | |||
} | |||
return this; | |||
} | |||
public static SwMap of(String name, Object value) { | |||
return new SwMap().put(name, value); | |||
} | |||
} |
@@ -0,0 +1,21 @@ | |||
package cc.smtweb.framework.core.annotation; | |||
import java.lang.annotation.ElementType; | |||
import java.lang.annotation.Retention; | |||
import java.lang.annotation.RetentionPolicy; | |||
import java.lang.annotation.Target; | |||
/** | |||
* 配置在@SwService中的函数,对应API请求,默认公用函数不用配置拦截器的函数, | |||
* 也可以作为拦截实现的基类 | |||
* @author kevin | |||
*/ | |||
@Retention(RetentionPolicy.RUNTIME) | |||
@Target({ElementType.METHOD}) | |||
public @interface SwAction { | |||
/** | |||
* 重写API请求地址,不配置使用: 服务类地址 + “/” + 函数名 | |||
* @return API请求地址 | |||
*/ | |||
String value() default ""; | |||
} |
@@ -0,0 +1,16 @@ | |||
package cc.smtweb.framework.core.annotation; | |||
import java.lang.annotation.*; | |||
/** | |||
* 参数注解,request定制的上下文内容,定制使用 | |||
* @author kevin | |||
* | |||
*/ | |||
@Target( { ElementType.PARAMETER}) | |||
@Retention(RetentionPolicy.RUNTIME) | |||
@Documented | |||
public @interface SwAttr { | |||
String value() default ""; | |||
} |
@@ -0,0 +1,22 @@ | |||
package cc.smtweb.framework.core.annotation; | |||
import java.lang.annotation.ElementType; | |||
import java.lang.annotation.Retention; | |||
import java.lang.annotation.RetentionPolicy; | |||
import java.lang.annotation.Target; | |||
/** | |||
* 被该注释修饰的类提供服务,如Dao,Service等 | |||
* @author kevin | |||
*/ | |||
@Retention(RetentionPolicy.RUNTIME) | |||
@Target({ElementType.TYPE, ElementType.METHOD}) | |||
public @interface SwBean { | |||
String value() default ""; | |||
/** | |||
* 是否强行生成对象,不检查单例 | |||
* @return 是否强行生成对象 | |||
*/ | |||
boolean alwaysCreate() default false; | |||
} |
@@ -0,0 +1,15 @@ | |||
package cc.smtweb.framework.core.annotation; | |||
import java.lang.annotation.*; | |||
/** | |||
* 参数注解,当编译器无法找到参数名称时,需要用该注解标明参数名称,当然你也可以用该参数标注参数别名与表单域字段名同意 | |||
* @author kevin | |||
* | |||
*/ | |||
@Target( { ElementType.PARAMETER}) | |||
@Retention(RetentionPolicy.RUNTIME) | |||
@Documented | |||
public @interface SwBody { | |||
String value() default ""; | |||
} |
@@ -0,0 +1,26 @@ | |||
package cc.smtweb.framework.core.annotation; | |||
import java.lang.annotation.ElementType; | |||
import java.lang.annotation.Retention; | |||
import java.lang.annotation.RetentionPolicy; | |||
import java.lang.annotation.Target; | |||
/** | |||
* 被该注释修饰的类提供缓存服务 | |||
* | |||
* @author kevin | |||
*/ | |||
@Retention(RetentionPolicy.RUNTIME) | |||
@Target({ElementType.TYPE}) | |||
public @interface SwCache { | |||
//唯一标识 | |||
String ident(); | |||
//标题,展示用 | |||
String title(); | |||
//依赖的缓存ident,多个用英文逗号分隔 | |||
String depends() default ""; | |||
//是否懒加载 | |||
boolean lazy() default false; | |||
//失效时间,单位分钟 | |||
long timeout() default 0; | |||
} |
@@ -0,0 +1,38 @@ | |||
package cc.smtweb.framework.core.annotation; | |||
import java.lang.annotation.ElementType; | |||
import java.lang.annotation.Retention; | |||
import java.lang.annotation.RetentionPolicy; | |||
import java.lang.annotation.Target; | |||
/** | |||
* 被该注释修饰的方法对应了字段名和类型 | |||
* @author kevin | |||
*/ | |||
@Retention(RetentionPolicy.RUNTIME) | |||
@Target({ElementType.FIELD}) | |||
public @interface SwColumn { | |||
String value() default ""; | |||
/** 字段作用,前端用RefType表示 */ | |||
Type[] type() default {}; | |||
public static enum Type { | |||
// 主键 | |||
ID, | |||
// 上级ID,树结构需要 | |||
PARENT_ID, | |||
// 排序字段,树结构需要 | |||
ORDER, | |||
// 编码字段 | |||
CODE, | |||
// 名词字段 | |||
NAME, | |||
// 主表ID,MapToOne | |||
MASTER_ID, | |||
// 创建时间 | |||
CREATE_TIME, | |||
// 更新时间 | |||
LAST_TIME, | |||
} | |||
} |
@@ -0,0 +1,21 @@ | |||
package cc.smtweb.framework.core.annotation; | |||
import java.lang.annotation.ElementType; | |||
import java.lang.annotation.Retention; | |||
import java.lang.annotation.RetentionPolicy; | |||
import java.lang.annotation.Target; | |||
/** | |||
* 被该注释修饰的方法对应了外键表名和字段名,名称可选 | |||
* @author kevin | |||
*/ | |||
@Retention(RetentionPolicy.RUNTIME) | |||
@Target({ElementType.FIELD}) | |||
public @interface SwColumnForeign { | |||
// 外键表名 | |||
String table() default ""; | |||
// ID字段名 | |||
String id() default ""; | |||
// 唯一名称字段名 | |||
String code() default ""; | |||
} |
@@ -0,0 +1,20 @@ | |||
package cc.smtweb.framework.core.annotation; | |||
import java.lang.annotation.ElementType; | |||
import java.lang.annotation.Retention; | |||
import java.lang.annotation.RetentionPolicy; | |||
import java.lang.annotation.Target; | |||
/** | |||
* 通过对@SwService/@SwBean类中的函数注解,服务启动时会调用一次被注解的函数 | |||
* @author kevin | |||
*/ | |||
@Retention(RetentionPolicy.RUNTIME) | |||
@Target({ElementType.METHOD}) | |||
public @interface SwConstruct { | |||
/** | |||
* 销毁顺序,值越小越先创建 | |||
* @return 销毁顺序值 | |||
*/ | |||
int order() default 0; | |||
} |
@@ -0,0 +1,20 @@ | |||
package cc.smtweb.framework.core.annotation; | |||
import java.lang.annotation.ElementType; | |||
import java.lang.annotation.Retention; | |||
import java.lang.annotation.RetentionPolicy; | |||
import java.lang.annotation.Target; | |||
/** | |||
* 通过对@SwService/@SwBean类中的函数注解,服务停止时会调用一次被注解的函数 | |||
* @author kevin | |||
*/ | |||
@Retention(RetentionPolicy.RUNTIME) | |||
@Target({ElementType.METHOD}) | |||
public @interface SwDestroy { | |||
/** | |||
* 销毁顺序,值越大越先销毁 | |||
* @return 销毁顺序值 | |||
*/ | |||
int order() default 0; | |||
} |
@@ -0,0 +1,16 @@ | |||
package cc.smtweb.framework.core.annotation; | |||
import java.lang.annotation.*; | |||
/** | |||
* 路径注解 | |||
* @author kevin | |||
* | |||
*/ | |||
@Target( { ElementType.PARAMETER, ElementType.FIELD}) | |||
@Retention(RetentionPolicy.RUNTIME) | |||
@Documented | |||
public @interface SwHeaderParam { | |||
String value() default ""; | |||
} |
@@ -0,0 +1,18 @@ | |||
package cc.smtweb.framework.core.annotation; | |||
import java.lang.annotation.*; | |||
/** | |||
* 参数注解,当编译器无法找到参数名称时,需要用该注解标明参数名称,当然你也可以用该参数标注参数别名与表单域字段名同意 | |||
* @author kevin | |||
* | |||
*/ | |||
@Target( { ElementType.PARAMETER, ElementType.FIELD}) | |||
@Retention(RetentionPolicy.RUNTIME) | |||
@Documented | |||
public @interface SwParam { | |||
String value() default ""; | |||
/** 注入类型,有多个类实现/子类时使用 */ | |||
Class<?> type() default Object.class; | |||
} |
@@ -0,0 +1,16 @@ | |||
package cc.smtweb.framework.core.annotation; | |||
import java.lang.annotation.*; | |||
/** | |||
* 路径注解 | |||
* @author kevin | |||
* | |||
*/ | |||
@Target( { ElementType.PARAMETER, ElementType.FIELD}) | |||
@Retention(RetentionPolicy.RUNTIME) | |||
@Documented | |||
public @interface SwPathParam { | |||
// String value() default ""; | |||
} |
@@ -0,0 +1,21 @@ | |||
package cc.smtweb.framework.core.annotation; | |||
import java.lang.annotation.ElementType; | |||
import java.lang.annotation.Retention; | |||
import java.lang.annotation.RetentionPolicy; | |||
import java.lang.annotation.Target; | |||
/** | |||
* 被该注释修饰的方法都会经过切面拦截校验权限,默认是需要已登录权限 | |||
* @author kevin | |||
*/ | |||
@Retention(RetentionPolicy.RUNTIME) | |||
@Target({ElementType.METHOD, ElementType.TYPE}) | |||
public @interface SwPerm { | |||
/** 无权限控制的值,在函数上注解@SwPerm(SwPerm.NONE) */ | |||
static final String NONE = "*"; | |||
static final String SESSION = ""; | |||
/** 权限定义值 */ | |||
String value() default SESSION; | |||
} |
@@ -0,0 +1,30 @@ | |||
package cc.smtweb.framework.core.annotation; | |||
import java.lang.annotation.ElementType; | |||
import java.lang.annotation.Retention; | |||
import java.lang.annotation.RetentionPolicy; | |||
import java.lang.annotation.Target; | |||
/** | |||
* 通过对@SwService/@SwBean类中的函数注解,定时任务方法注解 | |||
* | |||
* @author xkliu | |||
*/ | |||
@Retention(RetentionPolicy.RUNTIME) | |||
@Target(value={ElementType.METHOD}) | |||
public @interface SwScheduling { | |||
/** | |||
* 时间触发条件,如果值是单数字就是用秒定时,否则就是采用linux/unux crondtab 样式 | |||
*/ | |||
String value(); | |||
/** | |||
* 定时任务分组条件, 有值时同一分组不允许并发执行 | |||
*/ | |||
String group() default ""; | |||
/** | |||
* 是否支持多服务同时运行 | |||
*/ | |||
boolean multiServer() default false; | |||
} |
@@ -0,0 +1,16 @@ | |||
package cc.smtweb.framework.core.annotation; | |||
import java.lang.annotation.ElementType; | |||
import java.lang.annotation.Retention; | |||
import java.lang.annotation.RetentionPolicy; | |||
import java.lang.annotation.Target; | |||
/** | |||
* 被该注释修饰的类提供控制器服务 | |||
* @author kevin | |||
*/ | |||
@Retention(RetentionPolicy.RUNTIME) | |||
@Target({ElementType.TYPE}) | |||
public @interface SwService { | |||
String value() default ""; | |||
} |
@@ -0,0 +1,17 @@ | |||
package cc.smtweb.framework.core.annotation; | |||
import java.lang.annotation.ElementType; | |||
import java.lang.annotation.Retention; | |||
import java.lang.annotation.RetentionPolicy; | |||
import java.lang.annotation.Target; | |||
/** | |||
* 被该注释修饰的类对应了数据库表名(库+表的形式,如 sw_user.sys_user) | |||
* @author kevin | |||
*/ | |||
@Retention(RetentionPolicy.RUNTIME) | |||
@Target({ElementType.TYPE}) | |||
public @interface SwTable { | |||
/** 库名+表名 */ | |||
String value() default ""; | |||
} |
@@ -0,0 +1,493 @@ | |||
package cc.smtweb.framework.core.cache; | |||
import cc.smtweb.framework.core.annotation.SwCache; | |||
import cc.smtweb.framework.core.annotation.SwParam; | |||
import cc.smtweb.framework.core.cache.ISwCache; | |||
import cc.smtweb.framework.core.redis.RedisBroadcastEvent; | |||
import cc.smtweb.framework.core.redis.RedisManager; | |||
import cc.smtweb.framework.core.util.CommUtil; | |||
import cc.smtweb.framework.core.util.kryo.KryoTool; | |||
import com.github.benmanes.caffeine.cache.Caffeine; | |||
import com.github.benmanes.caffeine.cache.LoadingCache; | |||
import com.github.benmanes.caffeine.cache.RemovalCause; | |||
import com.github.benmanes.caffeine.cache.Scheduler; | |||
import lombok.extern.slf4j.Slf4j; | |||
import org.apache.commons.lang3.StringUtils; | |||
import org.checkerframework.checker.nullness.qual.NonNull; | |||
import org.springframework.beans.factory.annotation.Autowired; | |||
import java.io.Serializable; | |||
import java.util.*; | |||
import java.util.concurrent.ConcurrentHashMap; | |||
import java.util.concurrent.ScheduledExecutorService; | |||
import java.util.concurrent.TimeUnit; | |||
/** | |||
* 封装caffeine的抽象Cache类,实现内存缓存,并支持用redis广播进行数据缓存同步 | |||
* | |||
* @param <> 缓存的值类 | |||
* @author zhenggm | |||
*/ | |||
@Slf4j | |||
public abstract class AbstractCache<T extends Serializable> implements ISwCache<String, T> { | |||
protected final static int LS_NONE = 0; | |||
protected final static int LS_LOADING = 1; | |||
protected final static int LS_LOADED = 2; | |||
private final static String split_char = "-"; | |||
//唯一标识 | |||
protected String ident; | |||
//展示名称 | |||
protected String title; | |||
//是否懒加载,懒加载时,将不从库loadall | |||
protected boolean lazy; | |||
//依赖 | |||
protected String[] depends; | |||
//数据加载状态 | |||
private short loadStatu = LS_NONE; | |||
private Class<T> pTypeClass = null; | |||
private LoadingCache<String, T> cache; | |||
private Map<String, T> cacheOrg = new ConcurrentHashMap<>(); | |||
//本地缓存对象,按List缓存,减轻redis压力 | |||
protected Map<String, Set<T>> mapListLocal = new ConcurrentHashMap<>(); | |||
//本地按其他键值缓存的对象 | |||
protected Map<String, Map<String, T>> mapMapLocal = new ConcurrentHashMap<>(); | |||
/*注册要按list缓存的信息,key=list的类别,按什么字段来缓存。IGetBeanKey为根据bean获取此bean的key | |||
如资金科目明细项,需按资金科目缓存List。key=sc,value=class_id | |||
*/ | |||
protected Map<String, IGetBeanKey<T>> mapListReg = new HashMap<>(); | |||
protected Map<String, IGetBeanKey<T>> mapMapReg = new HashMap<>(); | |||
public AbstractCache() { | |||
pTypeClass = CommUtil.getParameterizedType(getClass()); | |||
} | |||
//注册 | |||
protected void install(ScheduledExecutorService executorService) { | |||
SwCache swCache = this.getClass().getAnnotation(SwCache.class); | |||
if (swCache != null) { | |||
ident = swCache.ident(); | |||
title = swCache.title(); | |||
lazy = swCache.lazy(); | |||
depends = StringUtils.split(swCache.depends(), ","); | |||
} | |||
final @NonNull Caffeine<Object, Object> kvCaffeine = Caffeine.newBuilder() | |||
.scheduler(Scheduler.forScheduledExecutorService(executorService)); | |||
if (swCache.timeout() > 0) { | |||
kvCaffeine.expireAfterAccess(swCache.timeout(), TimeUnit.MINUTES); | |||
} | |||
this.cache = kvCaffeine.build(this::onLoad); | |||
} | |||
public boolean isNotInited() { | |||
return loadStatu == LS_NONE; | |||
} | |||
/** | |||
* 注册标识,用于同步数据事件通知 | |||
*/ | |||
public String getIdent() { | |||
return ident; | |||
} | |||
public String getTitle() { | |||
return title; | |||
} | |||
public boolean isLazy() { | |||
return lazy; | |||
} | |||
public String[] getDepends() { | |||
return depends; | |||
} | |||
/** | |||
* 初始化 | |||
* redis有,则从redis下载; | |||
* 否则,非lazy,从数据库加载,并更新redis | |||
*/ | |||
protected void init() { | |||
if (RedisManager.getInstance().exists(getIdent())) { | |||
//从缓存服务器下载到本地 | |||
syncCache(); | |||
} else if (!lazy) { | |||
refresh(); | |||
} | |||
} | |||
/** | |||
* 注册其他key的List缓存,如tree的children | |||
* | |||
* @param key | |||
* @param iGetBeanKey | |||
*/ | |||
protected void regList(String key, IGetBeanKey<T> iGetBeanKey) { | |||
mapListReg.put(key, iGetBeanKey); | |||
} | |||
/** | |||
* 注册其他key的Map缓存,如按code缓存 | |||
* | |||
* @param key | |||
* @param iGetBeanKey | |||
*/ | |||
protected void regMap(String key, IGetBeanKey<T> iGetBeanKey) { | |||
mapMapReg.put(key, iGetBeanKey); | |||
} | |||
//获取bean的id | |||
protected abstract String getId(T bean); | |||
/** | |||
* 从redis获取全部缓存 | |||
* redis上只保留<id,Bean>,拉下来后,自行组织其他格式缓存 | |||
*/ | |||
public void syncCache() { | |||
loadStatu = LS_LOADING; | |||
cache.invalidateAll(); | |||
cacheOrg.clear(); | |||
mapListLocal.clear(); | |||
mapMapLocal.clear(); | |||
List<byte[]> vals = RedisManager.getInstance().hVals(getIdent()); | |||
for (byte[] v : vals) { | |||
T bean = CommUtil.readObject(v, pTypeClass); | |||
doUpdate(bean); | |||
} | |||
loadStatu = LS_LOADED; | |||
} | |||
/** | |||
* 监听redis同步事件 | |||
* | |||
* @param event | |||
*/ | |||
public void syncCache(RedisBroadcastEvent event) { | |||
//加载中,忽略 | |||
if (loadStatu == LS_LOADING) { | |||
log.info("加载中,忽略本次同步通知:" + event.toString()); | |||
return; | |||
} | |||
switch (event.getAction()) { | |||
case RedisBroadcastEvent.CODE_REMOVE: | |||
doRemove(event.getKey()); | |||
break; | |||
case RedisBroadcastEvent.CODE_CLEAR: | |||
doClear(); | |||
break; | |||
case RedisBroadcastEvent.CODE_CACHE_UPDATE: | |||
T bean = RedisManager.getInstance().hGet(getIdent(), event.getKey(), pTypeClass); | |||
if (bean != null) { | |||
doRemove(getId(bean)); | |||
doUpdate(bean); | |||
} | |||
break; | |||
case RedisBroadcastEvent.CODE_CACHE_REFRESH: | |||
syncCache(); | |||
break; | |||
default: | |||
throw new IllegalStateException("Unexpected value: " + event.getAction()); | |||
} | |||
} | |||
/** | |||
* 通过指定的key获取缓存对象值 | |||
* | |||
* @param key 缓存key值 | |||
* @return 缓存对象值 | |||
*/ | |||
protected T load(String key) { | |||
return null; | |||
} | |||
/** | |||
* 从数据库加载全部,lazy模式可以不用实现 | |||
*/ | |||
protected List<T> loadAll() { | |||
return null; | |||
} | |||
/** | |||
* @param key | |||
* @return | |||
*/ | |||
private T onLoad(String key) { | |||
if (lazy) {//非懒加载,不管 | |||
T result = load(key); | |||
if (result != null) { | |||
this.put(result); | |||
} | |||
return result; | |||
} | |||
return null; | |||
} | |||
/** | |||
* 删除时,需要拿原来的对象;对象被取出后,可能被修改 | |||
* | |||
* @param key | |||
* @return | |||
*/ | |||
protected T getForRemove(String key) { | |||
return cacheOrg.get(key); | |||
} | |||
/** | |||
* 本地删除缓存 | |||
* | |||
* @param key | |||
*/ | |||
protected void doRemove(String key) { | |||
T bean = getForRemove(key); | |||
if (bean != null) { | |||
for (Map.Entry<String, IGetBeanKey<T>> entry : mapListReg.entrySet()) { | |||
doRemoveList(entry.getKey(), getBeanKey(entry.getValue(), bean), bean); | |||
} | |||
for (Map.Entry<String, IGetBeanKey<T>> entry : mapMapReg.entrySet()) { | |||
doRemoveMap(entry.getKey(), getBeanKey(entry.getValue(), bean)); | |||
} | |||
} | |||
cache.invalidate(key); | |||
cacheOrg.remove(key); | |||
} | |||
/** | |||
* 插入新对象 | |||
*/ | |||
protected void doUpdate(T value) { | |||
final String key = getId(value); | |||
cache.put(key, value); | |||
cacheOrg.put(key, CommUtil.cloneObj(value, pTypeClass)); | |||
for (Map.Entry<String, IGetBeanKey<T>> entry : mapListReg.entrySet()) { | |||
doUpdateList(entry.getKey(), getBeanKey(entry.getValue(), value), value); | |||
} | |||
for (Map.Entry<String, IGetBeanKey<T>> entry : mapMapReg.entrySet()) { | |||
doUpdateMap(entry.getKey(), getBeanKey(entry.getValue(), value), value); | |||
} | |||
} | |||
//本地调用,删除一个对象时,更新列表缓存 | |||
private void doRemoveList(String regionKey, String key, T value) { | |||
if (StringUtils.isEmpty(key)) return; | |||
Set<T> list = mapListLocal.get(regionKey + split_char + key); | |||
if (list == null) { | |||
return; | |||
} | |||
list.remove(value); | |||
} | |||
//本地调用,删除对象时,更新map缓存 | |||
private void doRemoveMap(String regionKey, String key) { | |||
if (StringUtils.isEmpty(key)) return; | |||
Map<String, T> map = mapMapLocal.get(regionKey); | |||
if (map != null) { | |||
map.remove(key); | |||
} | |||
} | |||
//本地调用,更新列表缓存 | |||
private void doUpdateList(String regionKey, String key, T value) { | |||
if (StringUtils.isEmpty(key)) return; | |||
Set<T> list = mapListLocal.computeIfAbsent(regionKey + split_char + key, k -> new LinkedHashSet<>()); | |||
list.add(value); | |||
} | |||
//本地调用,更新对象时,更新map缓存 | |||
private void doUpdateMap(String regionKey, String key, T value) { | |||
if (StringUtils.isEmpty(key)) return; | |||
Map<String, T> map = mapMapLocal.computeIfAbsent(regionKey, k -> new HashMap<>()); | |||
map.put(key, value); | |||
} | |||
/** | |||
* 外部调用,更新缓存:先删除,再更新本地缓存,更新并通知redis | |||
* | |||
* @param bean | |||
*/ | |||
public final void put(T bean) { | |||
doRemove(getId(bean)); | |||
doUpdate(bean); | |||
if (loadStatu != LS_LOADING) { | |||
RedisManager.getInstance().hSet(getIdent(), getId(bean), bean); | |||
//通知redis | |||
publishUpdate(getId(bean)); | |||
} | |||
} | |||
/** | |||
* 将对象重置为修改前的值,用于取出修改后,事务提交失败 | |||
* | |||
* @param bean | |||
*/ | |||
public final void reset(T bean) { | |||
final String id = getId(bean); | |||
T b = getForRemove(id); | |||
if (b != null) { | |||
cache.put(id, b); | |||
} | |||
} | |||
@Override | |||
public final T get(String key) { | |||
return cache.get(key); | |||
} | |||
public final T get(long key) { | |||
return cache.get(String.valueOf(key)); | |||
} | |||
/** | |||
* 按其他key取bean | |||
* | |||
* @param rk 注册类型 | |||
* @param key | |||
* @return | |||
*/ | |||
protected final T localGetByKey(String rk, String key) { | |||
Map<String, T> map = mapMapLocal.get(rk); | |||
if (map != null) { | |||
return map.get(key); | |||
} | |||
return null; | |||
} | |||
public final Set<T> localGetListByKey(String rk, String key) { | |||
return mapListLocal.get(rk + split_char + key); | |||
} | |||
/** | |||
* 通知redis更新:更新单个bean | |||
* | |||
* @param key | |||
*/ | |||
protected final void publishUpdate(String key) { | |||
RedisBroadcastEvent message = new RedisBroadcastEvent(); | |||
message.setIdent(getIdent()); | |||
message.setKey(key); | |||
message.setAction(RedisBroadcastEvent.CODE_CACHE_UPDATE); | |||
RedisManager.getInstance().publish(message); | |||
} | |||
/** | |||
* 通知redis更新:删除单个bean | |||
* | |||
* @param key | |||
*/ | |||
@Override | |||
public final void publishRemove(String key) { | |||
RedisBroadcastEvent message = new RedisBroadcastEvent(); | |||
message.setIdent(getIdent()); | |||
message.setKey(key); | |||
message.setAction(RedisBroadcastEvent.CODE_REMOVE); | |||
RedisManager.getInstance().publish(message); | |||
} | |||
/** | |||
* 通知redis更新:清空缓存 | |||
*/ | |||
@Override | |||
public final void publishClear() { | |||
RedisBroadcastEvent message = new RedisBroadcastEvent(); | |||
message.setIdent(getIdent()); | |||
message.setAction(RedisBroadcastEvent.CODE_CLEAR); | |||
RedisManager.getInstance().publish(message); | |||
} | |||
/** | |||
* 通知redis更新:刷新缓存 * | |||
*/ | |||
public final void publishRefresh() { | |||
RedisBroadcastEvent message = new RedisBroadcastEvent(); | |||
message.setIdent(getIdent()); | |||
message.setAction(RedisBroadcastEvent.CODE_CACHE_REFRESH); | |||
RedisManager.getInstance().publish(message); | |||
} | |||
/** | |||
* 外部调用,删除单个对象 | |||
* | |||
* @param key | |||
*/ | |||
public void remove(String key) { | |||
this.doRemove(key); | |||
RedisManager.getInstance().hdel(getIdent(), key); | |||
publishRemove(key); | |||
} | |||
public void remove(long key) { | |||
remove(String.valueOf(key)); | |||
} | |||
/** | |||
* 本地调用,清空缓存 | |||
*/ | |||
protected void doClear() { | |||
cache.cleanUp(); | |||
cacheOrg.clear(); | |||
mapListLocal.clear(); | |||
mapMapLocal.clear(); | |||
} | |||
/** | |||
* 外部调用,清空缓存:清空本地缓存,并更新redis,通知其他服务 | |||
*/ | |||
public void clear() { | |||
doClear(); | |||
RedisManager.getInstance().del(getIdent()); | |||
publishClear(); | |||
} | |||
/** | |||
* 重载缓存:先清空,再重载 | |||
*/ | |||
public void refresh() { | |||
loadStatu = LS_LOADING; | |||
doClear(); | |||
List<T> list = loadAll(); | |||
if (list != null) { | |||
for (T bean : list) { | |||
doUpdate(bean); | |||
} | |||
} | |||
loadStatu = LS_LOADED; | |||
RedisManager.getInstance().hmSet(getIdent(), cacheOrg); | |||
publishRefresh(); | |||
} | |||
/** | |||
* 根据接口,得到bean的缓存key | |||
* | |||
* @param iGetBeanKey | |||
* @param bean | |||
* @return | |||
*/ | |||
protected String getBeanKey(IGetBeanKey<T> iGetBeanKey, T bean) { | |||
return iGetBeanKey.getKey(bean); | |||
} | |||
public Collection<T> getAll() { | |||
return cacheOrg.values(); | |||
} | |||
/** | |||
* 获取bean缓存key的接口类,用于按非id的缓存,如code等 | |||
* | |||
* @param <T> | |||
*/ | |||
public interface IGetBeanKey<T> { | |||
String getKey(T bean); | |||
} | |||
} |
@@ -0,0 +1,109 @@ | |||
package cc.smtweb.framework.core.cache; | |||
import cc.smtweb.framework.core.annotation.SwTable; | |||
import cc.smtweb.framework.core.db.cache.EntityCache; | |||
import cc.smtweb.framework.core.db.cache.ModelTableCache; | |||
import cc.smtweb.framework.core.db.vo.ModelTable; | |||
import cc.smtweb.framework.core.redis.RedisBroadcastEvent; | |||
import cc.smtweb.framework.core.util.SpringUtil; | |||
import lombok.extern.slf4j.Slf4j; | |||
import org.springframework.context.event.EventListener; | |||
import java.util.ArrayList; | |||
import java.util.HashMap; | |||
import java.util.List; | |||
import java.util.Map; | |||
import java.util.concurrent.Executors; | |||
import java.util.concurrent.ScheduledExecutorService; | |||
/** | |||
* 内存缓存管理器,管理lazy加载的缓存,长时间不使用会失效 | |||
*/ | |||
@Slf4j | |||
public class CacheManager { | |||
//所有缓存对象 | |||
private final Map<String, AbstractCache> cacheMap = new HashMap<>(); | |||
private final Map<Class, AbstractCache> cacheMapCls = new HashMap<>(); | |||
//记录顺序用 | |||
private List<AbstractCache> listCache = new ArrayList<>(); | |||
//缓存清理线程池 | |||
private final ScheduledExecutorService executorService = Executors.newScheduledThreadPool(2); | |||
public AbstractCache getCache(String ident) { | |||
final AbstractCache cache = cacheMap.get(ident); | |||
if (cache != null && cache.isNotInited()) { | |||
cache.init(); | |||
} | |||
return cache; | |||
} | |||
public <T extends AbstractCache> T getCache(Class<T> clazz) { | |||
final T cache = (T) cacheMapCls.get(clazz); | |||
if (cache != null && cache.isNotInited()) { | |||
cache.init(); | |||
} | |||
return cache; | |||
} | |||
public static CacheManager getIntance() { | |||
return SpringUtil.getBean(CacheManager.class); | |||
} | |||
// 初始化cache | |||
public boolean install(AbstractCache cache) { | |||
cache.install(executorService); | |||
if (cacheMap.putIfAbsent(cache.getIdent(), cache) == null) { | |||
cacheMapCls.put(cache.getClass(), cache); | |||
listCache.add(cache); | |||
return true; | |||
} | |||
return false; | |||
} | |||
/** | |||
* 启动时的初始化及预加载 | |||
* 1、按依赖关系排序 | |||
* 2、预加载(lazy模式不从库加载) | |||
*/ | |||
public void init() { | |||
listCache.sort((o1, o2) -> { | |||
//o1依赖o2 | |||
if (isContains(o1.getDepends(), o2.getIdent())) return 1; | |||
//o2依赖o1 | |||
if (isContains(o2.getDepends(), o1.getIdent())) return -1; | |||
return 0;//getPluginIndex(o1) - getPluginIndex(o2); | |||
}); | |||
for (AbstractCache cache : listCache) { | |||
cache.init(); | |||
} | |||
//按表加载并初始化 | |||
for (ModelTable table : ModelTableCache.getInstance().getAll()) { | |||
if (table.isNeedCache() && !cacheMap.containsKey(table.getName())) { | |||
EntityCache cache = new EntityCache(table.getName()); | |||
cache.install(executorService); | |||
cacheMapCls.put(cache.getClass(), cache); | |||
cacheMap.put(table.getName(), cache); | |||
} | |||
} | |||
} | |||
private boolean isContains(String[] src, String dest) { | |||
if (src == null || src.length == 0) return false; | |||
for (String s : src) { | |||
if (s.trim().equals(dest)) return true; | |||
} | |||
return false; | |||
} | |||
@EventListener | |||
public void onRedisBroadcastEvent(RedisBroadcastEvent event) { | |||
AbstractCache cache = getCache(event.getIdent()); | |||
if (cache == null) { | |||
log.info("接收到不存在的缓存更新!(" + event.toString() + ")"); | |||
return; | |||
} | |||
cache.syncCache(event); | |||
} | |||
} |
@@ -0,0 +1,24 @@ | |||
package cc.smtweb.framework.core.cache; | |||
public interface ISwCache<K, V> { | |||
/** 权限缓存名称 */ | |||
String REALM_CACHE = "RealmCache"; | |||
/** | |||
* 根据键值获取缓存对象 | |||
* @param key 缓存唯一键值 | |||
* @return 缓存对象 | |||
*/ | |||
V get(K key); | |||
/** | |||
* 给redis发送广播消息,多服务同步删除相同key值的缓存对象 | |||
* @param key 缓存键值 | |||
*/ | |||
void publishRemove(K key); | |||
/** | |||
* 给redis发送广播消息,多服务同步清空相同类型的缓存对象 | |||
*/ | |||
void publishClear(); | |||
} |
@@ -0,0 +1,271 @@ | |||
package cc.smtweb.framework.core.db; | |||
import cc.smtweb.framework.core.db.impl.DefaultEntity; | |||
import cc.smtweb.framework.core.db.jdbc.IdGenerator; | |||
import cc.smtweb.framework.core.db.jdbc.JdbcEngine; | |||
import cc.smtweb.framework.core.util.SpringUtil; | |||
import lombok.extern.slf4j.Slf4j; | |||
import org.springframework.jdbc.core.JdbcTemplate; | |||
import org.springframework.jdbc.core.ResultSetExtractor; | |||
import java.sql.ResultSet; | |||
import java.sql.SQLException; | |||
import java.util.List; | |||
import java.util.Map; | |||
import java.util.concurrent.ConcurrentHashMap; | |||
/** | |||
* 提供数据库基本操作和代理EntityDao操作,在DbEngineConfiguration中初始化 | |||
* | |||
* @author xkliu | |||
*/ | |||
@Slf4j | |||
public class DbEngine extends JdbcEngine { | |||
private String dbSchema; | |||
private final Map<Class<?>, EntityDao<?>> daoMap = new ConcurrentHashMap<>(); | |||
private final Map<String, EntityDao<DefaultEntity>> tableDaoMap = new ConcurrentHashMap<>(); | |||
public static DbEngine getInstance() { | |||
return SpringUtil.getBean(DbEngine.class); | |||
} | |||
public DbEngine(JdbcTemplate jdbcTemplate, IdGenerator idGenerator, String type) { | |||
super(jdbcTemplate, idGenerator, type); | |||
} | |||
/** | |||
* bean为Data格式,调用此方法 | |||
* @param type bean类 | |||
* @param <T> | |||
* @return | |||
*/ | |||
public <T> EntityDao<T> findDao(Class<T> type) { | |||
EntityDao<T> handler = (EntityDao<T>) daoMap.get(type); | |||
if (handler == null) { | |||
synchronized (daoMap) { | |||
handler = new EntityDao<>(type, this); | |||
daoMap.put(type, handler); | |||
} | |||
} | |||
return handler; | |||
} | |||
public EntityDao<DefaultEntity> findDao(String tableName) { | |||
EntityDao<DefaultEntity> handler = tableDaoMap.get(type); | |||
if (handler == null) { | |||
synchronized (tableDaoMap) { | |||
handler = new EntityDao<>(tableName, this); | |||
tableDaoMap.put(tableName, handler); | |||
} | |||
} | |||
return handler; | |||
} | |||
public String getDbSchema() { | |||
if (dbSchema == null) { | |||
synchronized (DbEngine.class) { | |||
if (dbSchema == null) { | |||
try { | |||
final ResultSetExtractor<String> rse = new ResultSetExtractor<String>() { | |||
@Override | |||
public String extractData(ResultSet rs) throws SQLException { | |||
if (rs.next()) return rs.getString(1); | |||
throw new SQLException("未指定当前数据库,请检查您的链接!"); | |||
} | |||
}; | |||
dbSchema = query("SELECT database()", rse); | |||
} catch (Exception e) { | |||
log.error("获取mysql的数据库失败", e); | |||
return null; | |||
} | |||
} | |||
} | |||
} | |||
return dbSchema; | |||
} | |||
/** | |||
* 根据PO对象的ID值更新其余所有字段 | |||
* | |||
* @param entity PO对象 | |||
* @param <T> PO对象类型 | |||
* @return 更新数量 | |||
*/ | |||
public <T> int updateEntity(T entity) { | |||
return updateEntity(entity, null, null); | |||
} | |||
/** | |||
* 使用ID字段更新单行数据 | |||
* | |||
* @param entity PO值对象,对象属性是需要更新的值 | |||
* @param fields 需要更新额字段列表,逗号分隔 | |||
* @param <T> PO值对象类型 | |||
* @return 更新数量 | |||
*/ | |||
public <T> int updateEntity(T entity, String fields) { | |||
return updateEntity(entity, fields, null); | |||
} | |||
/** | |||
* 指定自定义条件更新对象 | |||
* | |||
* @param entity PO值对象,对象属性是需要更新的值和更新条件值 | |||
* @param fields 需要更新额字段列表,逗号分隔 | |||
* @param whereFields 更新条件字段列表,逗号分隔 | |||
* @param <T> PO值对象类型 | |||
* @return 更新数量 | |||
*/ | |||
public <T> int updateEntity(T entity, String fields, String whereFields) { | |||
EntityDao<T> dao = findDao((Class<T>) entity.getClass()); | |||
return dao.updateEntity(entity, fields, whereFields); | |||
} | |||
/** | |||
* 用PO对象所有字段入单行数据 | |||
* | |||
* @param entity PO对象 | |||
* @param <T> PO对象类型 | |||
* @return 更新数量 | |||
*/ | |||
public <T> int insertEntity(T entity) { | |||
return insertEntity(entity, null); | |||
} | |||
/** | |||
* 插入单行数据 | |||
* | |||
* @param entity PO对象 | |||
* @param fields 逗号分隔的字段列表 | |||
* @param <T> PO对象类型 | |||
* @return 更新数量 | |||
*/ | |||
public <T> int insertEntity(T entity, String fields) { | |||
EntityDao<T> dao = findDao((Class<T>) entity.getClass()); | |||
return dao.insertEntity(entity, fields); | |||
} | |||
/** | |||
* 根据ID值删除单行数据 | |||
* | |||
* @param entity PO对象 | |||
* @param <T> PO对象类型 | |||
* @return 删除数量 | |||
*/ | |||
public <T> int deleteEntity(T entity) { | |||
EntityDao<T> dao = findDao((Class<T>) entity.getClass()); | |||
return dao.deleteEntity(entity); | |||
} | |||
/** | |||
* 根据ID值删除单行数据 | |||
* | |||
* @param entityType PO对象类型 | |||
* @param id 记录主建值 | |||
* @param <T> PO对象 | |||
* @return 删除数量 | |||
*/ | |||
public <T> int deleteEntity(Class<T> entityType, Long id) { | |||
EntityDao<T> dao = findDao(entityType); | |||
return dao.deleteEntity(id); | |||
} | |||
/** | |||
* 根据ID值删除单行数据 | |||
* | |||
* @param entityType PO对象类型 | |||
* @param whereSql Where条件SQL语句,以where开头 | |||
* @param params 条件的值,可以多个 | |||
* @param <T> PO对象 | |||
* @return 删除数量 | |||
*/ | |||
public <T> int deleteEntity(Class<T> entityType, String whereSql, Object... params) { | |||
EntityDao<T> dao = findDao(entityType); | |||
return dao.deleteEntity(whereSql, params); | |||
} | |||
/** | |||
* 读取实体对象ID值 | |||
* | |||
* @param entity | |||
* @param <T> | |||
* @return | |||
*/ | |||
public <T> Long readEntityId(T entity) { | |||
EntityDao<T> dao = findDao((Class<T>) entity.getClass()); | |||
return dao.readId(entity); | |||
} | |||
/** | |||
* 查询单行数据,返回bean | |||
*/ | |||
public <T> T queryEntity(Class<T> type, Long id) { | |||
return queryEntity(type, id, null); | |||
} | |||
public <T> T queryEntity(Class<T> type, Long id, String fields) { | |||
return findDao(type).queryEntity(id, fields); | |||
} | |||
/** | |||
* 查询对象所有数据,返回列表 | |||
*/ | |||
public <T> List<T> query(Class<T> type) { | |||
return query(type, null); | |||
} | |||
/** | |||
* 查询对象所有数据,返回列表 | |||
*/ | |||
public <T> List<T> query(Class<T> type, String fields) { | |||
return findDao(type).query(fields); | |||
} | |||
/** | |||
* 传入where条件查询实体类别 | |||
* | |||
* @param type 实体类型类 | |||
* @param sqlWhere sql的where语句部分,不包含from | |||
* @param params 条件参数值 | |||
* @param <T> 实体类型 | |||
* @return | |||
*/ | |||
public <T> List<T> queryWhere(Class<T> type, String sqlWhere, Object... params) { | |||
return findDao(type).queryWhere(sqlWhere, params); | |||
} | |||
/** | |||
* 批量插入单行数据 | |||
* | |||
* @param entities PO对象列表 | |||
* @param <T> 实体类型 | |||
* @return 更新数量 | |||
*/ | |||
public <T> int[] batchInsertEntity(List<T> entities) { | |||
if (entities == null || entities.isEmpty()) { | |||
return null; | |||
} | |||
return findDao((Class<T>) entities.get(0).getClass()).batchInsertEntity(entities, null); | |||
} | |||
/** | |||
* 批量插入单行数据 | |||
* | |||
* @param entities PO对象列表 | |||
* @param fields 逗号分隔的字段列表 | |||
* @param <T> 实体类型 | |||
* @return 更新数量 | |||
*/ | |||
public <T> int[] batchInsertEntity(List<T> entities, String fields) { | |||
if (entities == null || entities.isEmpty()) { | |||
return null; | |||
} | |||
return findDao((Class<T>) entities.get(0).getClass()).batchInsertEntity(entities, fields); | |||
} | |||
} |
@@ -0,0 +1,281 @@ | |||
package cc.smtweb.framework.core.db; | |||
import cc.smtweb.framework.core.db.dao.AbstractEntityDao; | |||
import cc.smtweb.framework.core.db.dao.EntityColumn; | |||
import cc.smtweb.framework.core.db.jdbc.JdbcEngine; | |||
import cc.smtweb.framework.core.db.vo.def.FieldType; | |||
import lombok.Getter; | |||
import org.apache.commons.lang3.StringUtils; | |||
import java.util.ArrayList; | |||
import java.util.List; | |||
/** | |||
* 提供数据对象Dao操作 | |||
* | |||
* @author xkliu | |||
*/ | |||
public class EntityDao<T> extends AbstractEntityDao<T> { | |||
@Getter | |||
protected JdbcEngine jdbcEngine; | |||
public EntityDao(Class<T> type, JdbcEngine jdbcEngine) { | |||
super(type); | |||
this.jdbcEngine = jdbcEngine; | |||
} | |||
public EntityDao(String tableName, JdbcEngine jdbcEngine) { | |||
super(tableName); | |||
this.jdbcEngine = jdbcEngine; | |||
} | |||
/** | |||
* 获取数据库唯一id | |||
* | |||
* @return 返回ID值 | |||
*/ | |||
public long nextId() { | |||
return this.jdbcEngine.nextId(); | |||
} | |||
/** | |||
* 根据PO对象的ID值更新其余所有字段 | |||
* | |||
* @param entity PO对象 | |||
* @return 更新数量 | |||
*/ | |||
public int updateEntity(T entity) { | |||
return updateEntity(entity, null, null); | |||
} | |||
/** | |||
* 使用ID字段更新单行数据 | |||
* | |||
* @param entity PO值对象,对象属性是需要更新的值 | |||
* @param fields 需要更新额字段列表,逗号分隔 | |||
* @return 更新数量 | |||
*/ | |||
public int updateEntity(T entity, String fields) { | |||
return updateEntity(entity, fields, null); | |||
} | |||
/** | |||
* 指定自定义条件更新对象 | |||
* | |||
* @param entity PO值对象,对象属性是需要更新的值和更新条件值 | |||
* @param fields 需要更新额字段列表,逗号分隔 | |||
* @param whereFields 更新条件字段列表,逗号分隔 | |||
* @return 更新数量 | |||
*/ | |||
public int updateEntity(T entity, String fields, String whereFields) { | |||
StringBuilder sb = new StringBuilder(); | |||
Object[] params = this.handleUpdate(entity, sb, fields, whereFields); | |||
return jdbcEngine.update(sb.toString(), params); | |||
} | |||
/** | |||
* 用PO对象所有字段入单行数据 | |||
* | |||
* @param type PO对象字段范围的类,是entity的父类 | |||
* @param entity PO对象 | |||
* @return 更新数量 | |||
*/ | |||
public int insertEntity(Class<? super T> type, T entity) { | |||
return insertEntity(type, entity, null); | |||
} | |||
/** | |||
* 用PO对象所有字段入单行数据 | |||
* | |||
* @param entity PO对象 | |||
* @return 更新数量 | |||
*/ | |||
public int insertEntity(T entity) { | |||
return insertEntity(entity.getClass(), entity, null); | |||
} | |||
/** | |||
* 插入单行数据 | |||
* | |||
* @param entity PO对象 | |||
* @param fields 逗号分隔的字段列表 | |||
* @return 更新数量 | |||
*/ | |||
public int insertEntity(T entity, String fields) { | |||
return insertEntity(entity.getClass(), entity, fields); | |||
} | |||
private int insertEntity(Class<?> type, T entity, String fields) { | |||
StringBuilder sb = new StringBuilder(); | |||
Object[] params = handleInsert(entity, sb, fields); | |||
return jdbcEngine.update(sb.toString(), params); | |||
} | |||
/** | |||
* 批量插入单行数据 | |||
* | |||
* @param entities PO对象列表 | |||
* @return 更新数量 | |||
*/ | |||
public int[] batchInsertEntity(List<T> entities) { | |||
return batchInsertEntity(entities, null); | |||
} | |||
/** | |||
* 批量插入单行数据 | |||
* | |||
* @param entities PO对象列表 | |||
* @param fields 逗号分隔的字段列表 | |||
* @return 更新数量 | |||
*/ | |||
public int[] batchInsertEntity(List<T> entities, String fields) { | |||
StringBuilder sql = new StringBuilder(); | |||
sql.append("insert into ").append(tableName).append("("); | |||
List<String> listFields = adjustFields(fields, FieldType.CREATE_TIME, FieldType.LAST_TIME); | |||
List<EntityColumn> insertColumns = new ArrayList<>(this.columns.size()); | |||
if (listFields == null) { | |||
for (EntityColumn column : this.columns.values()) { | |||
sql.append(column.getField().getName()).append(","); | |||
insertColumns.add(column); | |||
} | |||
} else { | |||
for (String name : listFields) { | |||
EntityColumn column = this.columns.get(name.trim()); | |||
sql.append(column.getField().getName()).append(","); | |||
insertColumns.add(column); | |||
} | |||
} | |||
sql.setCharAt(sql.length() - 1, ')'); | |||
// values(?,?) | |||
sql.append(" values("); | |||
for (int i = insertColumns.size(); i > 0; i--) { | |||
sql.append("?,"); | |||
} | |||
sql.setCharAt(sql.length() - 1, ')'); | |||
// 参数列表 | |||
List<Object[]> listParams = new ArrayList<>(entities.size()); | |||
for (T obj : entities) { | |||
List<Object> params = new ArrayList<>(this.columns.size()); | |||
for (EntityColumn column : this.columns.values()) { | |||
params.add(column.readValue(obj)); | |||
} | |||
listParams.add(params.toArray()); | |||
} | |||
return jdbcEngine.batchUpdate(sql.toString(), listParams); | |||
} | |||
/** | |||
* 根据ID值删除单行数据 | |||
* | |||
* @param entity PO对象 | |||
* @return 删除数量 | |||
*/ | |||
public int deleteEntity(T entity) { | |||
StringBuilder sb = new StringBuilder(); | |||
Object[] params = handleDelete(entity, sb); | |||
return jdbcEngine.update(sb.toString(), params); | |||
} | |||
/** | |||
* 根据ID值删除单行数据 | |||
* | |||
* @param id 记录主建值 | |||
* @return 删除数量 | |||
*/ | |||
public int deleteEntity(Long id) { | |||
StringBuilder sb = new StringBuilder(); | |||
handleDelete(sb); | |||
return jdbcEngine.update(sb.toString(), id); | |||
} | |||
/** | |||
* 根据ID值删除单行数据 | |||
* | |||
* @param whereSql Where条件SQL语句,以where开头 | |||
* @param params 条件的值,可以多个 | |||
* @return 删除数量 | |||
*/ | |||
public int deleteEntity(String whereSql, Object... params) { | |||
StringBuilder sb = new StringBuilder("DELETE FROM "); | |||
sb.append(getTableName()).append(" ").append(whereSql); | |||
return jdbcEngine.update(sb.toString(), params); | |||
} | |||
/** | |||
* 读取实体对象ID值 | |||
* | |||
* @param entity | |||
* @return | |||
*/ | |||
public Long readEntityId(T entity) { | |||
return (Long) readId(entity); | |||
} | |||
/** | |||
* 查询单行数据,返回bean | |||
*/ | |||
public T queryEntity(Long id) { | |||
return queryEntity(id, null); | |||
} | |||
public T queryEntity(Long id, String fields) { | |||
StringBuilder sb = new StringBuilder(); | |||
handleSelectOne(sb, fields); | |||
List<T> list = jdbcEngine.query(sb.toString(), type, id); | |||
if (list != null && !list.isEmpty()) { | |||
return list.get(0); | |||
} | |||
return null; | |||
} | |||
/** | |||
* 查询对象所有数据,返回列表 | |||
*/ | |||
public List<T> query() { | |||
return query(null); | |||
} | |||
/** | |||
* 查询对象所有数据,返回列表 | |||
*/ | |||
public List<T> query(String fields) { | |||
StringBuilder sb = new StringBuilder(); | |||
handleSelect(sb, fields); | |||
return jdbcEngine.query(sb.toString(), type); | |||
} | |||
/** | |||
* 查询对象所有数据,返回列表 | |||
*/ | |||
public List<T> queryWhere(String sqlWhere, Object... params) { | |||
StringBuilder sb = new StringBuilder(); | |||
handleSelect(sb, null); | |||
if (StringUtils.isNotEmpty(sqlWhere)) { | |||
sb.append(" where ").append(sqlWhere); | |||
} | |||
return jdbcEngine.query(sb.toString(), type, params); | |||
} | |||
} |
@@ -0,0 +1,64 @@ | |||
package cc.smtweb.framework.core.db.cache; | |||
import cc.smtweb.framework.core.annotation.SwCache; | |||
import cc.smtweb.framework.core.cache.AbstractCache; | |||
import cc.smtweb.framework.core.db.DbEngine; | |||
import cc.smtweb.framework.core.db.EntityDao; | |||
import cc.smtweb.framework.core.db.impl.DefaultEntity; | |||
import cc.smtweb.framework.core.db.vo.ModelCache; | |||
import cc.smtweb.framework.core.db.vo.ModelTable; | |||
import cc.smtweb.framework.core.util.CommUtil; | |||
import com.github.benmanes.caffeine.cache.Caffeine; | |||
import com.github.benmanes.caffeine.cache.Scheduler; | |||
import org.apache.commons.lang3.StringUtils; | |||
import org.checkerframework.checker.nullness.qual.NonNull; | |||
import java.util.List; | |||
import java.util.concurrent.ScheduledExecutorService; | |||
import java.util.concurrent.TimeUnit; | |||
/** | |||
* Created by Akmm at 2022/2/19 17:19 | |||
*/ | |||
public class EntityCache extends AbstractCache<DefaultEntity> { | |||
private String tableName; | |||
public EntityCache(String tableName) { | |||
this.tableName = tableName; | |||
ModelTable table = ModelTableCache.getInstance().getByName(tableName); | |||
ident = tableName; | |||
title = table.getTitle(); | |||
lazy = false; | |||
if (!table.isNeedCache() || CommUtil.isEmpty(table.getCaches())) return; | |||
for (ModelCache cache : table.getCaches()) { | |||
final IGetBeanKey<DefaultEntity> key = bean -> { | |||
String ret = ""; | |||
for (String s : StringUtils.split(cache.getFields(), ",")) { | |||
if (StringUtils.isNotEmpty(s)) { | |||
ret += "_" + bean.getStr(s); | |||
} | |||
} | |||
return ret; | |||
}; | |||
if (cache.isMapType()) { | |||
regMap(cache.getName(), key); | |||
} else { | |||
regList(cache.getName(), key); | |||
} | |||
} | |||
} | |||
@Override | |||
protected String getId(DefaultEntity bean) { | |||
return String.valueOf(bean.getEntityId()); | |||
} | |||
@Override | |||
protected List<DefaultEntity> loadAll() { | |||
EntityDao<DefaultEntity> dao = DbEngine.getInstance().findDao(tableName); | |||
return dao.query(); | |||
} | |||
} |
@@ -0,0 +1,45 @@ | |||
package cc.smtweb.framework.core.db.cache; | |||
import cc.smtweb.framework.core.annotation.SwCache; | |||
import cc.smtweb.framework.core.cache.AbstractCache; | |||
import cc.smtweb.framework.core.cache.CacheManager; | |||
import cc.smtweb.framework.core.db.DbEngine; | |||
import cc.smtweb.framework.core.db.EntityDao; | |||
import cc.smtweb.framework.core.db.vo.ModelDatabase; | |||
import org.springframework.beans.factory.annotation.Autowired; | |||
import org.springframework.jdbc.core.RowMapper; | |||
import java.sql.ResultSet; | |||
import java.sql.SQLException; | |||
import java.util.List; | |||
/** | |||
* Created by Akmm at 2022/1/12 18:34 | |||
*/ | |||
@SwCache(ident = "ASP_MODEL_DATABASE", title = "数据库") | |||
public class ModelDatabaseCache extends AbstractCache<ModelDatabase> { | |||
private final static String mk = "k"; | |||
public static ModelDatabaseCache getInstance() { | |||
return CacheManager.getIntance().getCache(ModelDatabaseCache.class); | |||
} | |||
public ModelDatabaseCache() { | |||
regMap(mk, ModelDatabase::getName); | |||
} | |||
@Override | |||
protected String getId(ModelDatabase bean) { | |||
return String.valueOf(bean.getId()); | |||
} | |||
@Override | |||
protected List<ModelDatabase> loadAll() { | |||
EntityDao<ModelDatabase> dao = DbEngine.getInstance().findDao(ModelDatabase.class); | |||
return dao.query(); | |||
} | |||
public final ModelDatabase getByName(String key) { | |||
return localGetByKey(mk, key); | |||
} | |||
} |
@@ -0,0 +1,90 @@ | |||
package cc.smtweb.framework.core.db.cache; | |||
import cc.smtweb.framework.core.annotation.SwCache; | |||
import cc.smtweb.framework.core.cache.AbstractCache; | |||
import cc.smtweb.framework.core.cache.CacheManager; | |||
import cc.smtweb.framework.core.db.DbEngine; | |||
import cc.smtweb.framework.core.db.vo.ModelTable; | |||
import org.springframework.dao.DataAccessException; | |||
import org.springframework.jdbc.core.ResultSetExtractor; | |||
import java.sql.ResultSet; | |||
import java.sql.SQLException; | |||
import java.util.ArrayList; | |||
import java.util.List; | |||
import java.util.Set; | |||
/** | |||
* Created by Akmm at 2022/1/12 18:34 | |||
*/ | |||
@SwCache(ident = "ASP_MODEL_TABLE", title = "数据库表定义") | |||
public class ModelTableCache extends AbstractCache<ModelTable> { | |||
private final static String mk = "k"; | |||
private final static String md = "d"; | |||
public static ModelTableCache getInstance() { | |||
return CacheManager.getIntance().getCache(ModelTableCache.class); | |||
} | |||
public ModelTableCache() { | |||
regMap(mk, k-> k.getName().toUpperCase()); | |||
regList(md, k-> String.valueOf(k.getDatabaseId())); | |||
} | |||
@Override | |||
protected String getId(ModelTable bean) { | |||
return String.valueOf(bean.getId()); | |||
} | |||
@Override | |||
protected List<ModelTable> loadAll() { | |||
return DbEngine.getInstance().query("SELECT\n" + | |||
"t.table_id,\n" + | |||
"t.prjoect_id,\n" + | |||
"t.catalog_id,\n" + | |||
"t.database_id,\n" + | |||
"t.table_extends,\n" + | |||
"t.table_name,\n" + | |||
"t.table_title,\n" + | |||
"t.table_abbr,\n" + | |||
"t.table_type,\n" + | |||
"t.need_cache,\n" + | |||
"t.table_content,\n" + | |||
"t.table_create_uid,\n" + | |||
"t.table_update_uid,\n" + | |||
"t.table_create_at,\n" + | |||
"t.table_update_at\n" + | |||
"from asp_model_table t\n", new ResultSetExtractor<List<ModelTable>>() { | |||
@Override | |||
public List<ModelTable> extractData(ResultSet rs) throws SQLException, DataAccessException { | |||
List<ModelTable> list = new ArrayList<>(); | |||
while (rs.next()) { | |||
ModelTable table = new ModelTable(); | |||
list.add(table); | |||
table.setId(rs.getLong("table_id")); | |||
table.setDatabaseId(rs.getLong("database_id")); | |||
table.setPrjoectId(rs.getLong("prjoect_id")); | |||
table.setCatalogId(rs.getLong("catalog_id")); | |||
table.setTableExtends(rs.getString("table_extends")); | |||
table.setName(rs.getString("table_name").toUpperCase()); | |||
table.setTitle(rs.getString("table_title")); | |||
table.setAbbr(rs.getString("table_abbr")); | |||
table.setType(rs.getInt("table_type")); | |||
table.setNeedCache(rs.getInt("need_cache") == 1); | |||
table.setCreateUid(rs.getLong("table_create_uid")); | |||
table.setCreateAt(rs.getLong("table_create_at")); | |||
table.setUpdateAt(rs.getLong("table_update_at")); | |||
table.setTableContent(rs.getString("table_content")); | |||
} | |||
return list; | |||
} | |||
}); | |||
} | |||
public final ModelTable getByName(String key) { | |||
return localGetByKey(mk, key.toUpperCase()); | |||
} | |||
public final Set<ModelTable> getDbTables(long dbId) { | |||
return localGetListByKey(md, String.valueOf(dbId)); | |||
} | |||
} |
@@ -0,0 +1,41 @@ | |||
package cc.smtweb.framework.core.db.config; | |||
import cc.smtweb.framework.core.db.DbEngine; | |||
import cc.smtweb.framework.core.db.jdbc.IdGenerator; | |||
import org.springframework.beans.factory.annotation.Value; | |||
import org.springframework.boot.context.properties.ConfigurationProperties; | |||
import org.springframework.context.annotation.Bean; | |||
import org.springframework.context.annotation.Configuration; | |||
import org.springframework.jdbc.core.JdbcTemplate; | |||
/** | |||
* 默认数据源 | |||
* @author xkliu | |||
*/ | |||
@Configuration | |||
public class DbEngineConfiguration { | |||
@Value("${smtweb.db.type}") | |||
private String dbType; | |||
// @Bean | |||
// public DataSource dataSource() { | |||
// return DataSourceBuilder.create().build(); | |||
// } | |||
// | |||
// @Bean | |||
// public NamedParameterJdbcTemplate jdbcTemplate(DataSource dataSource) { | |||
// return new NamedParameterJdbcTemplate(dataSource); | |||
// } | |||
/** | |||
* 产生数据库数据库访问对象 dbEngine | |||
* @param jdbcTemplate Spring框架Jdbc,通过 spring.datasource 配置 | |||
* @param idGenerator ID生成器对象,思想数据库ID生成 | |||
* @return dbEngine对象 | |||
*/ | |||
@Bean | |||
@ConfigurationProperties(prefix = "smtweb.db.default") | |||
public DbEngine dbEngine(JdbcTemplate jdbcTemplate, IdGenerator idGenerator) { | |||
System.out.println("create dbEngine============="); | |||
return new DbEngine(jdbcTemplate, idGenerator, dbType); | |||
} | |||
} |
@@ -0,0 +1,43 @@ | |||
package cc.smtweb.framework.core.db.config; | |||
import cc.smtweb.framework.core.db.vo.def.DataType; | |||
import org.springframework.boot.context.properties.ConfigurationProperties; | |||
import org.springframework.context.annotation.PropertySource; | |||
import org.springframework.stereotype.Component; | |||
import java.util.HashMap; | |||
import java.util.List; | |||
import java.util.Map; | |||
@Component | |||
//通过@PropertySource注解指定要读取的yaml配置文件,默认读取src\main\resources\application.yml配置 | |||
@PropertySource(value = "classpath:config/design_db.yaml", factory = YamlPropertyLoaderFactory.class) | |||
@ConfigurationProperties(prefix = "design") | |||
public class DesignConfig { | |||
private List<DataType> dataTypes; | |||
private Map<String, DataType> mapType = null; | |||
private void adjustMap() { | |||
if (mapType == null) { | |||
synchronized (DesignConfig.class) { | |||
if (mapType == null) { | |||
Map<String, DataType> map = new HashMap<>(); | |||
for (DataType type : dataTypes) { | |||
map.put(type.getType(), type); | |||
} | |||
mapType = map; | |||
} | |||
} | |||
} | |||
} | |||
public DataType getDataType(String type) { | |||
adjustMap(); | |||
return mapType.get(type); | |||
} | |||
public List<DataType> getDataTypes() { | |||
return dataTypes; | |||
} | |||
} |
@@ -0,0 +1,23 @@ | |||
package cc.smtweb.framework.core.db.config; | |||
import org.springframework.boot.env.YamlPropertySourceLoader; | |||
import org.springframework.core.env.PropertySource; | |||
import org.springframework.core.io.support.DefaultPropertySourceFactory; | |||
import org.springframework.core.io.support.EncodedResource; | |||
import java.io.IOException; | |||
/** | |||
* 实现yaml配置文件加载工厂,以使用@PropertySource注解加载指定yaml文件的配置 | |||
*/ | |||
public class YamlPropertyLoaderFactory extends DefaultPropertySourceFactory { | |||
@Override | |||
public PropertySource<?> createPropertySource(String name, EncodedResource resource) throws IOException { | |||
if (null == resource) { | |||
super.createPropertySource(name, resource); | |||
} | |||
return new YamlPropertySourceLoader().load(resource.getResource().getFilename(), resource.getResource()).get(0); | |||
} | |||
} |
@@ -0,0 +1,383 @@ | |||
package cc.smtweb.framework.core.db.dao; | |||
import cc.smtweb.framework.core.annotation.SwTable; | |||
import cc.smtweb.framework.core.cache.CacheManager; | |||
import cc.smtweb.framework.core.db.cache.ModelTableCache; | |||
import cc.smtweb.framework.core.db.impl.BaseBean; | |||
import cc.smtweb.framework.core.db.impl.DefaultEntity; | |||
import cc.smtweb.framework.core.db.vo.def.FieldType; | |||
import cc.smtweb.framework.core.db.vo.ModelField; | |||
import cc.smtweb.framework.core.db.vo.ModelTable; | |||
import cc.smtweb.framework.core.exception.DbException; | |||
import cc.smtweb.framework.core.util.DateUtil; | |||
import cc.smtweb.framework.core.util.SpringUtil; | |||
import cc.smtweb.framework.core.util.VariableUtil; | |||
import org.apache.commons.lang3.StringUtils; | |||
import org.springframework.util.ClassUtils; | |||
import java.beans.IntrospectionException; | |||
import java.beans.PropertyDescriptor; | |||
import java.lang.reflect.Method; | |||
import java.util.*; | |||
/** | |||
* 抽象的值对象数据库访问 | |||
* | |||
* @param <T> 数据库值对象类型 | |||
*/ | |||
public abstract class AbstractEntityDao<T> { | |||
protected ModelTable modelTable; | |||
protected String tableName; | |||
protected Map<String, EntityColumn> columns = new HashMap<>(); | |||
protected Class<T> type; | |||
protected CacheManager cacheManager = SpringUtil.getBean(CacheManager.class); | |||
// public AbstractEntityDao(String tableName) { | |||
// this.tableName = tableName; | |||
// } | |||
/** | |||
* 通过值对象类型构造值对象数据库访问 | |||
* | |||
* @param type 值对象类型 | |||
*/ | |||
public AbstractEntityDao(Class<T> type) { | |||
this.type = type; | |||
// type.isAnnotationPresent(Table.class); | |||
SwTable table = type.getAnnotation(SwTable.class); | |||
Class<? super T> superclass = type.getSuperclass(); | |||
if (table == null && superclass != null) { | |||
table = superclass.getAnnotation(SwTable.class); | |||
} | |||
if (table == null) { | |||
throw new IllegalAccessError("not find annotation @SwTable"); | |||
} | |||
tableName = table.value(); | |||
modelTable = ModelTableCache.getInstance().getByName(tableName); | |||
for (ModelField field : modelTable.getFields()) { | |||
String voFieldName = getVoFieldName(modelTable.getAbbr(), field.getName()); | |||
// 处理get method | |||
try { | |||
PropertyDescriptor pd = new PropertyDescriptor(voFieldName, type); | |||
// 获得get方法 | |||
Method readMethod = pd.getReadMethod(); | |||
Method writeMethod = pd.getWriteMethod(); | |||
if (readMethod != null && writeMethod != null) { | |||
Class<?>[] parameterTypes = writeMethod.getParameterTypes(); | |||
if (parameterTypes.length == 1 && ClassUtils.isAssignable(parameterTypes[0], readMethod.getReturnType())) { | |||
add(field, readMethod, writeMethod); | |||
} | |||
} | |||
} catch (IntrospectionException ignore) { | |||
} | |||
} | |||
} | |||
/** | |||
* 通过值对象类型构造值对象数据库访问 | |||
* | |||
* @param tableName 表名 | |||
*/ | |||
public AbstractEntityDao(String tableName) { | |||
this.tableName = tableName; | |||
this.type = (Class<T>)DefaultEntity.class; | |||
modelTable = cacheManager.getCache(ModelTableCache.class).getByName(tableName); | |||
for (ModelField field : modelTable.getFields()) { | |||
add(field, null, null); | |||
} | |||
} | |||
/** | |||
* 根据字段名,得到类中的字段属性名 | |||
* @param tbAbbr 表缩写,一般是字段的前缀 | |||
* @param fieldName 字段名 | |||
* @return | |||
*/ | |||
private String getVoFieldName(String tbAbbr, String fieldName) { | |||
fieldName = fieldName.toLowerCase(); | |||
tbAbbr = tbAbbr.toLowerCase() + "_"; | |||
if (fieldName.startsWith(tbAbbr)) return VariableUtil.underlineToHump(fieldName.substring(tbAbbr.length())); | |||
return VariableUtil.underlineToHump(fieldName); | |||
} | |||
/** | |||
* 添加值对象的属性访问方法何注解值 | |||
* | |||
* @param field 字段名 | |||
* @param readMethod 读值对象属性方法 | |||
* @param writeMethod 写值值对象属性方法 | |||
*/ | |||
protected void add(ModelField field, Method readMethod, Method writeMethod) { | |||
EntityColumn beanColumn = new EntityColumn(field, readMethod, writeMethod); | |||
columns.put(field.getName(), beanColumn); | |||
} | |||
protected void updateTime(T obj, FieldType type) { | |||
ModelField field = modelTable.findFieldByType(type); | |||
if (field == null) return; | |||
EntityColumn col = columns.get(field.getName()); | |||
if (col != null) { | |||
col.writeValue(obj, DateUtil.nowDateTimeLong()); | |||
} | |||
} | |||
/** | |||
* 校验传入的字段,如没有创建时间和最后更改时间,则加上 | |||
* | |||
* @param fields | |||
* @return | |||
*/ | |||
protected List<String> adjustFields(String fields, FieldType... types) { | |||
if (StringUtils.isEmpty(fields)) return null; | |||
String[] fieldNames = fields.toLowerCase().split(","); | |||
List<String> listFields = new ArrayList<>(fieldNames.length + 2); | |||
boolean[] includeTypes = new boolean[types.length]; | |||
for (int i = 0, len = includeTypes.length; i < len; i++) { | |||
includeTypes[i] = false; | |||
} | |||
for (String name : fieldNames) { | |||
EntityColumn column = this.columns.get(name.trim()); | |||
for (int i = 0, len = types.length; i < len; i++) { | |||
if (types[i].name().equalsIgnoreCase(column.getField().getFieldType())) { | |||
includeTypes[i] = true; | |||
} | |||
} | |||
listFields.add(name); | |||
} | |||
for (int i = 0, len = types.length; i < len; i++) { | |||
if (!includeTypes[i]) { | |||
ModelField field = modelTable.findFieldByType(types[i]); | |||
if (field != null) listFields.add(field.getName()); | |||
} | |||
} | |||
return listFields; | |||
} | |||
/** | |||
* 拼接插入SQL语句 | |||
* | |||
* @param obj 值对象 | |||
* @param sql 记录sql的字符缓存 | |||
* @param fields 逗号分割字段列表,设置为null表示使用所有字段 | |||
* @return SQL参数列表 | |||
*/ | |||
protected Object[] handleInsert(T obj, StringBuilder sql, String fields) { | |||
List<Object> result; | |||
List<String> listFields = adjustFields(fields, FieldType.CREATE_TIME, FieldType.LAST_TIME); | |||
sql.append("insert into ").append(tableName).append("("); | |||
updateTime(obj, FieldType.CREATE_TIME); | |||
updateTime(obj, FieldType.LAST_TIME); | |||
if (listFields == null) { | |||
result = new ArrayList<>(this.columns.size()); | |||
for (EntityColumn column : this.columns.values()) { | |||
result.add(column.readValue(obj)); | |||
sql.append(column.getField().getName()).append(","); | |||
} | |||
} else { | |||
result = new ArrayList<>(listFields.size()); | |||
for (String name : listFields) { | |||
EntityColumn column = this.columns.get(name.trim()); | |||
result.add(column.readValue(obj)); | |||
sql.append(column.getField().getName()).append(","); | |||
} | |||
} | |||
sql.setCharAt(sql.length() - 1, ')'); | |||
// values(?,?) | |||
sql.append(" values("); | |||
for (int i = result.size(); i > 0; i--) { | |||
sql.append("?,"); | |||
} | |||
sql.setCharAt(sql.length() - 1, ')'); | |||
return result.toArray(); | |||
} | |||
/** | |||
* 拼接更新SQL语句 | |||
* | |||
* @param obj 值对象 | |||
* @param sql 记录sql的字符缓存 | |||
* @param fields 逗号分割更新字段列表,设置为null表示使用所有字段 | |||
* @param whereFields 逗号分割查询字段列表,设置为null表示使用ID字段作为查询条件 | |||
* @return SQL参数列表 | |||
*/ | |||
protected Object[] handleUpdate(T obj, StringBuilder sql, String fields, String whereFields) { | |||
EntityColumn idColumn = findIdColumn(); | |||
List<String> listFields = adjustFields(fields, FieldType.LAST_TIME); | |||
updateTime(obj, FieldType.LAST_TIME); | |||
sql.append("update ").append(tableName).append(" set "); | |||
List<Object> result = new ArrayList<>(); | |||
if (listFields == null) { | |||
for (EntityColumn column : this.columns.values()) { | |||
if (idColumn != column) { | |||
sql.append(column.getField().getName()).append("=?,"); | |||
} | |||
} | |||
sql.setCharAt(sql.length() - 1, ' '); | |||
// 默认使用Id字段条件 | |||
result.add(idColumn.readValue(obj)); | |||
sql.append("where ").append(idColumn.getField().getName()).append("=?"); | |||
} else { | |||
for (String name : listFields) { | |||
name = name.trim(); | |||
EntityColumn beanColumn = getBeanColumn(name); | |||
sql.append(name).append("=?,"); | |||
result.add(beanColumn.readValue(obj)); | |||
} | |||
sql.setCharAt(sql.length() - 1, ' '); | |||
if (StringUtils.isNotBlank(whereFields)) { | |||
sql.append("where "); | |||
boolean first = true; | |||
for (String name : whereFields.split(",")) { | |||
name = name.trim(); | |||
if (first) { | |||
first = false; | |||
} else { | |||
sql.append(" and "); | |||
} | |||
sql.append(name).append("=?"); | |||
result.add(readValue(obj, name)); | |||
} | |||
} else { | |||
// 默认使用Id字段条件 | |||
result.add(idColumn.readValue(obj)); | |||
sql.append("where ").append(idColumn.getField().getName()).append("=?"); | |||
} | |||
} | |||
return result.toArray(); | |||
} | |||
private EntityColumn findIdColumn() { | |||
ModelField field = modelTable.findIdField(); | |||
EntityColumn idColumn = field != null ? columns.get(field.getName()) : null; | |||
if (idColumn == null) { | |||
throw new DbException(tableName + " not define id column"); | |||
} | |||
return idColumn; | |||
} | |||
/** | |||
* 拼接删除值对象语句 | |||
* | |||
* @param obj 值对象 | |||
* @param sql 记录sql的字符缓存 | |||
* @return SQL参数列表 | |||
*/ | |||
protected Object[] handleDelete(T obj, StringBuilder sql) { | |||
EntityColumn idColumn = findIdColumn(); | |||
sql.append("DELETE FROM ").append(tableName).append(" WHERE ").append(idColumn.getField()).append("=?"); | |||
return new Object[]{idColumn.readValue(obj)}; | |||
} | |||
/** | |||
* 拼接删除值对象语句,条件由外部设置 | |||
* | |||
* @param sql 记录sql的字符缓存 | |||
*/ | |||
protected void handleDelete(StringBuilder sql) { | |||
EntityColumn idColumn = findIdColumn(); | |||
sql.append("DELETE FROM ").append(tableName).append(" WHERE ").append(idColumn.getField().getName()).append("=?"); | |||
} | |||
private Object readValue(T obj, String fieldName) { | |||
EntityColumn beanColumn = getBeanColumn(fieldName); | |||
return beanColumn.readValue(obj); | |||
} | |||
private EntityColumn getBeanColumn(String fieldName) { | |||
EntityColumn beanColumn = this.columns.get(fieldName); | |||
if (beanColumn == null) { | |||
throw new DbException("not define column:" + fieldName); | |||
} | |||
return beanColumn; | |||
} | |||
/** | |||
* 获取表名 | |||
* | |||
* @return 表名 | |||
*/ | |||
protected String getTableName() { | |||
return tableName; | |||
} | |||
/** | |||
* 拼接查询SQL语句 | |||
* | |||
* @param sql SQL字符缓存 | |||
* @param fields 逗号分割的查询字段列表,传入null表示全部值对象字段 | |||
*/ | |||
public void handleSelect(StringBuilder sql, String fields) { | |||
sql.append("select "); | |||
if (fields != null) { | |||
sql.append(fields).append(' '); | |||
} else { | |||
for (String fieldName : columns.keySet()) { | |||
sql.append(fieldName).append(','); | |||
} | |||
sql.setCharAt(sql.length() - 1, ' '); | |||
} | |||
sql.append("from ").append(tableName); | |||
} | |||
protected void handleSelectOne(StringBuilder sql, String fields) { | |||
EntityColumn idColumn = findIdColumn(); | |||
sql.append("select "); | |||
if (fields != null) { | |||
sql.append(fields).append(' '); | |||
} else { | |||
for (EntityColumn field : columns.values()) { | |||
sql.append(field.getField().getName()).append(","); | |||
} | |||
sql.setCharAt(sql.length() - 1, ' '); | |||
} | |||
sql.append("from ").append(tableName).append(" where ").append(idColumn.getField().getName()).append("=?"); | |||
} | |||
/** | |||
* 读取ID值 | |||
* | |||
* @param entity | |||
* @return | |||
*/ | |||
public Long readId(T entity) { | |||
EntityColumn idColumn = findIdColumn(); | |||
return (Long) idColumn.readValue(entity); | |||
} | |||
} | |||
@@ -0,0 +1,66 @@ | |||
package cc.smtweb.framework.core.db.dao; | |||
import cc.smtweb.framework.core.db.impl.DefaultEntity; | |||
import cc.smtweb.framework.core.db.vo.ModelField; | |||
import cc.smtweb.framework.core.exception.DbException; | |||
import lombok.Getter; | |||
import java.lang.reflect.InvocationTargetException; | |||
import java.lang.reflect.Method; | |||
/** | |||
* 值对象字段处理类 | |||
* @author xkliu | |||
*/ | |||
@Getter | |||
public class EntityColumn { | |||
private ModelField field; | |||
private final Method readMethod; | |||
private final Method writeMethod; | |||
/** | |||
* 构建值对象字段 | |||
* @param field 字段名 | |||
* @param readMethod 读值方法 | |||
* @param writeMethod 写值方法 | |||
*/ | |||
public EntityColumn(ModelField field, Method readMethod, Method writeMethod) { | |||
this.field = field; | |||
this.readMethod = readMethod; | |||
this.writeMethod = writeMethod; | |||
} | |||
/** | |||
* 从对象中读取字段对应的属性值 | |||
* @param obj 值对象 | |||
* @return 属性值 | |||
*/ | |||
public Object readValue(Object obj) { | |||
if (readMethod != null) { | |||
try { | |||
return readMethod.invoke(obj); | |||
} catch (IllegalArgumentException | IllegalAccessException | InvocationTargetException e) { | |||
throw new DbException(e); | |||
} | |||
} else { | |||
return ((DefaultEntity)obj).get(field.getName()); | |||
} | |||
} | |||
/** | |||
* 写入值到对象字段对象属性 | |||
* @param obj 值对象 | |||
* @param value 属性值 | |||
*/ | |||
public void writeValue(Object obj, Object value) { | |||
if (readMethod != null) { | |||
try { | |||
writeMethod.invoke(obj, value); | |||
} catch (IllegalArgumentException | IllegalAccessException | InvocationTargetException e) { | |||
throw new DbException(e); | |||
} | |||
} else { | |||
((DefaultEntity)obj).put(field.getName(), value); | |||
} | |||
} | |||
} |
@@ -0,0 +1,26 @@ | |||
package cc.smtweb.framework.core.db.dao; | |||
import lombok.Getter; | |||
/** | |||
* 字段外键属性 | |||
* @author admin | |||
*/ | |||
@Getter | |||
public class EntityColumnForeign { | |||
private final String table; | |||
private final String id; | |||
private final String name; | |||
/** | |||
* 构造字段外键属性 | |||
* @param table 外键表名 | |||
* @param id 外键ID字段名 | |||
* @param name 外键名称字段名 | |||
*/ | |||
public EntityColumnForeign(String table, String id, String name) { | |||
this.table = table; | |||
this.id = id; | |||
this.name = name; | |||
} | |||
} |
@@ -0,0 +1,98 @@ | |||
package cc.smtweb.framework.core.db.impl; | |||
import cc.smtweb.framework.core.util.JsonUtil; | |||
import cc.smtweb.framework.core.util.NumberUtil; | |||
import com.fasterxml.jackson.databind.JsonNode; | |||
import java.io.Serializable; | |||
import java.util.HashMap; | |||
import java.util.Iterator; | |||
import java.util.Map; | |||
/** | |||
* Created by Akmm at 2016-02-23 9:31 | |||
* bean基类,基于Map, | |||
*/ | |||
public class BaseBean implements Serializable { | |||
protected Map<String, Object> data = new HashMap<>(); | |||
public Map<String, Object> getData() { | |||
return data; | |||
} | |||
public void setData(Map<String, Object> data) { | |||
this.data = data; | |||
} | |||
@Override | |||
public BaseBean clone() throws CloneNotSupportedException { | |||
BaseBean bean = (BaseBean) super.clone(); | |||
bean.data = new HashMap<>(); | |||
bean.getData().putAll(this.data); | |||
return bean; | |||
} | |||
public boolean isEmpty() { | |||
return data.isEmpty(); | |||
} | |||
public String getStr(String fieldName) { | |||
return getStr(fieldName, ""); | |||
} | |||
public String getStr(String fieldName, String defValue) { | |||
Object o = data.get(fieldName); | |||
return o != null ? o.toString() : defValue; | |||
} | |||
public double getDouble(String fieldName) { | |||
return getDouble(fieldName, 0.0); | |||
} | |||
public double getDouble(String fieldName, double defValue) { | |||
Object o = data.get(fieldName); | |||
if (o == null) return defValue; | |||
if (o instanceof Number) return ((Number) o).doubleValue(); | |||
return NumberUtil.getDoubleIgnoreErr(o.toString()); | |||
} | |||
public int getInt(String fieldName) { | |||
Object o = data.get(fieldName); | |||
if (o == null) return 0; | |||
if (o instanceof Number) return ((Number) o).intValue(); | |||
return NumberUtil.getIntIgnoreErr(o.toString()); | |||
} | |||
public long getLong(String fieldName) { | |||
Object o = data.get(fieldName); | |||
if (o == null) return 0L; | |||
if (o instanceof Number) return ((Number) o).longValue(); | |||
return NumberUtil.getLongIgnoreErr(o.toString()); | |||
} | |||
public boolean getBool(String fieldName) { | |||
Object o = data.get(fieldName); | |||
if (o == null) return false; | |||
String v = o.toString(); | |||
return "1".equals(v) || "t".equalsIgnoreCase(v) || "true".equalsIgnoreCase(v); | |||
} | |||
public void put(String fieldName, Object value) { | |||
this.data.put(fieldName, value); | |||
} | |||
public void setBool(String fieldName, boolean value) { | |||
this.data.put(fieldName, value ? 1 : 0); | |||
} | |||
public Object get(String fieldName) { | |||
return this.data.get(fieldName); | |||
} | |||
public void readFromJson(String json){ | |||
Map map = JsonUtil.parseMap(json); | |||
if (map != null) { | |||
data.putAll(map); | |||
} | |||
} | |||
} |
@@ -0,0 +1,362 @@ | |||
package cc.smtweb.framework.core.db.impl; | |||
import cc.smtweb.framework.core.db.DbEngine; | |||
import cc.smtweb.framework.core.util.SpringUtil; | |||
import lombok.extern.slf4j.Slf4j; | |||
import org.apache.commons.lang3.StringUtils; | |||
import org.springframework.dao.DataAccessException; | |||
import org.springframework.jdbc.core.ConnectionCallback; | |||
import org.springframework.jdbc.support.JdbcUtils; | |||
import java.sql.Connection; | |||
import java.sql.DatabaseMetaData; | |||
import java.sql.ResultSet; | |||
import java.sql.SQLException; | |||
import java.util.*; | |||
import java.util.function.Function; | |||
/** | |||
* Created with IntelliJ IDEA. | |||
* User: AKhh | |||
* Date: 12-12-21 下午10:14 | |||
* 获取连接的数据库相关信息,from ofbiz | |||
*/ | |||
@Slf4j | |||
public class DefaultDatabaseInfoImpl implements IDatabaseInfo { | |||
private static final String MODULE = DefaultDatabaseInfoImpl.class.getName(); | |||
private DbEngine dbEngine = SpringUtil.getBean(DbEngine.class); | |||
@SuppressWarnings("unchecked") | |||
public void printDatabaseInfo() { | |||
dbEngine.doConn(connection -> { | |||
try { | |||
DatabaseMetaData dbData = connection.getMetaData(); | |||
// 数据库信息 | |||
try { | |||
log.debug("Database Product Name is " + dbData.getDatabaseProductName(), MODULE); | |||
log.debug("Database Product Version is " + dbData.getDatabaseProductVersion(), MODULE); | |||
} catch (SQLException sqle) { | |||
log.debug("Unable to get Database name & version information", MODULE); | |||
} | |||
// JDBC Driver Info | |||
try { | |||
log.debug("Database Driver Name is " + dbData.getDriverName(), MODULE); | |||
log.debug("Database Driver Version is " + dbData.getDriverVersion(), MODULE); | |||
} catch (SQLException sqle) { | |||
log.debug("Unable to get Driver name & version information", MODULE); | |||
} | |||
// Db/Driver support settings | |||
try { | |||
log.debug("Database Setting/Support Information (those with a * should be true):", MODULE); | |||
log.debug("- supports transactions [" + dbData.supportsTransactions() + "]*", MODULE); | |||
log.debug("- isolation None [" + dbData.supportsTransactionIsolationLevel(Connection.TRANSACTION_NONE) + "]", MODULE); | |||
log.debug("- isolation ReadCommitted [" + dbData.supportsTransactionIsolationLevel(Connection.TRANSACTION_READ_COMMITTED) + "]", MODULE); | |||
log.debug("- isolation ReadUncommitted[" + dbData.supportsTransactionIsolationLevel(Connection.TRANSACTION_READ_UNCOMMITTED) + "]", MODULE); | |||
log.debug("- isolation RepeatableRead [" + dbData.supportsTransactionIsolationLevel(Connection.TRANSACTION_REPEATABLE_READ) + "]", MODULE); | |||
log.debug("- isolation Serializable [" + dbData.supportsTransactionIsolationLevel(Connection.TRANSACTION_SERIALIZABLE) + "]", MODULE); | |||
log.debug("- is case sensitive [" + dbData.supportsMixedCaseIdentifiers() + "]", MODULE); | |||
log.debug("- stores LowerCase [" + dbData.storesLowerCaseIdentifiers() + "]", MODULE); | |||
log.debug("- stores MixedCase [" + dbData.storesMixedCaseIdentifiers() + "]", MODULE); | |||
log.debug("- stores UpperCase [" + dbData.storesUpperCaseIdentifiers() + "]", MODULE); | |||
log.debug("- max table name length [" + dbData.getMaxTableNameLength() + "]", MODULE); | |||
log.debug("- max column name length [" + dbData.getMaxColumnNameLength() + "]", MODULE); | |||
log.debug("- max schema name length [" + dbData.getMaxSchemaNameLength() + "]", MODULE); | |||
log.debug("- concurrent connections [" + dbData.getMaxConnections() + "]", MODULE); | |||
log.debug("- concurrent statements [" + dbData.getMaxStatements() + "]", MODULE); | |||
log.debug("- ANSI SQL92 Entry [" + dbData.supportsANSI92EntryLevelSQL() + "]", MODULE); | |||
log.debug("- ANSI SQL92 Itermediate [" + dbData.supportsANSI92IntermediateSQL() + "]", MODULE); | |||
log.debug("- ANSI SQL92 Full [" + dbData.supportsANSI92FullSQL() + "]", MODULE); | |||
log.debug("- ODBC SQL Grammar Core [" + dbData.supportsCoreSQLGrammar() + "]", MODULE); | |||
log.debug("- ODBC SQL Grammar Extended[" + dbData.supportsExtendedSQLGrammar() + "]", MODULE); | |||
log.debug("- ODBC SQL Grammar Minimum [" + dbData.supportsMinimumSQLGrammar() + "]", MODULE); | |||
log.debug("- outer joins [" + dbData.supportsOuterJoins() + "]*", MODULE); | |||
log.debug("- limited outer joins [" + dbData.supportsLimitedOuterJoins() + "]", MODULE); | |||
log.debug("- full outer joins [" + dbData.supportsFullOuterJoins() + "]", MODULE); | |||
log.debug("- group by [" + dbData.supportsGroupBy() + "]*", MODULE); | |||
log.debug("- group by not in select [" + dbData.supportsGroupByUnrelated() + "]", MODULE); | |||
log.debug("- column aliasing [" + dbData.supportsColumnAliasing() + "]", MODULE); | |||
log.debug("- order by not in select [" + dbData.supportsOrderByUnrelated() + "]", MODULE); | |||
// this doesn't work in HSQLDB, other databases? Debug.logInfo("- named parameters [" + dbData.supportsNamedParameters() + "]", MODULE); | |||
log.debug("- alter table add column [" + dbData.supportsAlterTableWithAddColumn() + "]*", MODULE); | |||
log.debug("- non-nullable column [" + dbData.supportsNonNullableColumns() + "]*", MODULE); | |||
} catch (Exception e) { | |||
log.error("Unable to get misc. support/setting information", e); | |||
} | |||
} catch (SQLException e) { | |||
log.error(e.getMessage(), e); | |||
} | |||
return null; | |||
}); | |||
} | |||
//需要考虑分库的情况 | |||
@SuppressWarnings("unchecked") | |||
public Map<String, TableCheckInfo> getTables() { | |||
final Map<String, TableCheckInfo> tables = new TreeMap<>(); | |||
dbEngine.doConn(new Function<Connection, Object>() { | |||
@Override | |||
public Object apply(Connection connection) { | |||
try { | |||
DatabaseMetaData dbData = connection.getMetaData(); | |||
if (dbData == null) { | |||
return null; | |||
} | |||
System.out.println("获取数据库表信息..............................."); | |||
boolean needsUpperCase = false; | |||
try { | |||
needsUpperCase = dbData.storesLowerCaseIdentifiers() || dbData.storesMixedCaseIdentifiers(); | |||
} catch (SQLException sqle) { | |||
String message = "Error getting identifier case information... Error was:" + sqle.toString(); | |||
log.error(message, MODULE); | |||
} | |||
String lookupSchemaName = null; | |||
if (dbData.supportsSchemasInTableDefinitions()) { | |||
lookupSchemaName = dbData.getUserName(); | |||
} | |||
if (dbEngine.isMysql()) {//非oracle才需要 | |||
String dbName = dbEngine.getDbSchema(); | |||
/*Set<String> list = .getInstance().getModelReader().getListSchema(); | |||
if (list != null && !list.isEmpty()) { | |||
for (String catalog : list) { | |||
getTableSet(dbData, dbName + "_" + catalog, needsUpperCase, lookupSchemaName); | |||
} | |||
}*/ | |||
//当前库都要去读 | |||
getTableSet(dbData, dbName, needsUpperCase, lookupSchemaName); | |||
} else { | |||
//当前库都要去读 | |||
getTableSet(dbData, null, needsUpperCase, lookupSchemaName); | |||
} | |||
} catch (SQLException e) { | |||
log.error(e.getMessage(), e); | |||
} | |||
return null; | |||
} | |||
private void getTableSet(DatabaseMetaData dbData, String catalog, boolean needsUpperCase, String lookupSchemaName) { | |||
ResultSet tableSet = null; | |||
try { | |||
String[] types = {"TABLE"/*,"VIEW", "ALIAS", "SYNONYM"*/}; | |||
tableSet = dbData.getTables(catalog, lookupSchemaName, null, types); | |||
while (tableSet.next()) { | |||
String tableName = tableSet.getString("TABLE_NAME"); | |||
// for those databases which do not return the schema name with the table name (pgsql 7.3) | |||
//boolean appendSchemaName = false; | |||
if (needsUpperCase && tableName != null) { | |||
tableName = tableName.toUpperCase(); | |||
} | |||
//if (appendSchemaName) { | |||
// tableName = String.format("%s.%s", lookupSchemaName, tableName); | |||
//} | |||
String tableType = tableSet.getString("TABLE_TYPE"); | |||
// only allow certain table types | |||
if (tableType != null && | |||
!"TABLE".equalsIgnoreCase(tableType) && | |||
!"BASE TABLE".equalsIgnoreCase(tableType) && | |||
!"VIEW".equalsIgnoreCase(tableType) && | |||
!"ALIAS".equalsIgnoreCase(tableType) && | |||
!"SYNONYM".equalsIgnoreCase(tableType)) { | |||
continue; | |||
} | |||
try (ResultSet pkSet = dbData.getPrimaryKeys(catalog, lookupSchemaName, tableName)) { | |||
String s = ""; | |||
while (pkSet.next()) { | |||
s += "," + pkSet.getString(4); | |||
} | |||
TableCheckInfo table = new TableCheckInfo(); | |||
table.tableName = tableName; | |||
table.catalog = catalog; | |||
if (StringUtils.isNotEmpty(s)) { | |||
table.pk = s.substring(1); | |||
} | |||
tables.put(tableName, table); | |||
} | |||
} | |||
} catch (SQLException sqle) { | |||
String message = "Error getting next table information... Error was:" + sqle.toString(); | |||
log.error(message, MODULE); | |||
} finally { | |||
JdbcUtils.closeResultSet(tableSet); | |||
} | |||
} | |||
}); | |||
return tables; | |||
} | |||
@SuppressWarnings("unchecked") | |||
//获取列信息 | |||
public Map<String, List<ColumnCheckInfo>> getColumnInfo(final Map<String, TableCheckInfo> dbTables) { | |||
// if there are no tableNames, don't even try to get the columns | |||
final Map<String, List<ColumnCheckInfo>> colInfo = new HashMap<>(); | |||
if (dbTables.size() == 0) { | |||
return colInfo; | |||
} | |||
dbEngine.doConn(new Function<Connection, Object>() { | |||
@Override | |||
public Object apply(Connection connection) { | |||
try { | |||
DatabaseMetaData dbData = connection.getMetaData(); | |||
boolean needsUpperCase = false; | |||
try { | |||
needsUpperCase = dbData.storesLowerCaseIdentifiers() || dbData.storesMixedCaseIdentifiers(); | |||
} catch (SQLException sqle) { | |||
String message = "Error getting identifier case information... Error was:" + sqle.toString(); | |||
log.error(message, MODULE); | |||
} | |||
String lookupSchemaName = null; | |||
if (dbData.supportsSchemasInTableDefinitions()) { | |||
lookupSchemaName = dbData.getUserName(); | |||
} | |||
if (dbEngine.isMysql()) {//非oracle才需要 | |||
String dbName = dbEngine.getDbSchema(); | |||
/*Set<String> list = ModelConfigUtil.getInstance().getModelReader().getListSchema(); | |||
if (list != null && !list.isEmpty()) { | |||
for (String catalog : list) { | |||
getColumnInfo(dbData, dbName + "_" + catalog, needsUpperCase, lookupSchemaName); | |||
} | |||
}*/ | |||
} | |||
getColumnInfo(dbData, null, needsUpperCase, lookupSchemaName); | |||
} catch (SQLException e) { | |||
log.error(e.getMessage(), e); | |||
} | |||
return null; | |||
} | |||
private void getColumnInfo(DatabaseMetaData dbData, String catalog, boolean needsUpperCase, String lookupSchemaName) { | |||
ResultSet rsCols = null; | |||
try { | |||
rsCols = dbData.getColumns(catalog, lookupSchemaName, null, null); | |||
while (rsCols.next()) { | |||
ColumnCheckInfo ccInfo = new ColumnCheckInfo(); | |||
ccInfo.tableName = rsCols.getString("TABLE_NAME"); | |||
// for those databases which do not return the schema name with the table name (pgsql 7.3) | |||
//boolean appendSchemaName = false; | |||
// AKzz : 不在表名前加用户名. | |||
//if (ccInfo.tableName != null && lookupSchemaName != null && !ccInfo.tableName.startsWith(lookupSchemaName)) { | |||
// appendSchemaName = true; | |||
//} | |||
if (needsUpperCase && ccInfo.tableName != null) { | |||
ccInfo.tableName = ccInfo.tableName.toUpperCase(); | |||
} | |||
//if (appendSchemaName) { | |||
// ccInfo.tableName = lookupSchemaName + "." + ccInfo.tableName; | |||
//} | |||
// ignore the column info if the table name is not in the list we are concerned with | |||
if (!dbTables.containsKey(ccInfo.tableName)) { | |||
continue; | |||
} | |||
ccInfo.columnName = rsCols.getString("COLUMN_NAME"); | |||
if (needsUpperCase && ccInfo.columnName != null) { | |||
ccInfo.columnName = ccInfo.columnName.toUpperCase(); | |||
} | |||
// NOTE: this may need a toUpperCase in some cases, keep an eye on it | |||
ccInfo.typeName = rsCols.getString("TYPE_NAME"); | |||
ccInfo.columnSize = rsCols.getInt("COLUMN_SIZE"); | |||
ccInfo.decimalDigits = rsCols.getInt("DECIMAL_DIGITS"); | |||
// NOTE: this may need a toUpperCase in some cases, keep an eye on it | |||
ccInfo.isNullable = rsCols.getString("IS_NULLABLE"); | |||
List<ColumnCheckInfo> tableColInfo = colInfo.get(ccInfo.tableName); | |||
if (tableColInfo == null) { | |||
tableColInfo = new ArrayList<>(); | |||
colInfo.put(ccInfo.tableName, tableColInfo); | |||
} | |||
tableColInfo.add(ccInfo); | |||
} | |||
} catch (SQLException sqle) { | |||
String message = "Error getting table fields information... Error was:" + sqle.toString(); | |||
log.error(message, MODULE); | |||
} finally { | |||
JdbcUtils.closeResultSet(rsCols); | |||
} | |||
} | |||
}); | |||
return colInfo; | |||
} | |||
@SuppressWarnings("unchecked") | |||
//获取索引 | |||
public Map<String, Map<String, Set<String>>> getIndexInfo(final Collection<TableCheckInfo> tableNames, final boolean include_nounique) { | |||
final Map<String, Map<String, Set<String>>> indexInfo = new HashMap<>(); | |||
if (!tableNames.isEmpty()) { | |||
for (TableCheckInfo c : tableNames) { | |||
indexInfo.put(c.tableName, new HashMap<>()); | |||
} | |||
dbEngine.doConn(connection -> { | |||
try { | |||
DatabaseMetaData dbData = connection.getMetaData(); | |||
boolean needsUpperCase = false; | |||
try { | |||
needsUpperCase = dbData.storesLowerCaseIdentifiers() || dbData.storesMixedCaseIdentifiers(); | |||
} catch (SQLException sqle) { | |||
String message = "Error getting identifier case information... Error was:" + sqle.toString(); | |||
log.error(message, MODULE); | |||
} | |||
// int totalIndices = 0; | |||
String lookupSchemaName = null; | |||
if (dbData.supportsSchemasInTableDefinitions()) { | |||
lookupSchemaName = dbData.getUserName(); | |||
} | |||
for (TableCheckInfo curTableName : tableNames) { | |||
ResultSet rsCols = null; | |||
try { | |||
// false for unique, we don't really use unique indexes | |||
// true for approximate, don't really care if stats are up-to-date | |||
rsCols = dbData.getIndexInfo(curTableName.catalog, lookupSchemaName, curTableName.tableName, !include_nounique, true); | |||
while (rsCols != null && rsCols.next()) { | |||
// NOTE: The code in this block may look funny, but it is designed so that the wrapping loop can be removed | |||
// skip all index info for statistics | |||
if (rsCols.getShort("TYPE") == DatabaseMetaData.tableIndexStatistic) continue; | |||
//HACK: for now skip all "unique" indexes since our foreign key indices are not unique, but the primary key ones are | |||
//原来的方法: 只取了NON_UNIQUE == true的索引. | |||
//if (!rsCols.getBoolean ("NON_UNIQUE")) continue; | |||
boolean unique = rsCols.getBoolean("NON_UNIQUE"); | |||
//for (int x = 1; x<=rsCols.getMetaData().getColumnCount();x++){ | |||
// System.out.println(rsCols.getMetaData().getColumnName(x) + " = " + rsCols.getString(x)); | |||
//} | |||
//System.out.println("INDEX_NAME=" + rsCols.getString("INDEX_NAME") + " --- "+ "COLUMN_NAME=" + rsCols.getString("COLUMN_NAME") ); | |||
//改动, 可以取所有的索引. | |||
if (include_nounique) { | |||
// 要取nouniqune的 | |||
} else { | |||
// 不取nouniqune的, | |||
if (!unique) continue; | |||
} | |||
String tableName = rsCols.getString("TABLE_NAME"); | |||
if (needsUpperCase && tableName != null) { | |||
tableName = tableName.toUpperCase(); | |||
} | |||
String indexName = rsCols.getString("INDEX_NAME"); | |||
if (needsUpperCase && indexName != null) { | |||
indexName = indexName.toUpperCase(); | |||
} | |||
Map<String, Set<String>> tableIndexList = indexInfo.computeIfAbsent(tableName, k -> new HashMap<>()); | |||
Set<String> set = tableIndexList.computeIfAbsent(indexName, k -> new LinkedHashSet<>()); | |||
// totalIndices++; | |||
set.add(rsCols.getString("COLUMN_NAME")); | |||
} | |||
} catch (Exception e) { | |||
log.debug("Error getting index info using lookupSchemaName " + lookupSchemaName, e); | |||
} finally { | |||
JdbcUtils.closeResultSet(rsCols); | |||
} | |||
} | |||
} catch (SQLException e) { | |||
log.error(e.getMessage(), e); | |||
} | |||
return null; | |||
}); | |||
} | |||
return indexInfo; | |||
} | |||
} |
@@ -0,0 +1,62 @@ | |||
package cc.smtweb.framework.core.db.impl; | |||
import cc.smtweb.framework.core.db.cache.ModelTableCache; | |||
import cc.smtweb.framework.core.db.vo.ModelField; | |||
import cc.smtweb.framework.core.db.vo.ModelTable; | |||
import org.apache.commons.lang3.StringUtils; | |||
import java.io.Serializable; | |||
/** | |||
* Created by Akmm at 14-1-2 上午11:19 | |||
* 实体bean抽象类 | |||
*/ | |||
public class DefaultEntity extends BaseBean implements Serializable, Cloneable { | |||
//表名 | |||
private String tableName; | |||
public DefaultEntity(String tableName) { | |||
this.tableName = tableName; | |||
} | |||
public ModelTable getModelTable() { | |||
return ModelTableCache.getInstance().getByName(tableName); | |||
} | |||
//根据实体定义,设默认值 | |||
public void init() { | |||
ModelTable entity = getModelTable(); | |||
if (entity == null) return; | |||
String pkField = getPkFieldName(); | |||
for (ModelField field : entity.getFields()) { | |||
if (data.containsKey(field.getName())) continue;//有值了,不能动 | |||
if (field.getName().equalsIgnoreCase(pkField)) continue;//是pk,不要初始化 | |||
String s = field.getDefaultValue(); | |||
if (StringUtils.isNotEmpty(s)) put(field.getName(), s); | |||
} | |||
} | |||
/** 主键字段 */ | |||
public String getPkFieldName() { | |||
return getModelTable().getIdField(); | |||
} | |||
public long getEntityId() { | |||
return getLong(getPkFieldName()); | |||
} | |||
public void setEntityId(long id) { | |||
data.put(getPkFieldName(), id); | |||
} | |||
public boolean isNew() { | |||
return getEntityId() <= 0L; | |||
} | |||
@Override | |||
public DefaultEntity clone() throws CloneNotSupportedException { | |||
DefaultEntity bean = (DefaultEntity) super.clone(); | |||
bean.getData().remove(getPkFieldName()); | |||
return bean; | |||
} | |||
} |
@@ -0,0 +1,37 @@ | |||
package cc.smtweb.framework.core.db.impl; | |||
import java.util.Collection; | |||
import java.util.List; | |||
import java.util.Map; | |||
import java.util.Set; | |||
/** | |||
* Created with IntelliJ IDEA. | |||
* User: AKhh | |||
* Date: 12-12-21 下午10:13 | |||
* To change this template use File | Settings | File Templates. | |||
*/ | |||
public interface IDatabaseInfo { | |||
void printDatabaseInfo(); | |||
Map<String, TableCheckInfo> getTables(); | |||
Map<String, List<ColumnCheckInfo>> getColumnInfo(Map<String, TableCheckInfo> dbTables); | |||
Map<String, Map<String, Set<String>>> getIndexInfo(Collection<TableCheckInfo> tableNames, boolean include_nounique); | |||
public static class TableCheckInfo { | |||
public String tableName; | |||
public String catalog; | |||
public String pk; | |||
} | |||
public static class ColumnCheckInfo { | |||
public String tableName; | |||
public String columnName; | |||
public String typeName; | |||
public int columnSize; | |||
public int decimalDigits; | |||
public String isNullable; // Y|N or "" = ie nobody knows | |||
} | |||
} |
@@ -0,0 +1,134 @@ | |||
package cc.smtweb.framework.core.db.jdbc; | |||
/** | |||
* 主键生成器。 | |||
*/ | |||
//public class IdGenerator { | |||
// private AtomicLong key; | |||
// | |||
// public IdGenerator() { | |||
// this(0); | |||
// } | |||
// | |||
// /** | |||
// * | |||
// * @param seed 集群服务id,用于集群产生的数据不重复(0~255) | |||
// */ | |||
// public IdGenerator(int seed) { | |||
// long now = System.currentTimeMillis(); | |||
// | |||
// if(seed > 0xFF){ | |||
// seed = 0xFF; | |||
// } | |||
// | |||
// if(seed <= 0){ | |||
// seed = 1; | |||
// } | |||
// | |||
// key = new AtomicLong(now | (((long) seed) << 48)); | |||
// } | |||
// | |||
// public long nextCode() { | |||
// return key.incrementAndGet(); | |||
// } | |||
//} | |||
/** | |||
* tweeter的snowflake | |||
* time—42bits,精确到ms,那就意味着其可以表示长达(2^42-1)/(1000360024*365)=139.5年 | |||
* (a) id构成: 42位的时间前缀 + 10位的节点标识 + 12位的sequence避免并发的数字(12位不够用时强制得到新的时间前缀) | |||
* 注意这里进行了小改动: snowkflake是5位的datacenter加5位的机器id; 这里变成使用10位的机器id | |||
* (b) 对系统时间的依赖性非常强,需关闭ntp的时间同步功能。当检测到ntp时间调整后,将会拒绝分配id | |||
* @author xkliu | |||
*/ | |||
public class IdGenerator { | |||
/** | |||
* 起始的时间戳 epoch 2017-1-1 | |||
*/ | |||
private final static long EPOCH_STMP = 1483200000000L; | |||
/** | |||
* 每一部分占用的位数 | |||
*/ | |||
//序列号占用的位数 | |||
private final static long SEQUENCE_BIT = 12; | |||
//机器标识占用的位数 | |||
private final static long MACHINE_BIT = 10; | |||
/** | |||
* 每一部分的最大值 | |||
*/ | |||
private final static long MAX_MACHINE_NUM = ~(-1L << MACHINE_BIT); | |||
private final static long MAX_SEQUENCE = ~(-1L << SEQUENCE_BIT); | |||
/** | |||
* 每一部分向左的位移 | |||
*/ | |||
private final static long MACHINE_LEFT = SEQUENCE_BIT; | |||
private final static long TIMESTMP_LEFT = SEQUENCE_BIT + MACHINE_BIT; | |||
// private long datacenterId; //数据中心 | |||
// 机器标识 | |||
private long machineIdShift; | |||
// 序列号 | |||
private long sequence = 0L; | |||
// 上一次时间戳 | |||
private long lastStmp = -1L; | |||
// 0 ~ 1023 | |||
public IdGenerator(long machineId) { | |||
if (machineId > MAX_MACHINE_NUM || machineId < 0) { | |||
throw new IllegalArgumentException("machineId " + machineId + " can't be greater than " + MAX_MACHINE_NUM + " or less than 0"); | |||
} | |||
this.machineIdShift = machineId << MACHINE_LEFT; | |||
} | |||
/** | |||
* 产生下一个ID | |||
* | |||
* @return ID值 | |||
*/ | |||
public synchronized long nextId() { | |||
long currStmp = getNewStamp(); | |||
if (currStmp < lastStmp) { | |||
throw new RuntimeException("Clock moved backwards. Refusing to generate id"); | |||
} | |||
if (currStmp == lastStmp) { | |||
//相同毫秒内,序列号自增 | |||
sequence = (sequence + 1) & MAX_SEQUENCE; | |||
//同一毫秒的序列数已经达到最大 | |||
if (sequence == 0L) { | |||
currStmp = getNextMill(); | |||
} | |||
} else { | |||
//不同毫秒内,序列号置为0 | |||
sequence = 0L; | |||
} | |||
lastStmp = currStmp; | |||
// 时间戳部分 + 机器标识部分 + 序列号部分 | |||
return (currStmp - EPOCH_STMP) << TIMESTMP_LEFT | |||
| machineIdShift | |||
| sequence; | |||
} | |||
private long getNextMill() { | |||
long mill = getNewStamp(); | |||
while (mill <= lastStmp) { | |||
mill = getNewStamp(); | |||
} | |||
return mill; | |||
} | |||
private long getNewStamp() { | |||
return System.currentTimeMillis(); | |||
} | |||
// 根据ID值反向计算时间戳 | |||
public static long queryTimestamp(long id) { | |||
return (id >>> TIMESTMP_LEFT) + EPOCH_STMP; | |||
} | |||
} |
@@ -0,0 +1,411 @@ | |||
package cc.smtweb.framework.core.db.jdbc; | |||
import cc.smtweb.framework.core.SwMap; | |||
import cc.smtweb.framework.core.exception.DbException; | |||
import cc.smtweb.framework.core.util.JsonUtil; | |||
import lombok.Getter; | |||
import lombok.Setter; | |||
import org.apache.commons.lang3.StringUtils; | |||
import org.springframework.jdbc.core.BeanPropertyRowMapper; | |||
import org.springframework.jdbc.core.JdbcTemplate; | |||
import org.springframework.jdbc.core.ResultSetExtractor; | |||
import org.springframework.jdbc.core.RowMapper; | |||
import org.springframework.jdbc.datasource.DataSourceTransactionManager; | |||
import javax.sql.DataSource; | |||
import java.sql.Connection; | |||
import java.sql.SQLException; | |||
import java.util.HashSet; | |||
import java.util.List; | |||
import java.util.Objects; | |||
import java.util.Set; | |||
import java.util.function.Function; | |||
import java.util.function.Supplier; | |||
/** | |||
* JDBC访问类,包装了spring jdbcTemplate对象 | |||
*/ | |||
public class JdbcEngine { | |||
private final static String DB_TYPE_MYSQL = "mysql"; | |||
private JdbcTemplate jdbcTemplate; | |||
private DataSourceTransactionManager dataSourceTransactionManager; | |||
private IdGenerator idGenerator; | |||
protected String type; | |||
public JdbcEngine(JdbcTemplate jdbcTemplate, IdGenerator idGenerator, String type) { | |||
this.jdbcTemplate = jdbcTemplate; | |||
this.dataSourceTransactionManager = new DataSourceTransactionManager(jdbcTemplate.getDataSource()); | |||
this.idGenerator = idGenerator; | |||
this.type = type; | |||
} | |||
public boolean isMysql() { | |||
return DB_TYPE_MYSQL.equalsIgnoreCase(type); | |||
} | |||
/** | |||
* 获取数据库唯一id | |||
* | |||
* @return 返回ID值 | |||
*/ | |||
public long nextId() { | |||
return this.idGenerator.nextId(); | |||
} | |||
/** | |||
* 查询单行数据 | |||
* | |||
* @param sql 查询SQL | |||
* @param rowHandler ResultSet处理器 | |||
* @param <T> 返回得对象类型 | |||
* @return 返回单个对象数据 | |||
*/ | |||
public <T> T queryEntity(String sql, final ResultSetExtractor<T> rowHandler) { | |||
return jdbcTemplate.query(sql, rowHandler); | |||
} | |||
/** | |||
* 查询单行数据 | |||
* | |||
* @param sql 查询SQL | |||
* @param rowHandler ResultSet处理器 | |||
* @param params SQL参数 | |||
* @param <T> 返回得对象类型 | |||
* @return 返回单个对象数据 | |||
*/ | |||
public <T> T queryEntity(String sql, final ResultSetExtractor<T> rowHandler, Object... params) { | |||
return jdbcTemplate.query(sql, rowHandler, params); | |||
} | |||
/** | |||
* 通过回调函数查询SQL,返回列表或者对象数据 | |||
*/ | |||
public <T> T query(String sql, ResultSetExtractor<T> rse, Object... params) { | |||
return jdbcTemplate.query(sql, rse, params); | |||
} | |||
/** | |||
* 查询SQL,返回列表数据 | |||
*/ | |||
public <T> List<T> query(String sql, RowMapper<T> rowMapper) { | |||
return jdbcTemplate.query(sql, rowMapper); | |||
} | |||
/** | |||
* 查询SQL,返回列表数据 | |||
*/ | |||
public <T> List<T> query(String sql, RowMapper<T> rowMapper, Object... params) { | |||
return jdbcTemplate.query(sql, rowMapper, params); | |||
} | |||
/** | |||
* 翻页查询 | |||
*/ | |||
public <T> List<T> pagedQuery(String sql, RowMapper<T> rowMapper, int start, int limit, Object... params) { | |||
return jdbcTemplate.query(sql + " LIMIT " + start + "," + limit, rowMapper, params); | |||
} | |||
/** | |||
* 执行更新SQL | |||
*/ | |||
public int update(String sql) { | |||
return jdbcTemplate.update(sql); | |||
} | |||
/** | |||
* 执行更新SQL | |||
*/ | |||
public int update(String sql, Object... params) { | |||
return jdbcTemplate.update(sql, params); | |||
} | |||
/** | |||
* 执行更新语句直到无更新数据或者达到最大修改记录数 | |||
* | |||
* @param sql 需要执行的SQL语句 | |||
* @param limit 每次更新的条数 | |||
* @param count 最大更新次数 | |||
* @param params 执行参数 | |||
* @return 返回更新的数据数量 | |||
*/ | |||
public int pagedUpdate(String sql, int limit, int count, Object... params) { | |||
int result = 0; | |||
for (int i = 0, ret = 1; i < count && ret > 0; i++) { | |||
ret = jdbcTemplate.update(sql + " LIMIT " + limit, params); | |||
if (ret > 0) { | |||
result += ret; | |||
} | |||
} | |||
return result; | |||
} | |||
/** | |||
* 查询字符list | |||
* | |||
* @param sql 查询SQL | |||
* @param params 查询SQL参数 | |||
* @return 字符list | |||
*/ | |||
public List<String> queryStringList(String sql, Object... params) { | |||
return jdbcTemplate.query(sql, (resultSet, i) -> resultSet.getString(1), params); | |||
} | |||
/** | |||
* 查询字符set | |||
* | |||
* @param sql 查询SQL | |||
* @param params 查询SQL参数 | |||
* @return 字符set | |||
*/ | |||
public Set<String> queryStringSet(String sql, Object... params) { | |||
return jdbcTemplate.query(sql, (resultSet) -> { | |||
Set<String> result = new HashSet<>(); | |||
while (resultSet.next()) { | |||
String value = resultSet.getString(1); | |||
if (value != null) { | |||
result.add(value); | |||
} | |||
} | |||
return result; | |||
}, params); | |||
} | |||
/** | |||
* 查询字符 | |||
* | |||
* @param sql 查询SQL | |||
* @param params 查询SQL参数 | |||
* @return 字符 | |||
*/ | |||
public String queryString(String sql, Object... params) { | |||
List<String> list = jdbcTemplate.query(sql, (resultSet, i) -> resultSet.getString(1), params); | |||
if (list != null && !list.isEmpty()) { | |||
return list.get(0); | |||
} | |||
return null; | |||
} | |||
/** | |||
* 查询值为JSON格式,转换为对象返回 | |||
* | |||
* @param sql 查询SQL | |||
* @param clazz 对象类 | |||
* @param params 查询SQL参数 | |||
* @param <T> 对象类型 | |||
* @return JSON格式,转换为对象返回 | |||
*/ | |||
public <T> T queryJson(String sql, Class<T> clazz, Object... params) { | |||
List<String> list = jdbcTemplate.query(sql, (resultSet, i) -> resultSet.getString(1), params); | |||
if (list != null && !list.isEmpty()) { | |||
String s = list.get(0); | |||
if (StringUtils.isNotBlank(s)) { | |||
return JsonUtil.parse(s, clazz); | |||
} else { | |||
try { | |||
return clazz.newInstance(); | |||
} catch (InstantiationException | IllegalAccessException e) { | |||
throw new DbException(e); | |||
} | |||
} | |||
} | |||
return null; | |||
} | |||
/** | |||
* 查询Long对象 | |||
* | |||
* @param sql 查询SQL | |||
* @param params 查询SQL参数 | |||
* @return Long对象 | |||
*/ | |||
public Long queryLong(String sql, Object... params) { | |||
List<Long> list = jdbcTemplate.query(sql, (resultSet, i) -> resultSet.getLong(1), params); | |||
if (list != null && !list.isEmpty()) { | |||
return list.get(0); | |||
} | |||
return null; | |||
} | |||
/** | |||
* 查询List<Long>对象 | |||
* | |||
* @param sql 查询SQL | |||
* @param params 查询SQL参数 | |||
* @return List<Long>对象 | |||
*/ | |||
public List<Long> queryLongList(String sql, Object... params) { | |||
return jdbcTemplate.query(sql, (resultSet, i) -> resultSet.getLong(1), params); | |||
} | |||
/** | |||
* 查询Set<Long>对象 | |||
* | |||
* @param sql 查询SQL | |||
* @param params 查询SQL参数 | |||
* @return Set<Long>对象 | |||
*/ | |||
public Set<Long> queryLongSet(String sql, Object... params) { | |||
return jdbcTemplate.query(sql, (resultSet) -> { | |||
Set<Long> result = new HashSet<>(); | |||
while (resultSet.next()) { | |||
long value = resultSet.getLong(1); | |||
if (!resultSet.wasNull()) { | |||
result.add(value); | |||
} | |||
} | |||
return result; | |||
}, params); | |||
} | |||
/** | |||
* 查询Integer对象 | |||
* | |||
* @param sql 查询SQL | |||
* @param params 查询SQL参数 | |||
* @return Integer对象 | |||
*/ | |||
public Integer queryInt(String sql, Object... params) { | |||
List<Integer> list = jdbcTemplate.query(sql, (resultSet, i) -> resultSet.getInt(1), params); | |||
if (list != null && !list.isEmpty()) { | |||
return list.get(0); | |||
} | |||
return null; | |||
} | |||
/** | |||
* 执行批量更新语句 | |||
* | |||
* @param sql SQL语句 | |||
* @param params 数组参数列表 | |||
* @return 执行结果数组 | |||
*/ | |||
public int[] batchUpdate(String sql, List<Object[]> params) { | |||
return jdbcTemplate.batchUpdate(sql, params); | |||
} | |||
/** | |||
* 启动事务,需要用try(DbTrans dbTrans=dbEngine.openTrans())来处理异常后自动回滚事务,如果手工提交事务DbTaans.commit() | |||
* | |||
* @return 事务对象 | |||
*/ | |||
public JdbcTrans openTrans() { | |||
return new JdbcTrans(this.dataSourceTransactionManager); | |||
} | |||
/** | |||
* 在回调函数中执行事务,返回的true就提交事务,否则回滚事务 | |||
* | |||
* @param extractor 回调方法 | |||
* @return 事务是否成功,如果异常会也会事务回滚后再抛出异常 | |||
*/ | |||
public boolean doTrans(Supplier<Boolean> extractor) { | |||
try (JdbcTrans jdbcTrans = openTrans()) { | |||
boolean result = extractor.get(); | |||
if (result) { | |||
jdbcTrans.commit(); | |||
return true; | |||
} | |||
} | |||
return false; | |||
} | |||
/** | |||
* 获取原始数据库连接执行命令 | |||
* | |||
* @return 数据库连接 | |||
*/ | |||
public <T> T doConn(Function<Connection, T> extractor) { | |||
DataSource ds = Objects.requireNonNull(this.jdbcTemplate.getDataSource()); | |||
try (Connection conn = ds.getConnection()) { | |||
return extractor.apply(conn); | |||
} catch (SQLException e) { | |||
throw new DbException(e); | |||
} | |||
} | |||
/** | |||
* 查询单行数据,返回bean | |||
*/ | |||
public <T> T queryEntity(String sql, Class<T> type) { | |||
List<T> list = jdbcTemplate.query(sql, createRowMapper(type)); | |||
if (list != null && !list.isEmpty()) { | |||
return list.get(0); | |||
} | |||
return null; | |||
} | |||
/** | |||
* 查询单行数据,返回bean | |||
*/ | |||
public <T> T queryEntity(String sql, Class<T> type, Object... params) { | |||
List<T> list = query(sql, createRowMapper(type), params); | |||
if (list != null && !list.isEmpty()) { | |||
return list.get(0); | |||
} | |||
return null; | |||
} | |||
/** | |||
* 通过回调函数查询SQL,返回列表或者对象数据 | |||
*/ | |||
public <T> T query(String sql, ResultSetExtractor<T> rse) { | |||
return jdbcTemplate.query(sql, rse); | |||
} | |||
/** | |||
* 查询SQL,返回列表数据 | |||
*/ | |||
public <T> List<T> query(String sql, Class<T> type) { | |||
return jdbcTemplate.query(sql, createRowMapper(type)); | |||
} | |||
/** | |||
* 查询SQL,返回列表数据 | |||
*/ | |||
public <T> List<T> query(String sql, Class<T> type, Object... params) { | |||
return jdbcTemplate.query(sql, createRowMapper(type), params); | |||
} | |||
/** | |||
* 翻页查询 | |||
*/ | |||
public <T> List<T> pagedQuery(String sql, Class<T> type, int start, int limit) { | |||
return jdbcTemplate.query(sql + " LIMIT " + start + "," + limit, createRowMapper(type)); | |||
} | |||
private <T> RowMapper<T> createRowMapper(Class<T> type) { | |||
RowMapper<T> rowMapper; | |||
if (java.util.Map.class.isAssignableFrom(type)) { | |||
if (SwMap.class.equals(type)) { | |||
rowMapper = new SwMapPropertyRowMapper<>(type); | |||
} else { | |||
rowMapper = new MapPropertyRowMapper<>(type); | |||
} | |||
} else { | |||
rowMapper = new BeanPropertyRowMapper<>(type); | |||
} | |||
return rowMapper; | |||
} | |||
/** | |||
* 翻页查询 | |||
*/ | |||
public <T> List<T> pagedQuery(String sql, Class<T> type, int start, int limit, Object... params) { | |||
return jdbcTemplate.query(sql + " LIMIT " + start + "," + limit, createRowMapper(type), params); | |||
} | |||
} |
@@ -0,0 +1,56 @@ | |||
package cc.smtweb.framework.core.db.jdbc; | |||
import org.springframework.jdbc.datasource.DataSourceTransactionManager; | |||
import org.springframework.transaction.TransactionDefinition; | |||
import org.springframework.transaction.TransactionStatus; | |||
import org.springframework.transaction.support.DefaultTransactionDefinition; | |||
/** | |||
* JDBC事务处理类,可以在try中使用自动结束事务 | |||
* @author xkliu | |||
*/ | |||
public class JdbcTrans implements AutoCloseable { | |||
private DataSourceTransactionManager transactionManager; | |||
private TransactionStatus status; | |||
/** | |||
* 构造事务执行 | |||
* @param transactionManager spring事务管理对象 | |||
*/ | |||
public JdbcTrans(DataSourceTransactionManager transactionManager) { | |||
this.transactionManager = transactionManager; | |||
// 事务定义类 | |||
DefaultTransactionDefinition def = new DefaultTransactionDefinition(); | |||
def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED); | |||
// 返回事务对象 | |||
this.status = transactionManager.getTransaction(def); | |||
} | |||
/** | |||
* 回滚事务 | |||
*/ | |||
public void rollback() { | |||
if (status != null) { | |||
transactionManager.rollback(status); | |||
status = null; | |||
} | |||
} | |||
/** | |||
* 提交事务 | |||
*/ | |||
public void commit() { | |||
if (status != null) { | |||
transactionManager.commit(status); | |||
status = null; | |||
} | |||
} | |||
/** | |||
* 实现自动关闭,回滚方式结束事务 | |||
*/ | |||
@Override | |||
public void close() { | |||
this.rollback(); | |||
} | |||
} |
@@ -0,0 +1,39 @@ | |||
package cc.smtweb.framework.core.db.jdbc; | |||
import org.springframework.beans.BeanUtils; | |||
import org.springframework.jdbc.core.RowMapper; | |||
import java.sql.ResultSet; | |||
import java.sql.ResultSetMetaData; | |||
import java.sql.SQLException; | |||
/** | |||
* ORM映射处理器,实现spring jdbcTemplate的行集映射器 | |||
* @author xkliu | |||
*/ | |||
public class MapPropertyRowMapper<T> implements RowMapper<T> { | |||
private Class<T> mappedClass; | |||
public MapPropertyRowMapper(Class<T> mappedClass) { | |||
this.mappedClass = mappedClass; | |||
} | |||
@Override | |||
public T mapRow(ResultSet resultSet, int i) throws SQLException { | |||
T mappedObject = BeanUtils.instantiateClass(this.mappedClass); | |||
java.util.Map map = (java.util.Map)mappedObject; | |||
ResultSetMetaData rsmd = resultSet.getMetaData(); | |||
int columnCount = rsmd.getColumnCount(); | |||
for(int index = 1; index <= columnCount; ++index) { | |||
Object value = resultSet.getObject(index); | |||
if (value != null) { | |||
map.put(rsmd.getColumnLabel(index), value); | |||
} | |||
} | |||
return mappedObject; | |||
} | |||
} |
@@ -0,0 +1,59 @@ | |||
package cc.smtweb.framework.core.db.jdbc; | |||
import cc.smtweb.framework.core.SwMap; | |||
import org.springframework.jdbc.core.RowMapper; | |||
import java.sql.ResultSet; | |||
import java.sql.ResultSetMetaData; | |||
import java.sql.SQLException; | |||
/** | |||
* ORM映射处理器,实现spring jdbcTemplate的行集映射器,对下划线进行小驼峰命名转化 | |||
* @author xkliu | |||
*/ | |||
public class SwMapPropertyRowMapper<T> implements RowMapper<T> { | |||
public SwMapPropertyRowMapper(Class<T> mappedClass) { | |||
} | |||
@Override | |||
public T mapRow(ResultSet resultSet, int i) throws SQLException { | |||
SwMap map = new SwMap(); | |||
ResultSetMetaData rsmd = resultSet.getMetaData(); | |||
int columnCount = rsmd.getColumnCount(); | |||
for(int index = 1; index <= columnCount; ++index) { | |||
Object value = resultSet.getObject(index); | |||
if (value != null) { | |||
String columnLabel = rsmd.getColumnLabel(index); | |||
map.put(toCamelCase(columnLabel), value); | |||
} | |||
} | |||
return (T)map; | |||
} | |||
private String toCamelCase(String columnLabel) { | |||
int len = columnLabel.length(); | |||
StringBuilder sb = new StringBuilder(len); | |||
int lowCase = 1; | |||
for (int i = 0; i < len; i++) { | |||
char ch = columnLabel.charAt(i); | |||
if (ch == '_') { | |||
lowCase = 2; | |||
} else { | |||
if (lowCase == 1) { | |||
ch = Character.toLowerCase(ch); | |||
lowCase = 0; | |||
} else if (lowCase == 2) { | |||
ch = Character.toUpperCase(ch); | |||
lowCase = 0; | |||
} | |||
sb.append(ch); | |||
} | |||
} | |||
return sb.toString(); | |||
} | |||
} |
@@ -0,0 +1,129 @@ | |||
package cc.smtweb.framework.core.db.sqlbuilder; | |||
import cc.smtweb.framework.core.db.DbEngine; | |||
import cc.smtweb.framework.core.db.jdbc.JdbcEngine; | |||
import java.util.ArrayList; | |||
import java.util.List; | |||
import java.util.function.BiFunction; | |||
public class AbstractSelectSqlBuilder extends SqlBuilder<AbstractSelectSqlBuilder> { | |||
private List<String> orderBy; | |||
protected void makeFields(StringBuilder sb) { | |||
} | |||
protected Object[] makeParams(StringBuilder sb) { | |||
makeFields(sb); | |||
if (wheres != null) { | |||
List<Object> params = new ArrayList<>(wheres.size()); | |||
if (wheres.size() > 0) { | |||
String logicOp = " AND "; | |||
boolean addLoginOp = false; | |||
for (int i = 0; i < wheres.size(); i++) { | |||
SqlWhereValue whereValue = wheres.get(i); | |||
if (i == 0) { | |||
sb.append(" WHERE "); | |||
} | |||
if (whereValue.getName() == null) { | |||
Object value = whereValue.getValue(); | |||
if ("(".equals(value)) { | |||
if (i > 0) { | |||
sb.append(logicOp); | |||
} | |||
addLoginOp = false; | |||
} | |||
sb.append(value); | |||
logicOp = whereValue.getOp(); | |||
} else { | |||
if (addLoginOp) { | |||
sb.append(logicOp); | |||
} else { | |||
addLoginOp = true; | |||
} | |||
sb.append(whereValue.getName()).append(whereValue.getOp()).append('?'); | |||
params.add(whereValue.getValue()); | |||
} | |||
} | |||
} | |||
if (orderBy != null) { | |||
for (int i = 0; i < orderBy.size(); i++) { | |||
if (i == 0) { | |||
sb.append(" ORDER BY "); | |||
} else { | |||
sb.append(","); | |||
} | |||
sb.append(orderBy.get(i)); | |||
} | |||
} | |||
return params.toArray(new Object[params.size()]); | |||
} | |||
return null; | |||
} | |||
@Override | |||
public AbstractSelectSqlBuilder addOrderBy(String orderByField) { | |||
if (this.orderBy == null) { | |||
this.orderBy = new ArrayList<>(); | |||
} | |||
this.orderBy.add(orderByField); | |||
return this; | |||
} | |||
public <T> List<T> query(DbEngine dbEngine, Class<T> clazz) { | |||
return exec((sql, params) -> dbEngine.query(sql, clazz, params)); | |||
} | |||
public <T> T queryEntity(DbEngine dbEngine, Class<T> clazz) { | |||
return exec((sql, params) -> dbEngine.queryEntity(sql, clazz, params)); | |||
// StringBuilder sb = new StringBuilder("select "); | |||
// | |||
// Object[] params = makeParams(sb); | |||
// | |||
// return dbEngine.queryEntity(sb.toString(), clazz, params); | |||
} | |||
public <T> T exec(BiFunction<String, Object[], T> execute) { | |||
StringBuilder sb = new StringBuilder("select "); | |||
Object[] params = makeParams(sb); | |||
return execute.apply(sb.toString(), params); | |||
} | |||
public <T> List<T> pagedQuery(DbEngine dbEngine, Class<T> clazz, int start, int limit) { | |||
return exec((sql, params) -> dbEngine.pagedQuery(sql, clazz, start, limit, params)); | |||
// StringBuilder sb = new StringBuilder("select "); | |||
// | |||
// Object[] params = makeParams(sb); | |||
// | |||
// return dbEngine.pagedQuery(sb.toString(), clazz, start, limit, params); | |||
} | |||
public int queryInt(JdbcEngine dbEngine) { | |||
return exec(dbEngine::queryInt); | |||
} | |||
public SqlJoinTable addJoinTable(String dbName, String tableName, String tableAlias) { | |||
return null; | |||
} | |||
public SqlJoinTable findJoinTable(String dbName, String tableName) { | |||
return null; | |||
} | |||
} |
@@ -0,0 +1,20 @@ | |||
package cc.smtweb.framework.core.db.sqlbuilder; | |||
import cc.smtweb.framework.core.db.jdbc.JdbcEngine; | |||
public abstract class AbstractUpdateSqlBuilder<T extends AbstractUpdateSqlBuilder> extends SqlBuilder<T> { | |||
// 无效的更新值,用以占位表示不组装值到params对象里面 | |||
public static final Object VALUE_INVALID = new SqlFieldValue("", ""); | |||
public boolean isEmpty() { | |||
return fields.isEmpty(); | |||
} | |||
public abstract int update(JdbcEngine dbEngine); | |||
public void updateMap(java.util.Map<String, Object> map) { | |||
for (SqlFieldValue field: fields) { | |||
map.put(field.getName(), field.getValue()); | |||
} | |||
} | |||
} |
@@ -0,0 +1,33 @@ | |||
package cc.smtweb.framework.core.db.sqlbuilder; | |||
import cc.smtweb.framework.core.db.jdbc.JdbcEngine; | |||
public class DeleteSqlBuilder extends AbstractUpdateSqlBuilder<DeleteSqlBuilder> { | |||
private String tableName; | |||
DeleteSqlBuilder(String tableName) { | |||
this.tableName = tableName; | |||
} | |||
@Override | |||
public int update(JdbcEngine dbEngine) { | |||
int fieldSize = wheres.size(); | |||
Object[] params = new Object[fieldSize]; | |||
StringBuilder sb = new StringBuilder("delete from "); | |||
sb.append(tableName).append(" where "); | |||
int index = 0; | |||
for (SqlWhereValue whereValue: wheres) { | |||
if (index > 0){ | |||
sb.append(" and "); | |||
} | |||
sb.append(whereValue.getName()).append("=?"); | |||
params[index] = whereValue.getValue(); | |||
index++; | |||
} | |||
return dbEngine.update(sb.toString(), params); | |||
} | |||
} |
@@ -0,0 +1,20 @@ | |||
package cc.smtweb.framework.core.db.sqlbuilder; | |||
import java.util.function.BiFunction; | |||
public class DirectSelectSqlBuilder extends AbstractSelectSqlBuilder { | |||
private String sql; | |||
DirectSelectSqlBuilder(String sql) { | |||
this.sql = sql; | |||
} | |||
@Override | |||
public <T> T exec(BiFunction<String, Object[], T> execute) { | |||
StringBuilder sb = new StringBuilder(sql); | |||
Object[] params = makeParams(sb); | |||
return execute.apply(sb.toString(), params); | |||
} | |||
} |
@@ -0,0 +1,38 @@ | |||
package cc.smtweb.framework.core.db.sqlbuilder; | |||
import cc.smtweb.framework.core.db.jdbc.JdbcEngine; | |||
public class InsertSqlBuilder extends AbstractUpdateSqlBuilder<InsertSqlBuilder> { | |||
private String tableName; | |||
InsertSqlBuilder(String tableName) { | |||
this.tableName = tableName; | |||
} | |||
@Override | |||
public int update(JdbcEngine dbEngine) { | |||
int fieldSize = fields.size(); | |||
Object[] params = new Object[fieldSize]; | |||
StringBuilder sb = new StringBuilder("insert into "); | |||
sb.append(tableName).append('('); | |||
for (int i = 0; i < fieldSize; i++) { | |||
SqlFieldValue field = fields.get(i); | |||
sb.append(field.getName()).append(','); | |||
params[i] = field.getValue(); | |||
} | |||
sb.setCharAt(sb.length() - 1, ')'); | |||
sb.append(" values("); | |||
for (int i = 0; i < fieldSize; i++) { | |||
sb.append("?,"); | |||
} | |||
sb.setCharAt(sb.length() - 1, ')'); | |||
return dbEngine.update(sb.toString(), params); | |||
} | |||
} |
@@ -0,0 +1,98 @@ | |||
package cc.smtweb.framework.core.db.sqlbuilder; | |||
import cc.smtweb.framework.core.db.DbEngine; | |||
import cc.smtweb.framework.core.db.jdbc.JdbcEngine; | |||
import java.util.ArrayList; | |||
import java.util.List; | |||
import java.util.function.BiFunction; | |||
public class SelectSqlBuilder extends AbstractSelectSqlBuilder { | |||
private String tableName; | |||
private List<SqlJoinTable> joinTables; | |||
SelectSqlBuilder(String tableName) { | |||
this.tableName = tableName; | |||
} | |||
// @Override | |||
// public <T> List<T> query(DbEngine dbEngine, Class<T> clazz) { | |||
// StringBuilder sb = new StringBuilder("select "); | |||
// | |||
// Object[] params = makeParams(sb); | |||
// | |||
// if (params != null) { | |||
// return dbEngine.query(sb.toString(), clazz, params); | |||
// } else { | |||
// return dbEngine.query(sb.toString(), clazz); | |||
// } | |||
// } | |||
@Override | |||
protected void makeFields(StringBuilder sb) { | |||
for (SqlFieldValue field: fields) { | |||
sb.append(field.getName()).append(","); | |||
} | |||
sb.setCharAt(sb.length() - 1, ' '); | |||
sb.append("FROM ").append(tableName); | |||
if (joinTables != null) { | |||
for (SqlJoinTable joinTable: joinTables) { | |||
sb.append(joinTable.joinSql()).append(joinTable.getDbName()).append('.').append(joinTable.getTableName()) | |||
.append(' ').append(joinTable.getTableAlias()).append(" ON("); | |||
boolean first = true; | |||
for (SqlJoinField joinField: joinTable.getFields()) { | |||
if (first) { | |||
first = false; | |||
} else { | |||
sb.append(" AND "); | |||
} | |||
sb.append(joinField.getKeyField()).append("=").append(joinField.getValueField()); | |||
} | |||
sb.append(')'); | |||
} | |||
} | |||
} | |||
@Override | |||
public <T> T exec(BiFunction<String, Object[], T> execute) { | |||
StringBuilder sb = new StringBuilder("select "); | |||
Object[] params = makeParams(sb); | |||
return execute.apply(sb.toString(), params); | |||
} | |||
@Override | |||
public SqlJoinTable addJoinTable(String dbName, String tableName, String tableAlias) { | |||
if (joinTables == null) { | |||
joinTables = new ArrayList<>(); | |||
} | |||
SqlJoinTable joinTable = new SqlJoinTable(); | |||
joinTable.setDbName(dbName); | |||
joinTable.setTableName(tableName); | |||
joinTable.setTableAlias(tableAlias); | |||
joinTables.add(joinTable); | |||
return joinTable; | |||
} | |||
@Override | |||
public SqlJoinTable findJoinTable(String dbName, String tableName) { | |||
if (joinTables != null) { | |||
for (SqlJoinTable table: joinTables) { | |||
if (dbName.equals(table.getDbName()) && tableName.equals(table.getTableName())) { | |||
return table; | |||
} | |||
} | |||
} | |||
return null; | |||
} | |||
} |
@@ -0,0 +1,84 @@ | |||
package cc.smtweb.framework.core.db.sqlbuilder; | |||
import java.util.ArrayList; | |||
import java.util.List; | |||
/** | |||
* SQL语句建造器 | |||
* @author admin | |||
* @param <T> 建造器类型 | |||
*/ | |||
public abstract class SqlBuilder<T extends SqlBuilder> { | |||
protected List<SqlFieldValue> fields = new ArrayList<>(); | |||
protected List<SqlWhereValue> wheres; | |||
SqlBuilder() { | |||
} | |||
public static InsertSqlBuilder createInsert(String tableName) { | |||
return new InsertSqlBuilder(tableName); | |||
} | |||
public static UpdateSqlBuilder createUpdate(String tableName) { | |||
return new UpdateSqlBuilder(tableName); | |||
} | |||
public static SelectSqlBuilder createSelect(String tableName) { | |||
return new SelectSqlBuilder(tableName); | |||
} | |||
public static DeleteSqlBuilder createDelete(String tableName) { | |||
return new DeleteSqlBuilder(tableName); | |||
} | |||
public static InsertSqlBuilder createInsert(String dbName, String tableName) { | |||
return new InsertSqlBuilder(dbName + "." + tableName); | |||
} | |||
public static UpdateSqlBuilder createUpdate(String dbName, String tableName) { | |||
return new UpdateSqlBuilder(dbName + "." + tableName); | |||
} | |||
public static AbstractSelectSqlBuilder createSelect(String dbName, String tableName) { | |||
return new SelectSqlBuilder(dbName + "." + tableName); | |||
} | |||
public static AbstractSelectSqlBuilder createDirectSelect(String sql) { | |||
return new DirectSelectSqlBuilder(sql); | |||
} | |||
public static DeleteSqlBuilder createDelete(String dbName, String tableName) { | |||
return new DeleteSqlBuilder(dbName + "." + tableName); | |||
} | |||
public T add(String fieldName) { | |||
fields.add(new SqlFieldValue(fieldName, null)); | |||
return (T)this; | |||
} | |||
public T add(String fieldName, Object fieldValue) { | |||
fields.add(new SqlFieldValue(fieldName, fieldValue)); | |||
return (T)this; | |||
} | |||
public T addWhere(String fieldName, Object fieldValue, String op) { | |||
if (wheres == null) { | |||
wheres = new ArrayList<>(); | |||
} | |||
wheres.add(new SqlWhereValue(fieldName, fieldValue, op)); | |||
return (T)this; | |||
} | |||
public T addWhere(String fieldName, Object fieldValue) { | |||
return addWhere(fieldName, fieldValue, "="); | |||
} | |||
public T addWhereOrBegin() { | |||
return addWhere(null, "(", " or "); | |||
} | |||
public T addWhereOrEnd() { | |||
return addWhere(null, ")", " and "); | |||
} | |||
public T addOrderBy(String orderBy) { | |||
return (T)this; | |||
} | |||
} |
@@ -0,0 +1,14 @@ | |||
package cc.smtweb.framework.core.db.sqlbuilder; | |||
import lombok.Getter; | |||
@Getter | |||
class SqlFieldValue { | |||
private final String name; | |||
private final Object value; | |||
public SqlFieldValue(String name, Object value) { | |||
this.name = name; | |||
this.value = value; | |||
} | |||
} |
@@ -0,0 +1,15 @@ | |||
package cc.smtweb.framework.core.db.sqlbuilder; | |||
import lombok.Data; | |||
import lombok.Getter; | |||
@Getter | |||
public class SqlJoinField { | |||
private String keyField; | |||
private String valueField; | |||
public SqlJoinField(String keyField, String valueField) { | |||
this.keyField = keyField; | |||
this.valueField = valueField; | |||
} | |||
} |
@@ -0,0 +1,39 @@ | |||
package cc.smtweb.framework.core.db.sqlbuilder; | |||
import lombok.Data; | |||
import java.util.ArrayList; | |||
import java.util.List; | |||
/** | |||
* SelectSqlBuilder使用的查询关联表 | |||
* @author xkliu | |||
*/ | |||
@Data | |||
public class SqlJoinTable { | |||
public static final int LEFT_JOIN = 1; | |||
public static final int RIGHT_JOIN = 2; | |||
public static final int INNER_JOIN = 3; | |||
public static final int FULL_JOIN = 4; | |||
private int joinType = LEFT_JOIN; | |||
private String dbName; | |||
private String tableName; | |||
private String tableAlias; | |||
private List<SqlJoinField> fields = new ArrayList<>(); | |||
public void add(String keyField, String valueField) { | |||
fields.add(new SqlJoinField(keyField, valueField)); | |||
} | |||
public String joinSql() { | |||
switch (joinType) { | |||
default: | |||
case LEFT_JOIN: return " LEFT JOIN "; | |||
case RIGHT_JOIN: return " RIGHT JOIN "; | |||
case INNER_JOIN: return " INNER JOIN "; | |||
case FULL_JOIN: return " FULL JOIN "; | |||
} | |||
} | |||
} |
@@ -0,0 +1,13 @@ | |||
package cc.smtweb.framework.core.db.sqlbuilder; | |||
import lombok.Getter; | |||
class SqlWhereValue extends SqlFieldValue { | |||
@Getter | |||
private String op; | |||
public SqlWhereValue(String name, Object value, String op) { | |||
super(name, value); | |||
this.op = op; | |||
} | |||
} |
@@ -0,0 +1,55 @@ | |||
package cc.smtweb.framework.core.db.sqlbuilder; | |||
import cc.smtweb.framework.core.db.jdbc.JdbcEngine; | |||
import java.util.ArrayList; | |||
import java.util.List; | |||
public class UpdateSqlBuilder extends AbstractUpdateSqlBuilder<UpdateSqlBuilder> { | |||
private String tableName; | |||
UpdateSqlBuilder(String tableName) { | |||
this.tableName = tableName; | |||
} | |||
@Override | |||
public int update(JdbcEngine dbEngine) { | |||
int fieldSize = fields.size() + wheres.size(); | |||
List<Object> params = new ArrayList<>(fieldSize); | |||
// Object[] params = new Object[fieldSize]; | |||
StringBuilder sb = new StringBuilder("update "); | |||
sb.append(tableName).append(" set "); | |||
// int index = 0; | |||
for (SqlFieldValue field: fields) { | |||
if (field.getValue() == VALUE_INVALID) { | |||
sb.append(field.getName()); | |||
} else { | |||
sb.append(field.getName()).append("=?,"); | |||
params.add(field.getValue()); | |||
} | |||
// params[index] = field.getValue(); | |||
// index++; | |||
} | |||
sb.setCharAt(sb.length() - 1, ' '); | |||
sb.append("where "); | |||
boolean first = true; | |||
for (SqlWhereValue whereValue: wheres) { | |||
if (first) { | |||
first = false; | |||
} else { | |||
sb.append(" and "); | |||
} | |||
sb.append(whereValue.getName()).append("=?"); | |||
params.add(whereValue.getValue()); | |||
// params[index] | |||
// index++; | |||
} | |||
return dbEngine.update(sb.toString(), params.toArray()); | |||
} | |||
} |
@@ -0,0 +1,9 @@ | |||
package cc.smtweb.framework.core.db.vo; | |||
import lombok.Data; | |||
@Data | |||
public class KeyValueVO { | |||
private String key; | |||
private String value; | |||
} |
@@ -0,0 +1,30 @@ | |||
package cc.smtweb.framework.core.db.vo; | |||
import lombok.Data; | |||
import java.util.List; | |||
/** | |||
* 表缓存信息定义:{"name":"tn","title":"按表名","fields":"table_name","type":"M"} | |||
* Created by Akmm at 2022/2/21 16:22 | |||
*/ | |||
@Data | |||
public class ModelCache { | |||
//按map缓存 | |||
public static String CACHE_TYPE_MAP = "M"; | |||
//按list缓存 | |||
public static String CACHE_TYPE_LIST = "L"; | |||
//缓存名,根据此名称去获取缓存信息 getByKey的参数 | |||
private String name; | |||
//缓存中文名,给人看的 | |||
private String title; | |||
//字段,多个字段,键值以下划线分隔 | |||
private String fields; | |||
//缓存类别:list/map | |||
private String type; | |||
public boolean isMapType() { | |||
return CACHE_TYPE_MAP.equalsIgnoreCase(type); | |||
} | |||
} |
@@ -0,0 +1,27 @@ | |||
package cc.smtweb.framework.core.db.vo; | |||
import lombok.Data; | |||
import java.io.Serializable; | |||
/** | |||
* 目录 | |||
*/ | |||
@Data | |||
public class ModelCatalog implements Serializable { | |||
private long id; | |||
private long parentId; | |||
//项目id | |||
private long prjId; | |||
//目录编码及名称 | |||
private String code; | |||
private String name; | |||
//创建者 | |||
private Long createUid; | |||
//创建时间 | |||
private Long createTime; | |||
//最后更新人 | |||
private Long updateUid; | |||
//更新时间 | |||
private Long updateTime; | |||
} |
@@ -0,0 +1,31 @@ | |||
package cc.smtweb.framework.core.db.vo; | |||
import cc.smtweb.framework.core.annotation.SwTable; | |||
import lombok.Data; | |||
import java.io.Serializable; | |||
import java.util.Map; | |||
@Data | |||
@SwTable(value = "ASP_MODEL_DATABASE") | |||
public class ModelDatabase implements Serializable { | |||
private long id; | |||
//项目id | |||
private long prjId; | |||
// 库名 | |||
private String name; | |||
//中文标题 | |||
private String title; | |||
//状态:0-启用 1-停用 | |||
private int status; | |||
//版本 | |||
private int version; | |||
//创建者 | |||
private Long createUid; | |||
//创建时间 | |||
private Long createTime; | |||
//最后更新人 | |||
private Long updateUid; | |||
//更新时间 | |||
private Long updateTime; | |||
} |
@@ -0,0 +1,38 @@ | |||
package cc.smtweb.framework.core.db.vo; | |||
import cc.smtweb.framework.core.db.vo.def.DataType; | |||
import com.fasterxml.jackson.annotation.JsonProperty; | |||
import lombok.Data; | |||
import java.util.List; | |||
/** | |||
* 字段定义 | |||
* {name:"字段名,如id",fieldType:"字段类型,如编码字段", dataType:"数据类型,如ID/CODE/NAME等", null:"0-空/1-非空", default: "默认值", title:"中文名",link:"外键关联表",editor:"控件类型:TEXT/TextArea/NUMBER/COMBO"} | |||
*/ | |||
@Data | |||
public class ModelField { | |||
private String name; | |||
private String title; | |||
private String remark; | |||
//字段类型,如编码字段,参见FieldTypeDef | |||
private String fieldType; | |||
/** | |||
* 数据类型,参见DataType | |||
*/ | |||
private String dataType; | |||
/** | |||
* '禁止为空' | |||
*/ | |||
@JsonProperty("null") | |||
private int notNull; | |||
/** | |||
* '默认值' | |||
*/ | |||
@JsonProperty("default") | |||
private String defaultValue; | |||
//外键关联表 | |||
private String link; | |||
//控件类型:TEXT/TextArea/NUMBER/COMBO | |||
private String editor; | |||
} |
@@ -0,0 +1,20 @@ | |||
package cc.smtweb.framework.core.db.vo; | |||
import lombok.Data; | |||
import java.util.ArrayList; | |||
import java.util.List; | |||
/** | |||
* 索引定义 {name:"索引名称,如idx_t1", fields:"字段,如f1,f2", type="索引类别:P-主键 I-一般索引 U-唯一索引"} | |||
*/ | |||
@Data | |||
public class ModelIndex { | |||
public static final String TYPE_PRIMARY = "P"; | |||
public static final String TYPE_INDEX = "I"; | |||
public static final String TYPE_UNIQUE = "U"; | |||
private String type; | |||
private String name; | |||
private String fields; | |||
} |
@@ -0,0 +1,29 @@ | |||
package cc.smtweb.framework.core.db.vo; | |||
import lombok.Data; | |||
import java.io.Serializable; | |||
/** | |||
* 项目 | |||
*/ | |||
@Data | |||
public class ModelProject implements Serializable { | |||
private long id; | |||
// 项目名称 | |||
private String name; | |||
//依赖项目 | |||
private String depends; | |||
//状态:0-启用 1-停用 | |||
private int status; | |||
//备注 | |||
private String desc; | |||
//创建者 | |||
private Long createUid; | |||
//创建时间 | |||
private Long createTime; | |||
//最后更新人 | |||
private Long updateUid; | |||
//更新时间 | |||
private Long updateTime; | |||
} |
@@ -0,0 +1,142 @@ | |||
package cc.smtweb.framework.core.db.vo; | |||
import cc.smtweb.framework.core.annotation.SwTable; | |||
import cc.smtweb.framework.core.db.vo.def.FieldType; | |||
import cc.smtweb.framework.core.util.JsonUtil; | |||
import lombok.Data; | |||
import java.io.Serializable; | |||
import java.util.ArrayList; | |||
import java.util.List; | |||
@Data | |||
@SwTable(value="ASP_MODEL_TABLE") | |||
public class ModelTable implements Serializable { | |||
private long id; | |||
//所属数据库id及名称 | |||
private long databaseId; | |||
//项目id | |||
private long prjoectId; | |||
//目录id | |||
private long catalogId; | |||
//库表名 | |||
private String name; | |||
//中文名 | |||
private String title; | |||
//缩写 | |||
private String abbr; | |||
//继承的表 | |||
private String tableExtends; | |||
//类型 0-普通表,1 树型表 2 编码表 9-虚拟抽象表 11 视图' | |||
private int type; | |||
//表定义 | |||
private String tableContent; | |||
//是否需要缓存 | |||
private boolean needCache; | |||
//创建者 | |||
private Long createUid; | |||
//创建时间 | |||
private Long createAt; | |||
//最后更新人 | |||
private Long updateUid; | |||
//更新时间 | |||
private Long updateAt; | |||
/*冗余*/ | |||
private String idField; | |||
private String dbName; | |||
private List<ModelField> fields = new ArrayList<>(); | |||
private List<ModelIndex> indexes = new ArrayList<>(); | |||
private List<ModelCache> caches = new ArrayList<>(); | |||
public ModelField findField(String fieldName) { | |||
if (fieldName != null && fields != null) { | |||
for (ModelField modelField : fields) { | |||
if (fieldName.equals(modelField.getName())) { | |||
return modelField; | |||
} | |||
} | |||
} | |||
return null; | |||
} | |||
public String fullName() { | |||
return dbName + '.' + name; | |||
} | |||
public void addIndex(ModelIndex modelIndex) { | |||
indexes.add(modelIndex); | |||
} | |||
public ModelIndex findIndexByName(String indexName) { | |||
for (ModelIndex modelIndex : indexes) { | |||
if (indexName.equals(modelIndex.getName())) { | |||
return modelIndex; | |||
} | |||
} | |||
return null; | |||
} | |||
public ModelIndex findPrimaryIndex() { | |||
for (ModelIndex modelIndex : indexes) { | |||
if (ModelIndex.TYPE_PRIMARY.equalsIgnoreCase(modelIndex.getType())) { | |||
return modelIndex; | |||
} | |||
} | |||
return null; | |||
} | |||
public ModelField findFieldByName(String name) { | |||
if (name != null) { | |||
for (ModelField value : fields) { | |||
if (name.equalsIgnoreCase(value.getName())) { | |||
return value; | |||
} | |||
} | |||
} | |||
return null; | |||
} | |||
public ModelField findFieldByType(FieldType type) { | |||
if (type != null) { | |||
for (ModelField value : fields) { | |||
if (type.name().equalsIgnoreCase(value.getFieldType())) { | |||
return value; | |||
} | |||
} | |||
} | |||
return null; | |||
} | |||
public ModelField findIdField() { | |||
ModelIndex index = findPrimaryIndex(); | |||
if (index != null) { | |||
return findField(index.getFields()); | |||
} | |||
return null; | |||
} | |||
public void setTableContent(String tableContent) { | |||
this.tableContent = tableContent; | |||
//读取表定义信息 | |||
ModelTable bean = JsonUtil.parse(tableContent, ModelTable.class); | |||
if (bean == null) { | |||
return; | |||
} | |||
this.fields = bean.fields; | |||
this.indexes = bean.indexes; | |||
this.caches = bean.caches; | |||
ModelIndex i = findPrimaryIndex(); | |||
if (i != null) { | |||
this.idField = i.getFields(); | |||
} | |||
} | |||
} |
@@ -0,0 +1,33 @@ | |||
package cc.smtweb.framework.core.db.vo.def; | |||
import lombok.Data; | |||
/** | |||
* 数据类型定义,参见design_db.yaml配置 | |||
*/ | |||
@Data | |||
public class DataType { | |||
/*{type: "id", name: "ID", sql-type: "bigint", java-type: "long", widget: "fz-field-long", defaultValue: "0"} | |||
- {type: "code", name: "编码", sql-type: "varchar", java-type: "string", data-length: 32, widget: "fz-field-string", defaultValue: ""} | |||
- {type: "name", name: "名字", sql-type: "varchar", java-type: "string", data-length: 100, widget: "fz-field-string", defaultValue: ""} | |||
- {type: "remark", name: "备注", sql-type: "varchar", data-length: 255, java-type: "string", widget: "fz-field-string", defaultValue: ""} | |||
- {type: "text", name: "大文本", sql-type: "text", java-type: "string", widget: "fz-field-string", defaultValue: ""} | |||
- {type: "currency", name: "货币", sql-type: "bigint", java-type: "long", widget: "fz-field-long", defaultValue: "0"} | |||
- {type: "datetime", name: "时间日期", sql-type: "bigint", java-type: "long", widget: "fz-field-datetime", defaultValue: "0"} | |||
- {type: "date", name: "日期", sql-type: "int", java-type: "int", widget: "fz-field-date", defaultValue: "0"} | |||
- {type: "time", name: "时间", sql-type: "int", java-type: "int", widget: "fz-field-time", defaultValue: "0"} | |||
- {type: "int", name: "整型", sql-type: "int", java-type: "int", widget: "fz-field-int", defaultValue: "0"} | |||
- {type: "smallint", name: "短整型", sql-type: "smallint", java-type: "short", widget: "fz-field-int", defaultValue: "0"} | |||
- {type: "bool", name: "布尔型", sql-type: "tinyint", java-type: "boolean", widget: "fz-field-bool", defaultValue: "0"}*/ | |||
public static final String TYPE_STR = "varchar"; | |||
public static final String TYPE_BOOL = "bool"; | |||
public static final String TYPE_DATETIME = "datetime"; | |||
public static final String TYPE_DATE = "date"; | |||
private String type; | |||
private String name; | |||
private String sqlType; | |||
private int dataLength; | |||
private String javaType; | |||
private String defaultValue; | |||
} |
@@ -0,0 +1,22 @@ | |||
package cc.smtweb.framework.core.db.vo.def; | |||
/** | |||
* Created by Akmm at 2022/2/9 10:01 | |||
* 字段业务类别 | |||
*/ | |||
public enum FieldType { | |||
// 主键 | |||
ID, | |||
// 上级ID,树结构需要 | |||
PARENT_ID, | |||
// 排序字段,树结构需要 | |||
ORDER, | |||
// 编码字段 | |||
CODE, | |||
// 名词字段 | |||
NAME, | |||
// 创建时间 | |||
CREATE_TIME, | |||
// 更新时间 | |||
LAST_TIME | |||
} |
@@ -0,0 +1,41 @@ | |||
package cc.smtweb.framework.core.exception; | |||
import cc.smtweb.framework.core.SwException; | |||
/** | |||
* bean绑定错误 | |||
* @author kevin | |||
* @since 2010-9-14 上午11:17:43 | |||
* | |||
*/ | |||
public class BindBeanException extends SwException { | |||
/** | |||
* | |||
*/ | |||
private static final long serialVersionUID = 1L; | |||
private static String msg = "绑定bean异常:Context中已经存在这个bean: "; | |||
public BindBeanException() { | |||
super(); | |||
// TODO Auto-generated constructor stub | |||
} | |||
public BindBeanException(String message, Throwable cause) { | |||
super(message, cause); | |||
// TODO Auto-generated constructor stub | |||
} | |||
public BindBeanException(String message) { | |||
super(msg+message); | |||
// TODO Auto-generated constructor stub | |||
} | |||
public BindBeanException(Throwable cause) { | |||
super(cause); | |||
// TODO Auto-generated constructor stub | |||
} | |||
} |
@@ -0,0 +1,34 @@ | |||
package cc.smtweb.framework.core.exception; | |||
import cc.smtweb.framework.core.SwException; | |||
import java.text.ParseException; | |||
public class BindParamException extends SwException { | |||
/** | |||
* | |||
*/ | |||
private static final long serialVersionUID = 1L; | |||
private String paramName; | |||
public String getParamName() { | |||
return paramName; | |||
} | |||
public BindParamException(String message, String paramName) { | |||
this(message, null, paramName); | |||
} | |||
public BindParamException(Exception e, String paramName) { | |||
this(e.getMessage(), e, paramName); | |||
} | |||
public BindParamException(String message, Exception e, String paramName) { | |||
super("[" + paramName + "]" + message, e); | |||
this.paramName = paramName; | |||
} | |||
public BindParamException(ParseException e) { | |||
super(e); | |||
} | |||
} |
@@ -0,0 +1,29 @@ | |||
package cc.smtweb.framework.core.exception; | |||
/** | |||
* 业务异常 | |||
* @author kevin | |||
* | |||
*/ | |||
public class BizException extends RuntimeException { | |||
/** | |||
* | |||
*/ | |||
private static final long serialVersionUID = 1L; | |||
public BizException() { | |||
super(); | |||
} | |||
public BizException(String message, Throwable cause) { | |||
super(message, cause); | |||
} | |||
public BizException(String message) { | |||
super(message); | |||
} | |||
public BizException(Throwable cause) { | |||
super(cause.getMessage(), cause); | |||
} | |||
} |
@@ -0,0 +1,13 @@ | |||
package cc.smtweb.framework.core.exception; | |||
import cc.smtweb.framework.core.SwException; | |||
public class CacheException extends SwException { | |||
public CacheException(String message) { | |||
super(message); | |||
} | |||
public CacheException(Throwable e) { | |||
super(e); | |||
} | |||
} |
@@ -0,0 +1,33 @@ | |||
package cc.smtweb.framework.core.exception; | |||
import cc.smtweb.framework.core.SwException; | |||
/** | |||
* bean绑定错误 | |||
* @author kevin | |||
* @since 2010-9-14 上午11:17:43 | |||
* | |||
*/ | |||
public class DbException extends RuntimeException { | |||
/** | |||
* | |||
*/ | |||
private static final long serialVersionUID = 1L; | |||
public DbException() { | |||
super(); | |||
} | |||
public DbException(String message, Throwable cause) { | |||
super(message, cause); | |||
} | |||
public DbException(String message) { | |||
super(message); | |||
} | |||
public DbException(Throwable cause) { | |||
super(cause.getMessage(), cause); | |||
} | |||
} |
@@ -0,0 +1,105 @@ | |||
package cc.smtweb.framework.core.exception; | |||
import lombok.Getter; | |||
import lombok.Setter; | |||
import lombok.ToString; | |||
import org.apache.commons.lang3.StringUtils; | |||
import java.util.Set; | |||
/** | |||
* 错误码 JDK version used: <JDK1.8> | |||
* | |||
* @author kevin | |||
* @version v1.0 | |||
*/ | |||
@Setter | |||
@Getter | |||
@ToString | |||
public class ExceptionMessage { | |||
private int code; | |||
private String msg; | |||
private ExceptionMessage(int code, String msg) { | |||
this.code = code; | |||
this.msg = msg; | |||
} | |||
// 401 | |||
public static ExceptionMessage NO_AUTH_ERROR = new ExceptionMessage(401, "登录失效!"); | |||
// 403 | |||
public static ExceptionMessage FORBIDDEN_ERROR = new ExceptionMessage(403, "没有访问权限!"); | |||
// 404 | |||
public static ExceptionMessage NOT_FOUND_ERROR = new ExceptionMessage(404, "没有找到需要资源!"); | |||
// 通用异常 | |||
public static ExceptionMessage INNER_ERROR = new ExceptionMessage(100100, "内部服务错误,请稍后再试!"); | |||
// 参数错误 | |||
public static ExceptionMessage PARAM_ERROR = new ExceptionMessage(100101, "参数校验失败!"); | |||
// 数据库访问异常 | |||
public static ExceptionMessage DB_ERROR = new ExceptionMessage(100102, "数据库访问失败!"); | |||
// 外部网络访问异常 | |||
public static ExceptionMessage NETWORK_ERROR = new ExceptionMessage(100103, "外部网络访问失败!"); | |||
// 字符转换异常 | |||
public static ExceptionMessage UNSUPPORTED_ENCODING_ERROR = new ExceptionMessage(100104, "字符编码失败!"); | |||
// 没有登录 | |||
public static ExceptionMessage NO_LOGIN_ERROR = new ExceptionMessage(100105, "用户没有登录!"); | |||
// 没有登录 | |||
public static ExceptionMessage CAPTCHA_ERROR = new ExceptionMessage(100106, "验证码错误!"); | |||
// 用户不存在 | |||
public static ExceptionMessage USER_ERROR = new ExceptionMessage(100107, "手机号或密码错误,请重新输入!"); | |||
// 用户被禁 | |||
public static ExceptionMessage USER_FORBIDED = new ExceptionMessage(100108, "用户被禁!"); | |||
// 密码错误 | |||
public static ExceptionMessage PASSWORD_ERROR = new ExceptionMessage(100109, "密码错误,请重新输入!"); | |||
// 文件上传失败 | |||
public static ExceptionMessage FILE_UPLOAD_ERROR = new ExceptionMessage(100110, "文件上传失败!"); | |||
// 文件不存在 | |||
public static ExceptionMessage FILE_NOT_EXIST = new ExceptionMessage(100111, "文件不存在!"); | |||
// 树形层级只有6级 | |||
public static ExceptionMessage TREE_HIERARCHY_ERROR = new ExceptionMessage(100112, "树形层级过多!"); | |||
// 手机号被使用了 | |||
public static ExceptionMessage PHONE_ERROR = new ExceptionMessage(100113, "手机号被使用了!"); | |||
// 手机号不存在 | |||
public static ExceptionMessage PHONE_EXISTS_ERROR = new ExceptionMessage(100114, "手机号不存在!"); | |||
// 消息发送要延迟 | |||
public static ExceptionMessage SM_SEND_NEED_DELAY = new ExceptionMessage(100115, "验证码有效时间"); | |||
// 身份不能确认 | |||
public static ExceptionMessage IDENTITY_ERROR = new ExceptionMessage(100116, "请选择登录身份!"); | |||
// 身份不能确认 | |||
public static ExceptionMessage USER_PERMISSION_CHANGE = new ExceptionMessage(100117, "权限变更,请选择退出重新登录!"); | |||
// 数据发生变更 | |||
public static ExceptionMessage DATA_CHANGE_ERROR = new ExceptionMessage(100118, "数据发生变更,请刷新后重试!"); | |||
// 数据发生变更 | |||
public static ExceptionMessage SM_SEND_ERROR = new ExceptionMessage(100119, "验证码发生失败!"); | |||
// 敏感词 | |||
public static ExceptionMessage XSS_ERROR = new ExceptionMessage(100120, "含有敏感词,请检查。"); | |||
// 树形层级数量太多 | |||
public static ExceptionMessage TREE_NUMBER_ERROR = new ExceptionMessage(100121, "树形该层节点过多!"); | |||
public static ExceptionMessage APP_ID_NOT_EXISTS = new ExceptionMessage(100122, "不存在的应用ID"); | |||
} |