三个 DAL 相关的Java代码小工具

时间:2023-03-09 00:17:15
三个 DAL 相关的Java代码小工具

  最近在做 DAL (Data Access Layer 数据访问层) 的服务化,发现有不少地方是人工编写比较繁琐的,因此写了几个小工具来完成。

  

  1.  从 DAO 类自动生成 CoreService 类, CoreService 直接调用 DAO 类

  思路: 通过正则表达式解析方法参数, 使用正则替换及源 DAO 文件来生成 CoreService 源文件。

package zzz.study.utils;

import cc.lovesq.dao.CreativeDAO;

import java.util.List;
import static zzz.study.utils.BaseTool.*; public class AutoGenerateCoreService { public static void main(String[] args) {
testParseMethod();
generateCoreServiceFile(CreativeDAO.class); } public static void generateCoreServiceFile(Class<?> daocls) { String daoClassName = daocls.getSimpleName();
String packageName = daocls.getPackage().getName();
String daoRelativePath = "/" + packageName.replaceAll("\\.", "/");
String daoFileName = ALLIN_PROJ_PATH_SRC + "/" + daoRelativePath +
"/" + daocls.getSimpleName() + ".java";
String bizType = getBizType(packageName); String writeFilename = ALLIN_PROJ_PATH_SRC + "/cc/lovesq/service/" + daoClassName.replace("DAO", "CoreService") + ".java";
String serviceClassName = daoClassName.replace("DAO", "CoreService");
String daoRefName = firstToLower(daoClassName); List<String> lines = readLines(daoFileName);
String fileContents = "";
boolean daoFlag = false;
for (String line: lines) { if (daoFlag) {
fileContents += "\n\t@Resource\n";
fileContents += "\tprivate " + daoClassName + " " + daoRefName + ";\n\n";
daoFlag = false;
}
else if (line.contains("interface")) {
fileContents += "@Component\npublic class " + serviceClassName + " { \n";
daoFlag = true;
}
else if (line.contains(";")) {
if (!line.contains("import") && !line.contains("package")) {
System.out.println(line);
System.out.println("parsed: " + parseMethod(line));
List<String> parsed = transform(parseMethod(line));
String replaceStr = " {\n\t\treturn " + daoRefName + "." + parsed.get(0) + "(" + parsed.get(1) + ");\n\t}\n";
String accessQualifier = "";
if (!line.contains("public")) {
accessQualifier = "public ";
}
fileContents += "\t" + accessQualifier + " " + line.trim().replace(";", replaceStr);
}
else if (line.contains("package")) {
System.out.println(line);
fileContents += line.replace("dao", "service") + "\n\n";
fileContents += "import " + daocls.getPackage().getName() + "." + daoClassName + ";\n";
fileContents += "import javax.annotation.Resource;\n" +
"import org.springframework.stereotype.Component;\n" +
"import java.util.List;";
}
else {
fileContents += line + "\n";
}
}
else {
fileContents += line + "\n";
}
} writeFile(writeFilename, fileContents);
} }

  2.  根据对应数据库的 DO 类生成业务 Model 类及转换类 DataTransfer

思路: 模板、反射、正则替换

package zzz.study.utils;

import cc.lovesq.pojo.CreativeDO;

import java.lang.reflect.Field;

import static zzz.study.utils.BaseTool.*;

/**
* Created by shuqin on 16/5/3.
*/
public class AutoGenerateDataTransfer { private static final String dataTransferTpl =
"package cc.lovesq.transfer;\n" +
"\n" +
"import cc.lovesq.model.$modelClsName;\n" +
"import cc.lovesq.pojo.$doClsName;\n\n" +
"public class $modelClsNameDataTransfer { \n\n" +
indent(4) + "$doToModelMethod\n\n" +
indent(4) + "$modelToDOMethod\n\n" +
"}"; private static final String doToModelTpl =
"public static $modelClsName transfer2TO($doClsName $doInstName) {\n" +
indent(8) + "if ($doInstName == null) {\n" +
indent(12) + "return null;\n" +
indent(8) + "}\n" +
"\n" +
indent(8) + "$modelClsName $modelInstName = new $modelClsName();\n" +
indent(0) + "$setStatement\n" +
indent(8) + "return $modelInstName;\n" +
indent(4) + "}" ; private static final String modelToDOTpl =
"public static $doClsName transfer2DO($modelClsName $modelInstName) {\n" +
indent(8) + "if ($modelInstName == null) {\n" +
indent(12) + "return null;\n" +
indent(8) + "}\n" +
"\n" +
indent(8) + "$doClsName $doInstName = new $doClsName();\n" +
indent(0) + "$setStatement\n" +
indent(8) + "return $doInstName;\n" +
indent(4) + "}"; public static void main(String[] args) { autoGenDataTransfer(CreativeDO.class);
} /**
* 根据 DO 类自动生成 Model 类以及 DataTransfer 类
* @param doCls DO 类
*/
public static void autoGenDataTransfer(Class<?> doCls) { String doClassName = doCls.getSimpleName();
Field[] fields = doCls.getDeclaredFields();
String doInstName = firstToLower(doClassName);
String modelInstName = strip(doInstName, "DO");
String modelClsName = firstToUpper(modelInstName);
StringBuilder setStatementAll = new StringBuilder();
for (Field f: fields) {
String fieldName = f.getName();
String setStatement = String.format("%s%s.set%s(%s.get%s());\n", indent(8),
modelInstName, firstToUpper(fieldName), doInstName, firstToUpper(fieldName));
setStatementAll.append(setStatement);
}
String setStatementAllStr = setStatementAll.toString();
String do2modelMethod = doToModelTpl.replaceAll("\\$modelClsName", modelClsName)
.replaceAll("\\$modelInstName", modelInstName)
.replaceAll("\\$doClsName", doClassName)
.replaceAll("\\$doInstName", doInstName)
.replaceAll("\\$setStatement", setStatementAllStr); StringBuilder setStatementAllBuilder2 = new StringBuilder();
for (Field f: fields) {
String fieldName = f.getName();
String setStatement = String.format("%s%s.set%s(%s.get%s());\n", indent(8),
doInstName, firstToUpper(fieldName), modelInstName, firstToUpper(fieldName));
setStatementAllBuilder2.append(setStatement);
}
String setStatementAll2 = setStatementAllBuilder2.toString();
String model2doMethod = modelToDOTpl.replaceAll("\\$modelClsName", modelClsName)
.replaceAll("\\$modelInstName", modelInstName)
.replaceAll("\\$doClsName", doClassName)
.replaceAll("\\$doInstName", doInstName)
.replaceAll("\\$setStatement", setStatementAll2); String packageName = doCls.getPackage().getName();
String bizType = getBizType(packageName);
String dataTransferClassContent = dataTransferTpl.replaceAll("\\$modelClsName", modelClsName)
.replaceAll("\\$doClsName", doClassName)
.replaceAll("\\$bizType", bizType)
.replaceAll("\\$doToModelMethod", do2modelMethod)
.replaceAll("\\$modelToDOMethod", model2doMethod); //System.out.println(dataTransferClassContent); String doClsRelativePath = "/cc/lovesq/pojo/" + doClassName + ".java";
String doClsPath = ALLIN_PROJ_PATH_SRC + doClsRelativePath;
String doClsContent = readFile(doClsPath); System.out.println(doClsContent);
String modelClsContent = doClsContent.replace(doClassName, modelClsName)
.replace("pojo", "model"); String modelClsRelativePath = "/cc/lovesq/model/" + modelClsName + ".java";
String modelClsPath = ALLIN_PROJ_PATH_SRC + modelClsRelativePath;
writeFile(modelClsPath, modelClsContent); String transferRelativePath = "/cc/lovesq/transfer";
String qualifiedPath = ALLIN_PROJ_PATH_SRC + transferRelativePath + "/";
System.out.println(dataTransferClassContent);
String writeFilePath = qualifiedPath + modelClsName + "DataTransfer.java";
System.out.println("Write: " + writeFilePath);
writeFile(writeFilePath, dataTransferClassContent);
} }

3.  移除指定类的 Javadoc 注释

  知识点: 多行的正则匹配, 正则替换; 非贪婪匹配。 注意到  (\\\/\*.*?\\*\\/) 里面有个问号, 如果没有这个问号, 这个正则会从第一个 /* 匹配到最后一个 */ ,相当于整个文件都匹配进去了,显然不是期望的。加上问号后,该正则是非贪婪匹配,每次只要匹配到 */ 就会结束此次匹配。

package zzz.study.utils;

import cc.lovesq.service.CreativeService;

import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern; import static zzz.study.utils.BaseTool.*; /**
* 移除指定类的 Javadoc 注释
* Created by shuqin on 16/5/4.
*/
public class RemoveJavadocComments { private static final String javadocRegexStr = "(\\\/\*.*?\\*\\/)";
private static final Pattern javadocPattern = Pattern.compile(javadocRegexStr, Pattern.CASE_INSENSITIVE | Pattern.DOTALL);

    public static void main(String[] args) {

        // 移除指定包下面的类 Javadoc 注释
String tradeDALPackage = ALLIN_PROJ_PATH_SRC + "/cc/lovesq/controller";
List<Class> classes = getClasses(tradeDALPackage);
for (Class c: classes) {
if (c.getSimpleName().endsWith("Controller")) {
removeJavadoc(c);
}
} // 移除单个类的 Javadoc 注释
removeJavadoc(CreativeService.class);
} public static void removeJavadoc(Class<?> coreServiceCls) { String coreServiceName = coreServiceCls.getSimpleName();
String packageName = coreServiceCls.getPackage().getName();
String packagePath = "/" + packageName.replaceAll("\\.", "/"); String coreServiceClsRelativePath = packagePath + "/" + coreServiceName + ".java";
String coreServiceClsPath = ALLIN_PROJ_PATH_SRC + coreServiceClsRelativePath;
String coreServiceContent = readFile(coreServiceClsPath); Matcher m = javadocPattern.matcher(coreServiceContent);
String newContent = coreServiceContent;
while(m.find()) {
String matchedJavadoc = coreServiceContent.substring(m.start(), m.end());
newContent = newContent.replace(matchedJavadoc, "");
}
newContent = newContent.replaceAll("\n\\s*\n", "\n\n");
writeFile(coreServiceClsPath, newContent);
} }

基本工具类:

  

package zzz.study.utils;

import java.io.*;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern; /**
* Created by shuqin on 16/5/4.
*/
public class BaseTool { public static final String ALLIN_PROJ_PATH = System.getProperty("user.dir");
public static final String ALLIN_PROJ_PATH_SRC = ALLIN_PROJ_PATH + "/src/main/java"; public static String strip(String origin, String toStrip) {
int index = origin.indexOf(toStrip);
if (index == -1) {
return origin;
}
else {
return origin.substring(0, index);
}
} public static String firstToLower(String doClassName) {
return "" + String.valueOf(doClassName.charAt(0)).toLowerCase() + doClassName.substring(1);
} public static String firstToUpper(String fieldName) {
return "" + String.valueOf(fieldName.charAt(0)).toUpperCase() + fieldName.substring(1);
} public static String getBizType(String packageName) {
int preIndex = packageName.indexOf("common.");
if (preIndex == -1) {
return "";
}
String bizPart = packageName.substring(preIndex+"common.".length());
int bizIndex = bizPart.indexOf(".");
if (bizIndex == -1) {
return "";
}
return bizPart.substring(0, bizIndex);
} public static String indent(int n) {
StringBuilder spaces = new StringBuilder();
for (int i=0; i<n; i++) {
spaces.append(' ');
}
return spaces.toString();
} public static String readFile(String filePath) {
String fileContent = "";
try {
BufferedReader reader = new BufferedReader(new FileReader(new File(filePath)));
StringBuilder doClsContentBuilder = new StringBuilder();
String line = "";
while ((line = reader.readLine()) != null) {
doClsContentBuilder.append(line + "\n");
}
fileContent = doClsContentBuilder.toString();
} catch (IOException ioe) {
System.err.println("Failed to Read File " + filePath + " : " + ioe.getMessage());
}
return fileContent;
} public static List<String> readLines(String filePath) {
List<String> lines = new ArrayList<String>();
try {
BufferedReader reader = new BufferedReader(new FileReader(new File(filePath)));
String line = "";
while ((line = reader.readLine()) != null) {
lines.add(line);
}
} catch (IOException ioe) {
System.err.println("Failed to Read File " + filePath + " : " + ioe.getMessage());
}
return lines;
} public static void writeFile(String filePath, String fileContent) {
try {
BufferedWriter modelFileW = new BufferedWriter(new FileWriter(new File(filePath)));
modelFileW.write(fileContent);
modelFileW.close();
} catch (IOException ioe) {
System.err.println("Failed to write Java File " + filePath + " : " + ioe.getMessage());
}
} public static List<String> fetchAllFiles(String path) {
List<String> fetchedFiles = new ArrayList<String>();
fetchFiles(path, fetchedFiles);
return fetchedFiles;
} public static void fetchFiles(String path, List<String> fetchedFiles) {
File[] dirAndfiles = (new File(path)).listFiles();
if (dirAndfiles!=null && dirAndfiles.length > 0) {
for (File file: dirAndfiles) {
if (file.isFile()) {
fetchedFiles.add(file.getAbsolutePath());
}
} for (File file: dirAndfiles) {
if (file.isDirectory()) {
fetchFiles(file.getAbsolutePath(), fetchedFiles);
}
}
}
} public static List<Class> getClasses(String path) {
List<String> files = fetchAllFiles(path);
List<Class> result = new ArrayList<Class>();
ClassLoader cld = Thread.currentThread().getContextClassLoader();
for (String fname: files) {
String fn = fname.replace(ALLIN_PROJ_PATH_SRC + "/", "").replace(".java", "");
String qualifiedClassName = fn.replaceAll("/", ".");
try {
Class<?> cls = cld.loadClass(qualifiedClassName);
result.add(cls);
} catch (ClassNotFoundException cnfe) {
System.err.println("Failed to load class " + qualifiedClassName + " : " + cnfe.getMessage());
} }
return result;
} public static final String methodNameRegexStr = "\\s*(?:\\w+\\s+)?\\w+<?\\w+>?\\s+(\\w+)";
public static final String singleParamRegexStr = "[^,]*\\w+<?\\w+>?\\s+(\\w+)\\s*";
public static final String simpleMethodSignRexStr = methodNameRegexStr + "\\(" + singleParamRegexStr + "\\)\\s*;\\s*";
public static final String twoParamMethodSignRegStr = methodNameRegexStr + "\\(" + singleParamRegexStr + "," + singleParamRegexStr + "\\);\\s*";
//val generalParamMethodSignRegStr = methodNameRegexStr + "\\((" + singleParamRegexStr + "(?:," + singleParamRegexStr + ")*)\\);\\s*";
public static final String generalParamMethodSignRegStr = methodNameRegexStr + "\\((.*)\\);\\s*"; public static final Pattern singleParamPattern = Pattern.compile(singleParamRegexStr);
public static final Pattern generalParamMethodSignPattern = Pattern.compile(generalParamMethodSignRegStr); /**
* 从方法签名中解析出方法名称\参数列表
* @param methodSign 方法签名
* @return ["方法名称", "参数1, 参数2, ..., 参数N"]
*/
public static List<String> parseMethod(String methodSign) { Matcher m = generalParamMethodSignPattern.matcher(methodSign);
String methodName = "";
String args = "";
List<String> parsed = new ArrayList<String>(); if (m.find()) {
methodName = m.group(1);
args = m.group(2);
}
else {
return Arrays.asList(new String[]{"", ""});
}
parsed.add(methodName);
String[] params = args.split(",");
for (String param: params) {
String arg = extractArgName(param);
parsed.add(arg);
}
return parsed;
} public static String extractArgName(String singleParam) {
Matcher m = singleParamPattern.matcher(singleParam);
return m.find() ? m.group(1) : "";
} public static List<String> transform(List<String> parsed) {
if (parsed == null || parsed.isEmpty()) {
return parsed;
}
List<String> result = new ArrayList<String>();
result.add(parsed.get(0));
if (parsed.size() == 2) {
result.add(parsed.get(1));
}
else {
int size = parsed.size();
StringBuilder argBuilder = new StringBuilder();
for (int i=1; i< size-1; i++) {
argBuilder.append(parsed.get(i) + ", ");
}
argBuilder.append(parsed.get(size-1));
result.add(argBuilder.toString());
}
return result;
} public static void testParseMethod() {
Map<String,List<String>> testMethods = new HashMap<String, List<String>>();
testMethods.put(" List<OrderDO> queryOrder(int kdtId); ", Arrays.asList(new String[]{"queryOrder", "kdtId"}));
testMethods.put(" List<OrderDO> queryOrder( int kdtId ); ", Arrays.asList(new String[]{"queryOrder", "kdtId"}));
testMethods.put(" OrderDO queryOrder(@Param(\"kdtId\") int kdtId); ", Arrays.asList(new String[]{"queryOrder", "kdtId"}));
testMethods.put(" List<OrderDO> queryOrder(List<String> orderNos); " , Arrays.asList(new String[]{"queryOrder", "orderNos"}));
testMethods.put(" List<OrderDO> queryOrder(@Param(\"orderNos\") List<String> orderNos); ", Arrays.asList(new String[]{"queryOrder", "orderNos"}));
testMethods.put(" OrderDO queryOrder(String orderNo, Integer kdtId); ", Arrays.asList(new String[]{"queryOrder", "orderNo, kdtId"}));
testMethods.put(" OrderDO queryOrder(String orderNo, @Param(\"kdtId\") Integer kdtId); ", Arrays.asList(new String[]{"queryOrder", "orderNo, kdtId"}));
testMethods.put(" OrderDO queryOrder(@Param(\"orderNo\") String orderNo, Integer kdtId); ", Arrays.asList(new String[]{"queryOrder", "orderNo, kdtId"}));
testMethods.put(" OrderDO queryOrder(@Param(\"orderNo\") String orderNo, @Param(\"kdtId\") Integer kdtId); ", Arrays.asList(new String[]{"queryOrder", "orderNo, kdtId"}));
testMethods.put(" OrderDO queryOrder(List<String> orderNos, Integer kdtId); \n", Arrays.asList(new String[]{"queryOrder", "orderNos, kdtId"}));
testMethods.put(" OrderDO queryOrder(@Param(\"orderNos\") List<String> orderNos, Integer kdtId); ", Arrays.asList(new String[]{"queryOrder", "orderNos, kdtId"}));
testMethods.put(" OrderDO queryOrder(List<String> orderNos, @Param(\"kdtId\") Integer kdtId); ", Arrays.asList(new String[]{"queryOrder", "orderNos, kdtId"}));
testMethods.put(" OrderDO queryOrder(@Param(\"orderNos\") List<String> orderNos, @Param(\"kdtId\") Integer kdtId); ", Arrays.asList(new String[]{"queryOrder", "orderNos, kdtId"}));
testMethods.put(" OrderDO queryOrder(@Param(\"orderNos\") List<String> orderNos, @Param(\"page\") Integer page, @Param(\"pageSize\") Integer pageSize); ", Arrays.asList(new String[]{"queryOrder", "orderNos, page, pageSize"})); Set<Map.Entry<String, List<String>>> entries = testMethods.entrySet();
for (Map.Entry entry: entries) {
String methodSign = (String)entry.getKey();
List<String> expected = (List<String>)entry.getValue();
List<String> actual = transform(parseMethod(methodSign));
if (!assertListEqual(actual, expected)) {
System.err.println("failed: " + methodSign);
System.err.println("expected: " + expected);
System.err.println("actual: " + actual);
}
} System.out.println("Test ParseMethod passed"); } public static boolean assertListEqual(List<String> list1, List<String> list2) {
if (list1 == null && list2 == null) {
return true;
}
if ((list1 == null && list2 !=null) || (list1 != null && list2 ==null)) {
return false;
}
if (list1.size() != list2.size()) {
return false;
}
for (int i=0; i< list1.size(); i++) {
if (!list1.get(i).equals(list2.get(i))) {
return false;
}
}
return true;
} }

小结:

   任何繁琐容易出错的技术含量不高的活,都可以转化为技术含量"相对有挑战"的活, 就看方案与思路。只要看上去比较有规律的事情, 通常是可以自动化地完成的, 包括生成源代码文件。 方案与思路总体上决定了解决质量的高度。正则很强大!