first commit

This commit is contained in:
zc
2026-02-06 12:23:45 +08:00
parent 994b3cb834
commit 62beb474b2
33 changed files with 2260 additions and 0 deletions

View File

@@ -0,0 +1,16 @@
<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">
<parent>
<groupId>io.renren</groupId>
<artifactId>renren-security</artifactId>
<version>5.2.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>renren-dynamic-datasource</artifactId>
<packaging>jar</packaging>
<description>多数据源</description>
<build>
<finalName>${project.artifactId}</finalName>
</build>
</project>

View File

@@ -0,0 +1,25 @@
/**
* Copyright (c) 2018 人人开源 All rights reserved.
*
* https://www.renren.io
*
* 版权所有,侵权必究!
*/
package io.renren.commons.dynamic.datasource.annotation;
import java.lang.annotation.*;
/**
* 多数据源注解
*
* @author Mark sunlightcs@gmail.com
* @since 1.0.0
*/
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface DataSource {
String value() default "";
}

View File

@@ -0,0 +1,71 @@
/**
* Copyright (c) 2018 人人开源 All rights reserved.
*
* https://www.renren.io
*
* 版权所有,侵权必究!
*/
package io.renren.commons.dynamic.datasource.aspect;
import io.renren.commons.dynamic.datasource.annotation.DataSource;
import io.renren.commons.dynamic.datasource.config.DynamicContextHolder;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
/**
* 多数据源,切面处理类
*
* @author Mark sunlightcs@gmail.com
* @since 1.0.0
*/
@Aspect
@Component
@Order(Ordered.HIGHEST_PRECEDENCE)
public class DataSourceAspect {
protected Logger logger = LoggerFactory.getLogger(getClass());
@Pointcut("@annotation(io.renren.commons.dynamic.datasource.annotation.DataSource) " +
"|| @within(io.renren.commons.dynamic.datasource.annotation.DataSource)")
public void dataSourcePointCut() {
}
@Around("dataSourcePointCut()")
public Object around(ProceedingJoinPoint point) throws Throwable {
MethodSignature signature = (MethodSignature) point.getSignature();
Class targetClass = point.getTarget().getClass();
Method method = signature.getMethod();
DataSource targetDataSource = (DataSource)targetClass.getAnnotation(DataSource.class);
DataSource methodDataSource = method.getAnnotation(DataSource.class);
if(targetDataSource != null || methodDataSource != null){
String value;
if(methodDataSource != null){
value = methodDataSource.value();
}else {
value = targetDataSource.value();
}
DynamicContextHolder.push(value);
logger.debug("set datasource is {}", value);
}
try {
return point.proceed();
} finally {
DynamicContextHolder.poll();
logger.debug("clean datasource");
}
}
}

View File

@@ -0,0 +1,57 @@
/**
* Copyright (c) 2018 人人开源 All rights reserved.
*
* https://www.renren.io
*
* 版权所有,侵权必究!
*/
package io.renren.commons.dynamic.datasource.config;
import java.util.ArrayDeque;
import java.util.Deque;
/**
* 多数据源上下文
*
* @author Mark sunlightcs@gmail.com
*/
public class DynamicContextHolder {
@SuppressWarnings("unchecked")
private static final ThreadLocal<Deque<String>> CONTEXT_HOLDER = new ThreadLocal() {
@Override
protected Object initialValue() {
return new ArrayDeque();
}
};
/**
* 获得当前线程数据源
*
* @return 数据源名称
*/
public static String peek() {
return CONTEXT_HOLDER.get().peek();
}
/**
* 设置当前线程数据源
*
* @param dataSource 数据源名称
*/
public static void push(String dataSource) {
CONTEXT_HOLDER.get().push(dataSource);
}
/**
* 清空当前线程数据源
*/
public static void poll() {
Deque<String> deque = CONTEXT_HOLDER.get();
deque.poll();
if (deque.isEmpty()) {
CONTEXT_HOLDER.remove();
}
}
}

View File

@@ -0,0 +1,26 @@
/**
* Copyright (c) 2018 人人开源 All rights reserved.
*
* https://www.renren.io
*
* 版权所有,侵权必究!
*/
package io.renren.commons.dynamic.datasource.config;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
/**
* 多数据源
*
* @author Mark sunlightcs@gmail.com
* @since 1.0.0
*/
public class DynamicDataSource extends AbstractRoutingDataSource {
@Override
protected Object determineCurrentLookupKey() {
return DynamicContextHolder.peek();
}
}

View File

@@ -0,0 +1,64 @@
/**
* Copyright (c) 2018 人人开源 All rights reserved.
*
* https://www.renren.io
*
* 版权所有,侵权必究!
*/
package io.renren.commons.dynamic.datasource.config;
import com.alibaba.druid.pool.DruidDataSource;
import io.renren.commons.dynamic.datasource.properties.DataSourceProperties;
import io.renren.commons.dynamic.datasource.properties.DynamicDataSourceProperties;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.HashMap;
import java.util.Map;
/**
* 配置多数据源
*
* @author Mark sunlightcs@gmail.com
* @since 1.0.0
*/
@Configuration
@EnableConfigurationProperties(DynamicDataSourceProperties.class)
public class DynamicDataSourceConfig {
@Autowired
private DynamicDataSourceProperties properties;
@Bean
@ConfigurationProperties(prefix = "spring.datasource.druid")
public DataSourceProperties dataSourceProperties() {
return new DataSourceProperties();
}
@Bean
public DynamicDataSource dynamicDataSource(DataSourceProperties dataSourceProperties) {
DynamicDataSource dynamicDataSource = new DynamicDataSource();
dynamicDataSource.setTargetDataSources(getDynamicDataSource());
//默认数据源
DruidDataSource defaultDataSource = DynamicDataSourceFactory.buildDruidDataSource(dataSourceProperties);
dynamicDataSource.setDefaultTargetDataSource(defaultDataSource);
return dynamicDataSource;
}
private Map<Object, Object> getDynamicDataSource(){
Map<String, DataSourceProperties> dataSourcePropertiesMap = properties.getDatasource();
Map<Object, Object> targetDataSources = new HashMap<>(dataSourcePropertiesMap.size());
dataSourcePropertiesMap.forEach((k, v) -> {
DruidDataSource druidDataSource = DynamicDataSourceFactory.buildDruidDataSource(v);
targetDataSources.put(k, druidDataSource);
});
return targetDataSources;
}
}

View File

@@ -0,0 +1,54 @@
/**
* Copyright (c) 2018 人人开源 All rights reserved.
*
* https://www.renren.io
*
* 版权所有,侵权必究!
*/
package io.renren.commons.dynamic.datasource.config;
import com.alibaba.druid.pool.DruidDataSource;
import io.renren.commons.dynamic.datasource.properties.DataSourceProperties;
import java.sql.SQLException;
/**
* DruidDataSource
*
* @author Mark sunlightcs@gmail.com
* @since 1.0.0
*/
public class DynamicDataSourceFactory {
public static DruidDataSource buildDruidDataSource(DataSourceProperties properties) {
DruidDataSource druidDataSource = new DruidDataSource();
druidDataSource.setDriverClassName(properties.getDriverClassName());
druidDataSource.setUrl(properties.getUrl());
druidDataSource.setUsername(properties.getUsername());
druidDataSource.setPassword(properties.getPassword());
druidDataSource.setInitialSize(properties.getInitialSize());
druidDataSource.setMaxActive(properties.getMaxActive());
druidDataSource.setMinIdle(properties.getMinIdle());
druidDataSource.setMaxWait(properties.getMaxWait());
druidDataSource.setTimeBetweenEvictionRunsMillis(properties.getTimeBetweenEvictionRunsMillis());
druidDataSource.setMinEvictableIdleTimeMillis(properties.getMinEvictableIdleTimeMillis());
druidDataSource.setMaxEvictableIdleTimeMillis(properties.getMaxEvictableIdleTimeMillis());
druidDataSource.setValidationQuery(properties.getValidationQuery());
druidDataSource.setValidationQueryTimeout(properties.getValidationQueryTimeout());
druidDataSource.setTestOnBorrow(properties.isTestOnBorrow());
druidDataSource.setTestOnReturn(properties.isTestOnReturn());
druidDataSource.setPoolPreparedStatements(properties.isPoolPreparedStatements());
druidDataSource.setMaxOpenPreparedStatements(properties.getMaxOpenPreparedStatements());
druidDataSource.setSharePreparedStatements(properties.isSharePreparedStatements());
try {
// druidDataSource.setFilters(properties.getFilters());
druidDataSource.init();
} catch (SQLException e) {
e.printStackTrace();
}
return druidDataSource;
}
}

View File

@@ -0,0 +1,202 @@
/**
* Copyright (c) 2018 人人开源 All rights reserved.
*
* https://www.renren.io
*
* 版权所有,侵权必究!
*/
package io.renren.commons.dynamic.datasource.properties;
/**
* 多数据源属性
*
* @author Mark sunlightcs@gmail.com
* @since 1.0.0
*/
public class DataSourceProperties {
private String driverClassName;
private String url;
private String username;
private String password;
/**
* Druid默认参数
*/
private int initialSize = 2;
private int maxActive = 10;
private int minIdle = -1;
private long maxWait = 60 * 1000L;
private long timeBetweenEvictionRunsMillis = 60 * 1000L;
private long minEvictableIdleTimeMillis = 1000L * 60L * 30L;
private long maxEvictableIdleTimeMillis = 1000L * 60L * 60L * 7;
private String validationQuery = "select 1";
private int validationQueryTimeout = -1;
private boolean testOnBorrow = false;
private boolean testOnReturn = false;
private boolean testWhileIdle = true;
private boolean poolPreparedStatements = false;
private int maxOpenPreparedStatements = -1;
private boolean sharePreparedStatements = false;
private String filters = "stat,wall";
public String getDriverClassName() {
return driverClassName;
}
public void setDriverClassName(String driverClassName) {
this.driverClassName = driverClassName;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public int getInitialSize() {
return initialSize;
}
public void setInitialSize(int initialSize) {
this.initialSize = initialSize;
}
public int getMaxActive() {
return maxActive;
}
public void setMaxActive(int maxActive) {
this.maxActive = maxActive;
}
public int getMinIdle() {
return minIdle;
}
public void setMinIdle(int minIdle) {
this.minIdle = minIdle;
}
public long getMaxWait() {
return maxWait;
}
public void setMaxWait(long maxWait) {
this.maxWait = maxWait;
}
public long getTimeBetweenEvictionRunsMillis() {
return timeBetweenEvictionRunsMillis;
}
public void setTimeBetweenEvictionRunsMillis(long timeBetweenEvictionRunsMillis) {
this.timeBetweenEvictionRunsMillis = timeBetweenEvictionRunsMillis;
}
public long getMinEvictableIdleTimeMillis() {
return minEvictableIdleTimeMillis;
}
public void setMinEvictableIdleTimeMillis(long minEvictableIdleTimeMillis) {
this.minEvictableIdleTimeMillis = minEvictableIdleTimeMillis;
}
public long getMaxEvictableIdleTimeMillis() {
return maxEvictableIdleTimeMillis;
}
public void setMaxEvictableIdleTimeMillis(long maxEvictableIdleTimeMillis) {
this.maxEvictableIdleTimeMillis = maxEvictableIdleTimeMillis;
}
public String getValidationQuery() {
return validationQuery;
}
public void setValidationQuery(String validationQuery) {
this.validationQuery = validationQuery;
}
public int getValidationQueryTimeout() {
return validationQueryTimeout;
}
public void setValidationQueryTimeout(int validationQueryTimeout) {
this.validationQueryTimeout = validationQueryTimeout;
}
public boolean isTestOnBorrow() {
return testOnBorrow;
}
public void setTestOnBorrow(boolean testOnBorrow) {
this.testOnBorrow = testOnBorrow;
}
public boolean isTestOnReturn() {
return testOnReturn;
}
public void setTestOnReturn(boolean testOnReturn) {
this.testOnReturn = testOnReturn;
}
public boolean isTestWhileIdle() {
return testWhileIdle;
}
public void setTestWhileIdle(boolean testWhileIdle) {
this.testWhileIdle = testWhileIdle;
}
public boolean isPoolPreparedStatements() {
return poolPreparedStatements;
}
public void setPoolPreparedStatements(boolean poolPreparedStatements) {
this.poolPreparedStatements = poolPreparedStatements;
}
public int getMaxOpenPreparedStatements() {
return maxOpenPreparedStatements;
}
public void setMaxOpenPreparedStatements(int maxOpenPreparedStatements) {
this.maxOpenPreparedStatements = maxOpenPreparedStatements;
}
public boolean isSharePreparedStatements() {
return sharePreparedStatements;
}
public void setSharePreparedStatements(boolean sharePreparedStatements) {
this.sharePreparedStatements = sharePreparedStatements;
}
public String getFilters() {
return filters;
}
public void setFilters(String filters) {
this.filters = filters;
}
}

View File

@@ -0,0 +1,33 @@
/**
* Copyright (c) 2018 人人开源 All rights reserved.
*
* https://www.renren.io
*
* 版权所有,侵权必究!
*/
package io.renren.commons.dynamic.datasource.properties;
import org.springframework.boot.context.properties.ConfigurationProperties;
import java.util.LinkedHashMap;
import java.util.Map;
/**
* 多数据源属性
*
* @author Mark sunlightcs@gmail.com
* @since 1.0.0
*/
@ConfigurationProperties(prefix = "dynamic")
public class DynamicDataSourceProperties {
private Map<String, DataSourceProperties> datasource = new LinkedHashMap<>();
public Map<String, DataSourceProperties> getDatasource() {
return datasource;
}
public void setDatasource(Map<String, DataSourceProperties> datasource) {
this.datasource = datasource;
}
}

135
renren-generator/pom.xml Normal file
View File

@@ -0,0 +1,135 @@
<?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>io.renren</groupId>
<artifactId>renren-generator</artifactId>
<version>5.2.0</version>
<packaging>jar</packaging>
<description>renren-generator</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.5</version>
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
<mybatis.spring.boot.version>2.2.2</mybatis.spring.boot.version>
<pagehelper.spring.boot.version>1.4.2</pagehelper.spring.boot.version>
<druid.version>1.2.14</druid.version>
<commons.lang.version>2.6</commons.lang.version>
<commons.io.version>2.11.0</commons.io.version>
<commons.configuration.version>1.10</commons.configuration.version>
<velocity.version>1.7</velocity.version>
<mssql.version>4.0</mssql.version>
<oracle.version>11.2.0.3</oracle.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>${mybatis.spring.boot.version}</version>
</dependency>
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper-spring-boot-starter</artifactId>
<version>${pagehelper.spring.boot.version}</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>${druid.version}</version>
</dependency>
<dependency>
<groupId>commons-lang</groupId>
<artifactId>commons-lang</artifactId>
<version>${commons.lang.version}</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>${commons.io.version}</version>
</dependency>
<dependency>
<groupId>commons-configuration</groupId>
<artifactId>commons-configuration</artifactId>
<version>${commons.configuration.version}</version>
</dependency>
<dependency>
<artifactId>velocity</artifactId>
<groupId>org.apache.velocity</groupId>
<version>${velocity.version}</version>
</dependency>
<!-- mysql驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!-- oracle驱动 -->
<dependency>
<groupId>com.oracle</groupId>
<artifactId>ojdbc6</artifactId>
<version>${oracle.version}</version>
</dependency>
<!-- sqlserver驱动 -->
<dependency>
<groupId>com.microsoft.sqlserver</groupId>
<artifactId>sqljdbc4</artifactId>
<version>${mssql.version}</version>
</dependency>
<!-- postgresql驱动 -->
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
<!-- 阿里云maven仓库 -->
<repositories>
<repository>
<id>public</id>
<name>aliyun nexus</name>
<url>https://maven.aliyun.com/repository/public/</url>
<releases>
<enabled>true</enabled>
</releases>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>public</id>
<name>aliyun nexus</name>
<url>https://maven.aliyun.com/repository/public/</url>
<releases>
<enabled>true</enabled>
</releases>
<snapshots>
<enabled>false</enabled>
</snapshots>
</pluginRepository>
</pluginRepositories>
</project>

View File

@@ -0,0 +1,25 @@
/**
* Copyright (c) 2018 人人开源 All rights reserved.
*
* https://www.renren.io
*
* 版权所有,侵权必究!
*/
package io.renren;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
* renren-generator
*
* @author Mark sunlightcs@gmail.com
*/
@SpringBootApplication
public class GeneratorApplication {
public static void main(String[] args) {
SpringApplication.run(GeneratorApplication.class, args);
}
}

View File

@@ -0,0 +1,52 @@
/**
* Copyright (c) 2018 人人开源 All rights reserved.
*
* https://www.renren.io
*
* 版权所有,侵权必究!
*/
package io.renren.config;
import io.renren.dao.*;
import io.renren.utils.RenException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
/**
* 数据库配置
*
* @author Mark sunlightcs@gmail.com
*/
@Configuration
public class DbConfig {
@Value("${renren.database: mysql}")
private String database;
@Autowired
private MySQLGeneratorDao mySQLGeneratorDao;
@Autowired
private OracleGeneratorDao oracleGeneratorDao;
@Autowired
private SQLServerGeneratorDao sqlServerGeneratorDao;
@Autowired
private PostgreSQLGeneratorDao postgreSQLGeneratorDao;
@Bean
@Primary
public GeneratorDao getGeneratorDao(){
if("mysql".equalsIgnoreCase(database)){
return mySQLGeneratorDao;
}else if("oracle".equalsIgnoreCase(database)){
return oracleGeneratorDao;
}else if("sqlserver".equalsIgnoreCase(database)){
return sqlServerGeneratorDao;
}else if("postgresql".equalsIgnoreCase(database)){
return postgreSQLGeneratorDao;
}else {
throw new RenException("不支持当前数据库:" + database);
}
}
}

View File

@@ -0,0 +1,62 @@
/**
* Copyright (c) 2018 人人开源 All rights reserved.
*
* https://www.renren.io
*
* 版权所有,侵权必究!
*/
package io.renren.controller;
import io.renren.service.SysGeneratorService;
import io.renren.utils.PageUtils;
import io.renren.utils.Query;
import io.renren.utils.R;
import org.apache.commons.io.IOUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Map;
/**
* 代码生成器
*
* @author Mark sunlightcs@gmail.com
*/
@Controller
@RequestMapping("/sys/generator")
public class SysGeneratorController {
@Autowired
private SysGeneratorService sysGeneratorService;
/**
* 列表
*/
@ResponseBody
@RequestMapping("/list")
public R list(@RequestParam Map<String, Object> params){
PageUtils pageUtil = sysGeneratorService.queryList(new Query(params));
return R.ok().put("page", pageUtil);
}
/**
* 生成代码
*/
@RequestMapping("/code")
public void code(String tables, HttpServletResponse response) throws IOException{
byte[] data = sysGeneratorService.generatorCode(tables.split(","));
response.reset();
response.setHeader("Content-Disposition", "attachment; filename=\"renren.zip\"");
response.addHeader("Content-Length", "" + data.length);
response.setContentType("application/octet-stream; charset=UTF-8");
IOUtils.write(data, response.getOutputStream());
}
}

View File

@@ -0,0 +1,25 @@
/**
* Copyright (c) 2018 人人开源 All rights reserved.
*
* https://www.renren.io
*
* 版权所有,侵权必究!
*/
package io.renren.dao;
import java.util.List;
import java.util.Map;
/**
* 数据库接口
*
* @author Mark sunlightcs@gmail.com
*/
public interface GeneratorDao {
List<Map<String, Object>> queryList(Map<String, Object> map);
Map<String, String> queryTable(String tableName);
List<Map<String, String>> queryColumns(String tableName);
}

View File

@@ -0,0 +1,23 @@
/**
* Copyright (c) 2018 人人开源 All rights reserved.
*
* https://www.renren.io
*
* 版权所有,侵权必究!
*/
package io.renren.dao;
import org.apache.ibatis.annotations.Mapper;
/**
* MySQL代码生成器
*
* @author Mark sunlightcs@gmail.com
*/
@Mapper
public interface MySQLGeneratorDao extends GeneratorDao {
}

View File

@@ -0,0 +1,21 @@
/**
* Copyright (c) 2018 人人开源 All rights reserved.
*
* https://www.renren.io
*
* 版权所有,侵权必究!
*/
package io.renren.dao;
import org.apache.ibatis.annotations.Mapper;
/**
* Oracle代码生成器
*
* @author Mark sunlightcs@gmail.com
*/
@Mapper
public interface OracleGeneratorDao extends GeneratorDao {
}

View File

@@ -0,0 +1,21 @@
/**
* Copyright (c) 2018 人人开源 All rights reserved.
*
* https://www.renren.io
*
* 版权所有,侵权必究!
*/
package io.renren.dao;
import org.apache.ibatis.annotations.Mapper;
/**
* PostgreSQL代码生成器
*
* @author Mark sunlightcs@gmail.com
*/
@Mapper
public interface PostgreSQLGeneratorDao extends GeneratorDao {
}

View File

@@ -0,0 +1,21 @@
/**
* Copyright (c) 2018 人人开源 All rights reserved.
*
* https://www.renren.io
*
* 版权所有,侵权必究!
*/
package io.renren.dao;
import org.apache.ibatis.annotations.Mapper;
/**
* SQLServer代码生成器
*
* @author Mark sunlightcs@gmail.com
*/
@Mapper
public interface SQLServerGeneratorDao extends GeneratorDao {
}

View File

@@ -0,0 +1,75 @@
/**
* Copyright (c) 2018 人人开源 All rights reserved.
*
* https://www.renren.io
*
* 版权所有,侵权必究!
*/
package io.renren.entity;
/**
* 列的属性
*
* @author Mark sunlightcs@gmail.com
*/
public class ColumnEntity {
//列名
private String columnName;
//列名类型
private String dataType;
//列名备注
private String comments;
//属性名称(第一个字母大写)user_name => UserName
private String attrName;
//属性名称(第一个字母小写)user_name => userName
private String attrname;
//属性类型
private String attrType;
//auto_increment
private String extra;
public String getColumnName() {
return columnName;
}
public void setColumnName(String columnName) {
this.columnName = columnName;
}
public String getDataType() {
return dataType;
}
public void setDataType(String dataType) {
this.dataType = dataType;
}
public String getComments() {
return comments;
}
public void setComments(String comments) {
this.comments = comments;
}
public String getAttrname() {
return attrname;
}
public void setAttrname(String attrname) {
this.attrname = attrname;
}
public String getAttrName() {
return attrName;
}
public void setAttrName(String attrName) {
this.attrName = attrName;
}
public String getAttrType() {
return attrType;
}
public void setAttrType(String attrType) {
this.attrType = attrType;
}
public String getExtra() {
return extra;
}
public void setExtra(String extra) {
this.extra = extra;
}
}

View File

@@ -0,0 +1,69 @@
/**
* Copyright (c) 2018 人人开源 All rights reserved.
*
* https://www.renren.io
*
* 版权所有,侵权必究!
*/
package io.renren.entity;
import java.util.List;
/**
* 表数据
*
* @author Mark sunlightcs@gmail.com
*/
public class TableEntity {
//表的名称
private String tableName;
//表的备注
private String comments;
//表的主键
private ColumnEntity pk;
//表的列名(不包含主键)
private List<ColumnEntity> columns;
//类名(第一个字母大写)sys_user => SysUser
private String className;
//类名(第一个字母小写)sys_user => sysUser
private String classname;
public String getTableName() {
return tableName;
}
public void setTableName(String tableName) {
this.tableName = tableName;
}
public String getComments() {
return comments;
}
public void setComments(String comments) {
this.comments = comments;
}
public ColumnEntity getPk() {
return pk;
}
public void setPk(ColumnEntity pk) {
this.pk = pk;
}
public List<ColumnEntity> getColumns() {
return columns;
}
public void setColumns(List<ColumnEntity> columns) {
this.columns = columns;
}
public String getClassName() {
return className;
}
public void setClassName(String className) {
this.className = className;
}
public String getClassname() {
return classname;
}
public void setClassname(String classname) {
this.classname = classname;
}
}

View File

@@ -0,0 +1,66 @@
/**
* Copyright (c) 2018 人人开源 All rights reserved.
*
* https://www.renren.io
*
* 版权所有,侵权必究!
*/
package io.renren.service;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import io.renren.dao.GeneratorDao;
import io.renren.utils.GenUtils;
import io.renren.utils.PageUtils;
import io.renren.utils.Query;
import org.apache.commons.io.IOUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.io.ByteArrayOutputStream;
import java.util.List;
import java.util.Map;
import java.util.zip.ZipOutputStream;
/**
* 代码生成器
*
* @author Mark sunlightcs@gmail.com
*/
@Service
public class SysGeneratorService {
@Autowired
private GeneratorDao generatorDao;
public PageUtils queryList(Query query) {
Page<?> page = PageHelper.startPage(query.getPage(), query.getLimit());
List<Map<String, Object>> list = generatorDao.queryList(query);
return new PageUtils(list, (int)page.getTotal(), query.getLimit(), query.getPage());
}
public Map<String, String> queryTable(String tableName) {
return generatorDao.queryTable(tableName);
}
public List<Map<String, String>> queryColumns(String tableName) {
return generatorDao.queryColumns(tableName);
}
public byte[] generatorCode(String[] tableNames) {
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
ZipOutputStream zip = new ZipOutputStream(outputStream);
for(String tableName : tableNames){
//查询表信息
Map<String, String> table = queryTable(tableName);
//查询列信息
List<Map<String, String>> columns = queryColumns(tableName);
//生成代码
GenUtils.generatorCode(table, columns, zip);
}
IOUtils.closeQuietly(zip);
return outputStream.toByteArray();
}
}

View File

@@ -0,0 +1,77 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.renren.utils;
/**
* <p>
* 断言类
* </p>
*
* @author miemie
* @since 2018-07-24
*/
public final class Assert {
/**
* 断言这个 boolean 为 true
* 为 false 则抛出异常
*
* @param expression boolean 值
* @param message 消息
*/
public static void isTrue(boolean expression, String message, Object... params) {
if (!expression) {
return;
}
}
/**
* 断言这个 boolean 为 false
* 为 true 则抛出异常
*
* @param expression boolean 值
* @param message 消息
*/
public static void isFalse(boolean expression, String message, Object... params) {
isTrue(!expression, message, params);
}
/**
* 断言这个 object 为 null
* 不为 null 则抛异常
*
* @param object 对象
* @param message 消息
*/
public static void isNull(Object object, String message, Object... params) {
isTrue(object == null, message, params);
}
/**
* 断言这个 object 不为 null
* 为 null 则抛异常
*
* @param object 对象
* @param message 消息
*/
public static void notNull(Object object, String message, Object... params) {
isTrue(object != null, message, params);
}
}

View File

@@ -0,0 +1,36 @@
/**
* Copyright (c) 2018 人人开源 All rights reserved.
*
* https://www.renren.io
*
* 版权所有,侵权必究!
*/
package io.renren.utils;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
* 日期处理
*
* @author Mark sunlightcs@gmail.com
*/
public class DateUtils {
/** 时间格式(yyyy-MM-dd) */
public final static String DATE_PATTERN = "yyyy-MM-dd";
/** 时间格式(yyyy-MM-dd HH:mm:ss) */
public final static String DATE_TIME_PATTERN = "yyyy-MM-dd HH:mm:ss";
public static String format(Date date) {
return format(date, DATE_PATTERN);
}
public static String format(Date date, String pattern) {
if(date != null){
SimpleDateFormat df = new SimpleDateFormat(pattern);
return df.format(date);
}
return null;
}
}

View File

@@ -0,0 +1,254 @@
/**
* Copyright (c) 2018 人人开源 All rights reserved.
*
* https://www.renren.io
*
* 版权所有,侵权必究!
*/
package io.renren.utils;
import io.renren.entity.ColumnEntity;
import io.renren.entity.TableEntity;
import org.apache.commons.configuration.Configuration;
import org.apache.commons.configuration.ConfigurationException;
import org.apache.commons.configuration.PropertiesConfiguration;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.WordUtils;
import org.apache.velocity.Template;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.Velocity;
import java.io.File;
import java.io.IOException;
import java.io.StringWriter;
import java.util.*;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
/**
* 代码生成器 工具类
*
* @author Mark sunlightcs@gmail.com
*/
public class GenUtils {
public static List<String> getTemplates(){
List<String> templates = new ArrayList<String>();
templates.add("template/DTO.java.vm");
templates.add("template/Entity.java.vm");
templates.add("template/Dao.java.vm");
templates.add("template/Dao.xml.vm");
templates.add("template/Service.java.vm");
templates.add("template/ServiceImpl.java.vm");
templates.add("template/Controller.java.vm");
templates.add("template/Excel.java.vm");
templates.add("template/index.vue.vm");
templates.add("template/add-or-update.vue.vm");
templates.add("template/mysql.vm");
templates.add("template/sqlserver.vm");
templates.add("template/oracle.vm");
templates.add("template/postgresql.vm");
return templates;
}
/**
* 生成代码
*/
public static void generatorCode(Map<String, String> table,
List<Map<String, String>> columns, ZipOutputStream zip){
//配置信息
Configuration config = getConfig();
boolean hasBigDecimal = false;
//表信息
TableEntity tableEntity = new TableEntity();
tableEntity.setTableName(table.get("tableName"));
tableEntity.setComments(table.get("tableComment"));
//表名转换成Java类名
String className = tableToJava(tableEntity.getTableName(), config.getString("tablePrefix"));
tableEntity.setClassName(className);
tableEntity.setClassname(StringUtils.uncapitalize(className));
//列信息
List<ColumnEntity> columnsList = new ArrayList<>();
for(Map<String, String> column : columns){
ColumnEntity columnEntity = new ColumnEntity();
columnEntity.setColumnName(column.get("columnName"));
columnEntity.setDataType(column.get("dataType"));
columnEntity.setComments(column.get("columnComment"));
columnEntity.setExtra(column.get("extra"));
//列名转换成Java属性名
String attrName = columnToJava(columnEntity.getColumnName());
columnEntity.setAttrName(attrName);
columnEntity.setAttrname(StringUtils.uncapitalize(attrName));
//列的数据类型转换成Java类型
String attrType = config.getString(columnEntity.getDataType(), "unknowType");
columnEntity.setAttrType(attrType);
if (!hasBigDecimal && attrType.equals("BigDecimal" )) {
hasBigDecimal = true;
}
//是否主键
if("PRI".equalsIgnoreCase(column.get("columnKey")) && tableEntity.getPk() == null){
tableEntity.setPk(columnEntity);
}
columnsList.add(columnEntity);
}
tableEntity.setColumns(columnsList);
//没主键,则第一个字段为主键
if(tableEntity.getPk() == null){
tableEntity.setPk(tableEntity.getColumns().get(0));
}
//设置velocity资源加载器
Properties prop = new Properties();
prop.put("file.resource.loader.class", "org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader");
Velocity.init(prop);
//封装模板数据
Map<String, Object> map = new HashMap<>();
map.put("tableName", tableEntity.getTableName());
map.put("comments", tableEntity.getComments());
map.put("pk", tableEntity.getPk());
map.put("className", tableEntity.getClassName());
map.put("classname", tableEntity.getClassname());
map.put("pathName", tableEntity.getClassname().toLowerCase());
map.put("columns", tableEntity.getColumns());
map.put("hasBigDecimal", hasBigDecimal);
map.put("version", config.getString("version" ));
map.put("package", config.getString("package" ));
map.put("moduleName", config.getString("moduleName" ));
map.put("author", config.getString("author"));
map.put("email", config.getString("email"));
map.put("datetime", DateUtils.format(new Date(), DateUtils.DATE_TIME_PATTERN));
map.put("date", DateUtils.format(new Date(), DateUtils.DATE_PATTERN));
for(int i=0; i<=10; i++){
map.put("id"+i, IdWorker.getId());
}
VelocityContext context = new VelocityContext(map);
//获取模板列表
List<String> templates = getTemplates();
for(String template : templates){
//渲染模板
StringWriter sw = new StringWriter();
Template tpl = Velocity.getTemplate(template, "UTF-8");
tpl.merge(context, sw);
try {
//添加到zip
zip.putNextEntry(new ZipEntry(getFileName(template, tableEntity.getClassName(), config.getString("package"), config.getString("moduleName"))));
IOUtils.write(sw.toString(), zip, "UTF-8");
IOUtils.closeQuietly(sw);
zip.closeEntry();
} catch (IOException e) {
throw new RenException("渲染模板失败,表名:" + tableEntity.getTableName(), e);
}
}
}
/**
* 列名转换成Java属性名
*/
public static String columnToJava(String columnName) {
return WordUtils.capitalizeFully(columnName, new char[]{'_'}).replace("_", "");
}
/**
* 表名转换成Java类名
*/
public static String tableToJava(String tableName, String tablePrefix) {
if(StringUtils.isNotBlank(tablePrefix)){
tableName = tableName.startsWith(tablePrefix) ? tableName.replaceFirst(tablePrefix, "") : tableName;
}
return columnToJava(tableName);
}
/**
* 获取配置信息
*/
public static Configuration getConfig(){
try {
return new PropertiesConfiguration("generator.properties");
} catch (ConfigurationException e) {
throw new RenException("获取配置文件失败,", e);
}
}
/**
* 获取文件名
*/
public static String getFileName(String template, String className, String packageName, String moduleName) {
String packagePath = "main" + File.separator + "java" + File.separator;
if (StringUtils.isNotBlank(packageName)) {
packagePath += packageName.replace(".", File.separator) + File.separator + "modules" + File.separator + moduleName + File.separator;
}
if (template.contains("Entity.java.vm" )) {
return packagePath + "entity" + File.separator + className + "Entity.java";
}
if (template.contains("Excel.java.vm" )) {
return packagePath + "excel" + File.separator + className + "Excel.java";
}
if (template.contains("Dao.java.vm" )) {
return packagePath + "dao" + File.separator + className + "Dao.java";
}
if (template.contains("Service.java.vm" )) {
return packagePath + "service" + File.separator + className + "Service.java";
}
if (template.contains("ServiceImpl.java.vm" )) {
return packagePath + "service" + File.separator + "impl" + File.separator + className + "ServiceImpl.java";
}
if (template.contains("Controller.java.vm" )) {
return packagePath + "controller" + File.separator + className + "Controller.java";
}
if (template.contains("Dao.xml.vm" )) {
return "main" + File.separator + "resources" + File.separator + "mapper" + File.separator + moduleName + File.separator + className + "Dao.xml";
}
if (template.contains("DTO.java.vm" )) {
return packagePath + "dto" + File.separator + className + "DTO.java";
}
if (template.contains("index.vue.vm" )) {
return "vue" + File.separator + "views" + File.separator + "modules" +
File.separator + moduleName + File.separator + className.toLowerCase() + ".vue";
}
if (template.contains("add-or-update.vue.vm" )) {
return "vue" + File.separator + "views" + File.separator + "modules" +
File.separator + moduleName + File.separator + className.toLowerCase() + "-add-or-update.vue";
}
if (template.contains("mysql.vm" )) {
return className.toLowerCase() + ".mysql.sql";
}
if (template.contains("oracle.vm" )) {
return className.toLowerCase() + ".oracle.sql";
}
if (template.contains("sqlserver.vm" )) {
return className.toLowerCase() + ".sqlserver.sql";
}
if (template.contains("postgresql.vm" )) {
return className.toLowerCase() + ".postgresql.sql";
}
return null;
}
}

View File

@@ -0,0 +1,67 @@
/*
* Copyright (c) 2011-2020, hubin (jobob@qq.com).
* <p>
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package io.renren.utils;
import java.util.UUID;
import java.util.concurrent.ThreadLocalRandom;
/**
* <p>
* 高效GUID产生算法(sequence),基于Snowflake实现64位自增ID算法。 <br>
* 优化开源项目 http://git.oschina.net/yu120/sequence
* </p>
*
* @author hubin
* @since 2016-08-01
*/
public class IdWorker {
/**
* 主机和进程的机器码
*/
private static Sequence WORKER = new Sequence();
public static long getId() {
return WORKER.nextId();
}
public static String getIdStr() {
return String.valueOf(WORKER.nextId());
}
/**
* <p>
* 有参构造器
* </p>
*
* @param workerId 工作机器 ID
* @param datacenterId 序列号
*/
public static void initSequence(long workerId, long datacenterId) {
WORKER = new Sequence(workerId, datacenterId);
}
/**
* <p>
* 使用ThreadLocalRandom获取UUID获取更优的效果 去掉"-"
* </p>
*/
public static String get32UUID() {
ThreadLocalRandom random = ThreadLocalRandom.current();
return new UUID(random.nextLong(), random.nextLong()).toString().replace(StringPool.DASH, StringPool.EMPTY);
}
}

View File

@@ -0,0 +1,87 @@
/**
* Copyright (c) 2018 人人开源 All rights reserved.
*
* https://www.renren.io
*
* 版权所有,侵权必究!
*/
package io.renren.utils;
import java.io.Serializable;
import java.util.List;
/**
* 分页工具类
*
* @author Mark sunlightcs@gmail.com
*/
public class PageUtils implements Serializable {
private static final long serialVersionUID = 1L;
//总记录数
private int totalCount;
//每页记录数
private int pageSize;
//总页数
private int totalPage;
//当前页数
private int currPage;
//列表数据
private List<?> list;
/**
* 分页
* @param list 列表数据
* @param totalCount 总记录数
* @param pageSize 每页记录数
* @param currPage 当前页数
*/
public PageUtils(List<?> list, int totalCount, int pageSize, int currPage) {
this.list = list;
this.totalCount = totalCount;
this.pageSize = pageSize;
this.currPage = currPage;
this.totalPage = (int)Math.ceil((double)totalCount/pageSize);
}
public int getTotalCount() {
return totalCount;
}
public void setTotalCount(int totalCount) {
this.totalCount = totalCount;
}
public int getPageSize() {
return pageSize;
}
public void setPageSize(int pageSize) {
this.pageSize = pageSize;
}
public int getTotalPage() {
return totalPage;
}
public void setTotalPage(int totalPage) {
this.totalPage = totalPage;
}
public int getCurrPage() {
return currPage;
}
public void setCurrPage(int currPage) {
this.currPage = currPage;
}
public List<?> getList() {
return list;
}
public void setList(List<?> list) {
this.list = list;
}
}

View File

@@ -0,0 +1,53 @@
/**
* Copyright (c) 2018 人人开源 All rights reserved.
*
* https://www.renren.io
*
* 版权所有,侵权必究!
*/
package io.renren.utils;
import java.util.LinkedHashMap;
import java.util.Map;
/**
* 查询参数
*
* @author Mark sunlightcs@gmail.com
*/
public class Query extends LinkedHashMap<String, Object> {
private static final long serialVersionUID = 1L;
//当前页码
private int page;
//每页条数
private int limit;
public Query(Map<String, Object> params){
this.putAll(params);
//分页参数
this.page = Integer.parseInt(params.get("page").toString());
this.limit = Integer.parseInt(params.get("limit").toString());
this.put("offset", (page - 1) * limit);
this.put("page", page);
this.put("limit", limit);
}
public int getPage() {
return page;
}
public void setPage(int page) {
this.page = page;
}
public int getLimit() {
return limit;
}
public void setLimit(int limit) {
this.limit = limit;
}
}

View File

@@ -0,0 +1,62 @@
/**
* Copyright (c) 2018 人人开源 All rights reserved.
*
* https://www.renren.io
*
* 版权所有,侵权必究!
*/
package io.renren.utils;
import java.util.HashMap;
import java.util.Map;
/**
* 返回数据
*
* @author Mark sunlightcs@gmail.com
*/
public class R extends HashMap<String, Object> {
private static final long serialVersionUID = 1L;
public R() {
put("code", 0);
}
public static R error() {
return error(500, "未知异常,请联系管理员");
}
public static R error(String msg) {
return error(500, msg);
}
public static R error(int code, String msg) {
R r = new R();
r.put("code", code);
r.put("msg", msg);
return r;
}
public static R ok(String msg) {
R r = new R();
r.put("msg", msg);
return r;
}
public static R ok(Map<String, Object> map) {
R r = new R();
r.putAll(map);
return r;
}
public static R ok() {
return new R();
}
@Override
public R put(String key, Object value) {
super.put(key, value);
return this;
}
}

View File

@@ -0,0 +1,61 @@
/**
* Copyright (c) 2018 人人开源 All rights reserved.
*
* https://www.renren.io
*
* 版权所有,侵权必究!
*/
package io.renren.utils;
/**
* 自定义异常
*
* @author Mark sunlightcs@gmail.com
*/
public class RenException extends RuntimeException {
private static final long serialVersionUID = 1L;
private String msg;
private int code = 500;
public RenException(String msg) {
super(msg);
this.msg = msg;
}
public RenException(String msg, Throwable e) {
super(msg, e);
this.msg = msg;
}
public RenException(String msg, int code) {
super(msg);
this.msg = msg;
this.code = code;
}
public RenException(String msg, int code, Throwable e) {
super(msg, e);
this.msg = msg;
this.code = code;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
public int getCode() {
return code;
}
public void setCode(int code) {
this.code = code;
}
}

View File

@@ -0,0 +1,42 @@
/**
* Copyright (c) 2018 人人开源 All rights reserved.
*
* https://www.renren.io
*
* 版权所有,侵权必究!
*/
package io.renren.utils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
/**
* 异常处理器
*
* @author Mark sunlightcs@gmail.com
*/
@RestControllerAdvice
public class RenExceptionHandler {
private Logger logger = LoggerFactory.getLogger(getClass());
/**
* 处理自定义异常
*/
@ExceptionHandler(RenException.class)
public R handleRenException(RenException e){
R r = new R();
r.put("code", e.getCode());
r.put("msg", e.getMessage());
return r;
}
@ExceptionHandler(Exception.class)
public R handleException(Exception e){
logger.error(e.getMessage(), e);
return R.error();
}
}

View File

@@ -0,0 +1,203 @@
/*
* Copyright (c) 2011-2020, hubin (jobob@qq.com).
* <p>
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package io.renren.utils;
import org.apache.commons.lang.StringUtils;
import org.apache.ibatis.logging.Log;
import org.apache.ibatis.logging.LogFactory;
import java.lang.management.ManagementFactory;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.util.concurrent.ThreadLocalRandom;
/**
* <p>
* 分布式高效有序ID生产黑科技(sequence) <br>
* 优化开源项目http://git.oschina.net/yu120/sequence
* </p>
*
* @author hubin
* @since 2016-08-18
*/
public class Sequence {
private static final Log logger = LogFactory.getLog(Sequence.class);
/**
* 时间起始标记点,作为基准,一般取系统的最近时间(一旦确定不能变动)
*/
private final long twepoch = 1288834974657L;
/**
* 机器标识位数
*/
private final long workerIdBits = 5L;
private final long datacenterIdBits = 5L;
private final long maxWorkerId = -1L ^ (-1L << workerIdBits);
private final long maxDatacenterId = -1L ^ (-1L << datacenterIdBits);
/**
* 毫秒内自增位
*/
private final long sequenceBits = 12L;
private final long workerIdShift = sequenceBits;
private final long datacenterIdShift = sequenceBits + workerIdBits;
/**
* 时间戳左移动位
*/
private final long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits;
private final long sequenceMask = -1L ^ (-1L << sequenceBits);
private final long workerId;
/**
* 数据标识 ID 部分
*/
private final long datacenterId;
/**
* 并发控制
*/
private long sequence = 0L;
/**
* 上次生产 ID 时间戳
*/
private long lastTimestamp = -1L;
public Sequence() {
this.datacenterId = getDatacenterId(maxDatacenterId);
this.workerId = getMaxWorkerId(datacenterId, maxWorkerId);
}
/**
* <p>
* 有参构造器
* </p>
*
* @param workerId 工作机器 ID
* @param datacenterId 序列号
*/
public Sequence(long workerId, long datacenterId) {
Assert.isFalse(workerId > maxWorkerId || workerId < 0,
String.format("worker Id can't be greater than %d or less than 0", maxWorkerId));
Assert.isFalse(datacenterId > maxDatacenterId || datacenterId < 0,
String.format("datacenter Id can't be greater than %d or less than 0", maxDatacenterId));
this.workerId = workerId;
this.datacenterId = datacenterId;
}
/**
* <p>
* 获取 maxWorkerId
* </p>
*/
protected static long getMaxWorkerId(long datacenterId, long maxWorkerId) {
StringBuilder mpid = new StringBuilder();
mpid.append(datacenterId);
String name = ManagementFactory.getRuntimeMXBean().getName();
if (StringUtils.isNotEmpty(name)) {
/*
* GET jvmPid
*/
mpid.append(name.split(StringPool.AT)[0]);
}
/*
* MAC + PID 的 hashcode 获取16个低位
*/
return (mpid.toString().hashCode() & 0xffff) % (maxWorkerId + 1);
}
/**
* <p>
* 数据标识id部分
* </p>
*/
protected static long getDatacenterId(long maxDatacenterId) {
long id = 0L;
try {
InetAddress ip = InetAddress.getLocalHost();
NetworkInterface network = NetworkInterface.getByInetAddress(ip);
if (network == null) {
id = 1L;
} else {
byte[] mac = network.getHardwareAddress();
if (null != mac) {
id = ((0x000000FF & (long) mac[mac.length - 1]) | (0x0000FF00 & (((long) mac[mac.length - 2]) << 8))) >> 6;
id = id % (maxDatacenterId + 1);
}
}
} catch (Exception e) {
logger.warn(" getDatacenterId: " + e.getMessage());
}
return id;
}
/**
* 获取下一个ID
*
* @return
*/
public synchronized long nextId() {
long timestamp = timeGen();
//闰秒
if (timestamp < lastTimestamp) {
long offset = lastTimestamp - timestamp;
if (offset <= 5) {
try {
wait(offset << 1);
timestamp = timeGen();
if (timestamp < lastTimestamp) {
throw new RuntimeException(String.format("Clock moved backwards. Refusing to generate id for %d milliseconds", offset));
}
} catch (Exception e) {
throw new RuntimeException(e);
}
} else {
throw new RuntimeException(String.format("Clock moved backwards. Refusing to generate id for %d milliseconds", offset));
}
}
if (lastTimestamp == timestamp) {
// 相同毫秒内,序列号自增
sequence = (sequence + 1) & sequenceMask;
if (sequence == 0) {
// 同一毫秒的序列数已经达到最大
timestamp = tilNextMillis(lastTimestamp);
}
} else {
// 不同毫秒内,序列号置为 1 - 3 随机数
sequence = ThreadLocalRandom.current().nextLong(1, 3);
}
lastTimestamp = timestamp;
// 时间戳部分 | 数据中心部分 | 机器标识部分 | 序列号部分
return ((timestamp - twepoch) << timestampLeftShift)
| (datacenterId << datacenterIdShift)
| (workerId << workerIdShift)
| sequence;
}
protected long tilNextMillis(long lastTimestamp) {
long timestamp = timeGen();
while (timestamp <= lastTimestamp) {
timestamp = timeGen();
}
return timestamp;
}
protected long timeGen() {
return SystemClock.now();
}
}

View File

@@ -0,0 +1,95 @@
package io.renren.utils;
/**
* Copy to jodd.util
* <p>
* Pool of <code>String</code> constants to prevent repeating of
* hard-coded <code>String</code> literals in the code.
* Due to fact that these are <code>public static final</code>
* they will be inlined by java compiler and
* reference to this class will be dropped.
* There is <b>no</b> performance gain of using this pool.
* Read: http://java.sun.com/docs/books/jls/third_edition/html/lexical.html#3.10.5
* <ul>
* <li>Literal strings within the same class in the same package represent references to the same <code>String</code> object.</li>
* <li>Literal strings within different classes in the same package represent references to the same <code>String</code> object.</li>
* <li>Literal strings within different classes in different packages likewise represent references to the same <code>String</code> object.</li>
* <li>Strings computed by constant expressions are computed at compile time and then treated as if they were literals.</li>
* <li>Strings computed by concatenation at run time are newly created and therefore distinct.</li>
* </ul>
*/
public interface StringPool {
String AMPERSAND = "&";
String AND = "and";
String AT = "@";
String ASTERISK = "*";
String STAR = ASTERISK;
String BACK_SLASH = "\\";
String COLON = ":";
String COMMA = ",";
String DASH = "-";
String DOLLAR = "$";
String DOT = ".";
String DOTDOT = "..";
String DOT_CLASS = ".class";
String DOT_JAVA = ".java";
String DOT_XML = ".xml";
String EMPTY = "";
String EQUALS = "=";
String FALSE = "false";
String SLASH = "/";
String HASH = "#";
String HAT = "^";
String LEFT_BRACE = "{";
String LEFT_BRACKET = "(";
String LEFT_CHEV = "<";
String NEWLINE = "\n";
String N = "n";
String NO = "no";
String NULL = "null";
String OFF = "off";
String ON = "on";
String PERCENT = "%";
String PIPE = "|";
String PLUS = "+";
String QUESTION_MARK = "?";
String EXCLAMATION_MARK = "!";
String QUOTE = "\"";
String RETURN = "\r";
String TAB = "\t";
String RIGHT_BRACE = "}";
String RIGHT_BRACKET = ")";
String RIGHT_CHEV = ">";
String SEMICOLON = ";";
String SINGLE_QUOTE = "'";
String BACKTICK = "`";
String SPACE = " ";
String TILDA = "~";
String LEFT_SQ_BRACKET = "[";
String RIGHT_SQ_BRACKET = "]";
String TRUE = "true";
String UNDERSCORE = "_";
String UTF_8 = "UTF-8";
String US_ASCII = "US-ASCII";
String ISO_8859_1 = "ISO-8859-1";
String Y = "y";
String YES = "yes";
String ONE = "1";
String ZERO = "0";
String DOLLAR_LEFT_BRACE = "${";
String HASH_LEFT_BRACE = "#{";
String CRLF = "\r\n";
String HTML_NBSP = "&nbsp;";
String HTML_AMP = "&amp";
String HTML_QUOTE = "&quot;";
String HTML_LT = "&lt;";
String HTML_GT = "&gt;";
// ---------------------------------------------------------------- array
String[] EMPTY_ARRAY = new String[0];
byte[] BYTES_NEW_LINE = StringPool.NEWLINE.getBytes();
}

View File

@@ -0,0 +1,80 @@
/*
* Copyright (c) 2011-2020, hubin (jobob@qq.com).
* <p>
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package io.renren.utils;
import java.sql.Timestamp;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;
/**
* <p>
* 高并发场景下System.currentTimeMillis()的性能问题的优化
* </p>
* <p>
* System.currentTimeMillis()的调用比new一个普通对象要耗时的多具体耗时高出多少我还没测试过有人说是100倍左右<br>
* System.currentTimeMillis()之所以慢是因为去跟系统打了一次交道<br>
* 后台定时更新时钟JVM退出时线程自动回收<br>
* 10亿43410,206,210.72815533980582%<br>
* 1亿4699,29,162.0344827586207%<br>
* 1000万480,12,40.0%<br>
* 100万50,10,5.0%<br>
* </p>
*
* @author hubin
* @since 2016-08-01
*/
public class SystemClock {
private final long period;
private final AtomicLong now;
private SystemClock(long period) {
this.period = period;
this.now = new AtomicLong(System.currentTimeMillis());
scheduleClockUpdating();
}
private static SystemClock instance() {
return InstanceHolder.INSTANCE;
}
public static long now() {
return instance().currentTimeMillis();
}
public static String nowDate() {
return new Timestamp(instance().currentTimeMillis()).toString();
}
private void scheduleClockUpdating() {
ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor(runnable -> {
Thread thread = new Thread(runnable, "System Clock");
thread.setDaemon(true);
return thread;
});
scheduler.scheduleAtFixedRate(() -> now.set(System.currentTimeMillis()), period, period, TimeUnit.MILLISECONDS);
}
private long currentTimeMillis() {
return now.get();
}
private static class InstanceHolder {
public static final SystemClock INSTANCE = new SystemClock(1);
}
}