背景
有时候下载的源码没有数据库的DDL语句,需要手动去创建,这就很麻烦了,这时需要一个利器通过POJO对象生成DDL语句,一键生成,直接执行即可。
工程结构示例
pom.xml文件
<?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>org.example</groupId>
<artifactId>pojoToMysql</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>org.reflections</groupId>
<artifactId>reflections</artifactId>
<version>0.9.11</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.10</version>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>4.2.1</version>
</dependency>
</dependencies>
</project>
相关注解
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface FieldPlus {
boolean isExist() default true;
// 注释
String comment() default "";
// not null 标识
boolean isNotNull() default true;
String defaultValue() default "";
int length() default 0;
}
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Indexes {
/**
* 命名规则 1-索引类型 2-字段名称组合(以|分隔) 3-索引名称 以:分隔
* 示例 index:anchor_id|merchant_id:index_ac_mc
* unique:anchor_id|merchant_id:uq_ac_mc
* @return 索引数组
*/
String[] index() default "";
}
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface PrimaryKey {
String value() default "id";
}
相关的实体类
@Data
public class Entity {
private String tableName;
private List<Property> properties;
/**
* 索引
* primaryKey 主键
* uniqueIndex 唯一索引
* index 普通索引
*/
private List<Tuple> indexes;
}
@Data
public class Property {
/**
* 字段名字
*/
private String fieldName;
/**
* 字段定义
*/
private String fieldDefinition;
/**
* 注释
*/
private String comment;
/**
* 非空标识
*/
private boolean notNullSign;
}
扫描包的类
public class PackageScanner {
public List<Class<?>> scanPackage(String packageName) throws IOException, ClassNotFoundException {
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
String path = packageName.replace('.', '/');
Enumeration<URL> resources = classLoader.getResources(path);
List<Class<?>> classes = new ArrayList<>();
while (resources.hasMoreElements()) {
URL resource = resources.nextElement();
File file = new File(resource.getFile());
if (file.isDirectory()) {
for (File classFile : Objects.requireNonNull(file.listFiles())) {
if (classFile.getName().endsWith(".class")) {
String className = packageName + "." + classFile.getName().replace(".class", "");
Class<?> clazz = Class.forName(className);
classes.add(clazz);
}
}
}
}
return classes;
}
}
两个需要生成DDL的示例类
@PrimaryKey()
@Indexes(index = {"index:merchant_id|anchor_id:index_mc_ac","unique:id|anchor_id|merchant_id:uq_id_ac_mc"})
public class LiveInfo {
@FieldPlus(length = 20)
private Long id;
@FieldPlus(comment = "商户ID")
private Integer merchantId;
@FieldPlus(comment = "主播ID")
private Long anchorId;
@FieldPlus(comment = "房间地址", length = 256)
private String pullAddress;
private Integer liveType;
@FieldPlus(comment = "用户名称")
private String username;
@FieldPlus(comment = "开始时间")
private LocalDateTime createTime;
}
@PrimaryKey(value = "id")
public class UserInfo {
private Long id;
@FieldPlus(comment = "用户名称")
private String username;
private Date updateTime;
}
驼峰转下横杠工具类
public class StringUtils {
public static String toUnderScoreCase(String camelCaseStr) {
StringBuilder builder = new StringBuilder();
for (int i = 0; i < camelCaseStr.length(); i++) {
char c = camelCaseStr.charAt(i);
if (Character.isUpperCase(c)) {
if (i > 0) {
builder.append('_');
}
builder.append(Character.toLowerCase(c));
} else {
builder.append(c);
}
}
return builder.toString();
}
}
核心方法
public class Main {
public static void main(String[] args) throws IOException, ClassNotFoundException {
PackageScanner packageScanner = new PackageScanner();
// 获取pojo clazz文件
List<Class<?>> classes = packageScanner.scanPackage("org.example.pojo");
List<Entity> entityList = new ArrayList<>();
// 遍历完封装成对象
for (Class<?> clazz : classes) {
Entity entity = new Entity();
String substring = clazz.getName().substring(clazz.getName().lastIndexOf(".") + 1);
String tableName = StringUtils.toUnderScoreCase(substring);
entity.setTableName(tableName);
// 获取类上的索引
List<Tuple> indexList = new ArrayList<>();
PrimaryKey primaryKey = clazz.getAnnotation(PrimaryKey.class);
if (Objects.nonNull(primaryKey)){
//indexHashMap.put(primaryKey.value(), "primaryKey");
indexList.add(new Tuple("primaryKey", primaryKey.value()));
}
Indexes indexes = clazz.getAnnotation(Indexes.class);
if (Objects.nonNull(indexes)){
for (String index : indexes.index()) {
String[] split = index.split(":");
indexList.add(new Tuple(split[0], split[1], split[2]));
}
}
entity.setIndexes(indexList);
// 遍历属性
Field[] fields = clazz.getDeclaredFields();
List<Property> propertyList = new ArrayList<>();
for (Field field : fields) {
Property property = new Property();
String fieldName = StringUtils.toUnderScoreCase(field.getName());
property.setFieldName(fieldName);
String typeName = field.getType().getName();
//System.out.println("属性名" + name);
// 注解处理
FieldPlus fieldPlus = field.getAnnotation(FieldPlus.class);
if (Objects.equals("java.lang.String",typeName)){
if (Objects.nonNull(fieldPlus) && fieldPlus.length() > 0){
property.setFieldDefinition("varchar(" + fieldPlus.length() + ")");
} else {
property.setFieldDefinition("varchar(128)");
}
} else if (Objects.equals("java.lang.Long",typeName) || Objects.equals("java.lang.Integer",typeName)){
if (Objects.nonNull(fieldPlus) && fieldPlus.length() > 0 && fieldPlus.length() <= 3){
property.setFieldDefinition("tinyint(" + fieldPlus.length() + ")");
} else if (Objects.nonNull(fieldPlus) && fieldPlus.length() > 3 && fieldPlus.length() <= 10) {
property.setFieldDefinition("int(" + fieldPlus.length() + ")");
} else if (Objects.nonNull(fieldPlus) && fieldPlus.length() > 10) {
property.setFieldDefinition("bigint(" + fieldPlus.length() + ")");
} else {
property.setFieldDefinition("int(10)");
}
} else if (Objects.equals("java.time.LocalDateTime", typeName) || Objects.equals("java.util.Date", typeName)) {
property.setFieldDefinition("timestamp");
}
// 注解处理
if (Objects.nonNull(fieldPlus)){
property.setComment(Objects.nonNull(fieldPlus.comment()) && !("").equals(fieldPlus.comment())
? fieldPlus.comment() : null );
property.setNotNullSign(fieldPlus.isNotNull());
}
propertyList.add(property);
}
entity.setProperties(propertyList);
entityList.add(entity);
}
// 遍历对象生成DDL语句输出txt文档
FileOutputStream fos = new FileOutputStream("init.txt");
for (Entity entity : entityList) {
String beforeSql = "CREATE TABLE `{}` (";
String tableName = StrUtil.format(beforeSql, entity.getTableName());
fos.write(tableName.getBytes());
// 写入换行符(根据操作系统的不同,换行符可能会有所差异,Windows为"\r\n",Unix/Linux为"\n")
fos.write(System.getProperty("line.separator").getBytes());
// 填充字段
for (Property property : entity.getProperties()) {
String field = " " + property.getFieldName() + " " + property.getFieldDefinition();
if (property.isNotNullSign()){
field = field + " NOT NULL";
}
if (Objects.nonNull(property.getComment())){
field = field + " COMMENT '" + property.getComment() +"'";
}
field = field + ",";
fos.write(field.getBytes());
fos.write(System.getProperty("line.separator").getBytes());
}
if (entity.getIndexes() != null && entity.getIndexes().size() > 0){
for (int i = 0; i < entity.getIndexes().size(); i++) {
Tuple tuple = entity.getIndexes().get(i);
if ("primaryKey".equals(tuple.get(0))){
String primaryKeyFormat = " PRIMARY KEY (`{}`) USING BTREE,";
primaryKeyFormat = processEndComma(entity, i, primaryKeyFormat);
String primaryKey = StrUtil.format(primaryKeyFormat, (String)tuple.get(1));
fos.write(primaryKey.getBytes());
fos.write(System.getProperty("line.separator").getBytes());
} else if ("index".equals(tuple.get(0))){
String indexFormat = " KEY `{}` ({}) USING BTREE,";
indexFormat = processEndComma(entity, i, indexFormat);
String[] split = ((String) tuple.get(1)).split("\\|");
StringBuilder fieldArray = new StringBuilder();
for (String s : split) {
fieldArray.append("`").append(s).append("`").append(",");
}
String index = StrUtil.format(indexFormat, tuple.get(2), fieldArray.substring(0, fieldArray.length()-1));
fos.write(index.getBytes());
fos.write(System.getProperty("line.separator").getBytes());
} else if ("unique".equals(tuple.get(0))){
String uniqueIndexFormat = " UNIQUE KEY `{}` ({}) USING BTREE,";
uniqueIndexFormat = processEndComma(entity, i, uniqueIndexFormat);
String[] split = ((String) tuple.get(1)).split("\\|");
StringBuilder fieldArray = new StringBuilder();
for (String s : split) {
fieldArray.append("`").append(s).append("`").append(",");
}
String index = StrUtil.format(uniqueIndexFormat, tuple.get(2), fieldArray.substring(0, fieldArray.length()-1));
fos.write(index.getBytes());
fos.write(System.getProperty("line.separator").getBytes());
}
}
}
String endSql = ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;";
fos.write(endSql.getBytes());
fos.write(System.getProperty("line.separator").getBytes());
fos.write(System.getProperty("line.separator").getBytes());
}
fos.close();
}
private static String processEndComma(Entity entity, int i, String indexFormat) {
if (i == entity.getIndexes().size()-1){
indexFormat = indexFormat.replace(",","");
}
return indexFormat;
}
}
最后的成果
CREATE TABLE `live_info` (
id bigint(20) NOT NULL,
merchant_id int(10) NOT NULL COMMENT '商户ID',
anchor_id int(10) NOT NULL COMMENT '主播ID',
pull_address varchar(256) NOT NULL COMMENT '房间地址',
live_type int(10),
username varchar(128) NOT NULL COMMENT '用户名称',
create_time timestamp NOT NULL COMMENT '开始时间',
PRIMARY KEY (`id`) USING BTREE,
KEY `index_mc_ac` (`merchant_id`,`anchor_id`) USING BTREE,
UNIQUE KEY `uq_id_ac_mc` (`id`,`anchor_id`,`merchant_id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;
CREATE TABLE `user_info` (
id int(10),
username varchar(128) NOT NULL COMMENT '用户名称',
update_time timestamp,
PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;