在使用 MyBatis-generator 生成代码时,总有各种场景需要合并生成的 sqlMapper XML文件和 生成的Model 及 Client 文件。
通过参考各种文档和不断的一点点实验总结了以下方法:
1.XML 文件:mybatis-generator 本身支持融合,但是在融合的时候,老的相同的节点不会被删除,导致生成后的文件节点重复。所以在插件的 sqlMapDocumentGenerated 方法中 先把原来的XML文件读出为XML Document,然后与生成的新的 Document 进行比较,删除老的Document中与新Document相同的节点,然后再保存回文件。mybatis 进行融合的时候老文件已经没有自动生成的节点了,所以融合以后是最终想要的文件。
2.Java文件:mybatis-generator 不支持 java文件的融合,搜索到的资料,很多都是修改源码,但这样就不通用了。我的处理方法是在相应的插件方法中(比如 clientGenerated, modelBaseRecordClassGenerated)把老的文件使用 JavaParse 解析出来,然后逐一与新的 Interface 或 TopLevelClass 对比,把不存在新生成对象中的 import, annotation, field, method 等添加到新的对象中去。
3.还有另一个种方法,就是在对象完全生成以后的相应插件方法中返回 false,这样mybatis-generator 就不会保存文件,我们自己处理要保存的文件。这样方法就比较多样了:比如还是像上面那样处理,然后 getFormatContent ,自己保存;又或者在文件中加上某个明显的标记,把老文件中属于自定义的部分用正则拿出来,然后放到新对象的 getFormatContent 返回字符串的后面。
代码:
XML 的融合:
@Override
public boolean sqlMapDocumentGenerated(org.mybatis.generator.api.dom.xml.Document document,
IntrospectedTable introspectedTable) {
this.document = document;
String targetPackage = super.context.getSqlMapGeneratorConfiguration().getTargetPackage();
String targetProject = super.context.getSqlMapGeneratorConfiguration().getTargetProject();
try
{
File directory = shellCallback.getDirectory(targetProject, targetPackage);
String fileName = introspectedTable.getTableConfiguration().getDomainObjectName() + "Mapper.xml";
File xmlFile = new File(directory, fileName);
if (!directory.exists() || !xmlFile.exists()) {
return true;
}
// old exist xml nodes
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setValidating(false);
DocumentBuilder db = dbf.newDocumentBuilder();
Document doc = db.parse(new FileInputStream(xmlFile));
org.w3c.dom.Element rootElement = doc.getDocumentElement();
NodeList list = rootElement.getChildNodes();
// new xml nodes
List<Element> elements = document.getRootElement().getElements();
// match pattern
Pattern p = Pattern.compile("<(\\w+)\\s+id=\"(\\w+)\"");
List<Node> oldDocumentWillRemoveElements = new ArrayList<Node>();
boolean findSameNode = false;
// traverse new nodes to compare old nodes to filter
for (Iterator<Element> elementIt = elements.iterator(); elementIt.hasNext(); ) {
findSameNode = false;
String newNodeName = "";
String NewIdValue = "";
Element element = elementIt.next();
Matcher m = p.matcher(element.getFormattedContent(0));
if (m.find()) {
newNodeName = m.group(1);
NewIdValue = m.group(2);
}
for (int i = 0; i < list.getLength(); i++) {
Node node = list.item(i);
if (node.getNodeType() == Node.ELEMENT_NODE) {
if (newNodeName.equals(node.getNodeName())) {
NamedNodeMap attr = node.getAttributes();
for (int j = 0; j < attr.getLength(); j++) {
Node attrNode = attr.item(j);
if (attrNode.getNodeName().equals("id") && attrNode.getNodeValue().equals(NewIdValue)) {
//elementIt.remove();
oldDocumentWillRemoveElements.add(node);
findSameNode = true;
break;
}
}
if (findSameNode == true)
break;
}
}
}
}
// remove old xml nodes that exist in new xml
for(Node n : oldDocumentWillRemoveElements){
rootElement.removeChild(n);
}
System.out.println("deleted same nodes from old document:" + oldDocumentWillRemoveElements.size());
// save old xml document
//TransformerFactory tff = TransformerFactory.newInstance();
//Transformer tf = tff.newTransformer();
//tf.setOutputProperty(OutputKeys.INDENT, "no");
//tf.transform(new DOMSource(doc), new StreamResult(xmlFile));
DomWriter dw = new DomWriter();
String s = dw.toString(doc);
FileOutputStream fos = new FileOutputStream(xmlFile, false);
OutputStreamWriter osw;
osw = new OutputStreamWriter(fos, "UTF-8");
BufferedWriter bw = new BufferedWriter(osw);
bw.write(s);
bw.close();
System.out.println("saved old document");
}
catch (Exception ex){
System.out.println("【Error】");
ex.printStackTrace();
}
}
java 文件的融合:
@Override
public boolean modelBaseRecordClassGenerated(TopLevelClass topLevelClass, IntrospectedTable introspectedTable) {
String targetPackage = super.context.getJavaModelGeneratorConfiguration().getTargetPackage();
String targetProject = super.context.getJavaModelGeneratorConfiguration().getTargetProject();
try {
File directory = shellCallback.getDirectory(targetProject, targetPackage);
String fileName = introspectedTable.getTableConfiguration().getDomainObjectName() + ".java";
File javaFile = new File(directory, fileName);
CompilationUnit oldCU = JavaParser.parse(javaFile);
NodeList<ImportDeclaration> oldImports = oldCU.getImports();
// imports
AddImports(oldImports, topLevelClass);
NodeList<TypeDeclaration<?>> oldTypes = oldCU.getTypes();
// types
for (int i=0; i<oldTypes.size();i++){
TypeDeclaration oldType = oldTypes.get(i);
// class annotations
NodeList<AnnotationExpr> oldTypeAnn = oldType.getAnnotations();
AddAnnotations(oldTypeAnn, topLevelClass);
// fields
List<FieldDeclaration> oldTypeFields = oldType.getFields();
List<Field> newFields = topLevelClass.getFields();
AddFields(oldTypeFields, newFields, topLevelClass);
// methos
List<MethodDeclaration> oldTypeMethods = oldType.getMethods();
AddMethods(oldTypeMethods, topLevelClass);
}
}
catch (Exception e){
e.printStackTrace();
}
return true;
}
@Override
public boolean clientGenerated(Interface interfaze, TopLevelClass topLevelClass, IntrospectedTable introspectedTable) {
String targetPackage = super.context.getJavaClientGeneratorConfiguration().getTargetPackage();
String targetProject = super.context.getJavaClientGeneratorConfiguration().getTargetProject();
try {
File directory = shellCallback.getDirectory(targetProject, targetPackage);
String fileName = introspectedTable.getTableConfiguration().getDomainObjectName() + "Mapper.java";
File javaFile = new File(directory, fileName);
CompilationUnit oldCU = JavaParser.parse(javaFile);
NodeList<ImportDeclaration> oldImports = oldCU.getImports();
// imports
AddImports(oldImports, interfaze);
NodeList<TypeDeclaration<?>> oldTypes = oldCU.getTypes();
// types
for (int i=0; i<oldTypes.size();i++){
TypeDeclaration oldType = oldTypes.get(i);
// annotations
NodeList<AnnotationExpr> oldTypeAnn = oldType.getAnnotations();
AddAnnotations(oldTypeAnn, interfaze);
// fields
List<FieldDeclaration> oldTypeFields = oldType.getFields();
List<Field> newFields = interfaze.getFields();
AddFields(oldTypeFields, newFields, interfaze);
// methos
List<MethodDeclaration> oldTypeMethods = oldType.getMethods();
AddMethods(oldTypeMethods, interfaze);
}
}
catch (Exception e){
e.printStackTrace();
}
return true;
}
private void SetModifiers(EnumSet<Modifier> modifiers, JavaElement element){
Iterator<Modifier> it = modifiers.iterator();
while (it.hasNext()) {
Modifier modifier = it.next();
switch (modifier){
case FINAL:
element.setFinal(true);
break;
case STATIC:
element.setStatic(true);
break;
case VOLATILE:
if(element instanceof Field){
Field field = (Field)element;
field.setVolatile(true);
}
break;
case TRANSIENT:
if(element instanceof Field){
Field field = (Field)element;
field.setTransient(true);
}
break;
case NATIVE:
if(element instanceof Method){
Method method = (Method)element;
method.setNative(true);
}
break;
case SYNCHRONIZED:
if(element instanceof Method){
Method method = (Method)element;
method.setSynchronized(true);
}
break;
case PUBLIC:
element.setVisibility(JavaVisibility.PUBLIC);
break;
case DEFAULT:
if(element instanceof Method){
Method method = (Method)element;
method.setDefault(true);
}
break;
case PRIVATE:
element.setVisibility(JavaVisibility.PRIVATE);
break;
case PROTECTED:
element.setVisibility(JavaVisibility.PROTECTED);
break;
}
}
}
private void AddAnnotations( NodeList<AnnotationExpr> oldTypeAnn, JavaElement element){
if(oldTypeAnn == null || oldTypeAnn.size() < 1){
return;
}
boolean isExist = false;
for(int j = 0; j < oldTypeAnn.size(); j++){
AnnotationExpr ota = oldTypeAnn.get(j);
isExist = false;
for(java.lang.String na : element.getAnnotations()){
if(ota.toString().equals(na)) {
isExist = true;
break;
}
}
if(!isExist){
element.addAnnotation(ota.toString());
}
}
}
private void AddFields(List<FieldDeclaration> oldTypeFields, List<Field> newFields, org.mybatis.generator.api.dom.java.CompilationUnit cu){
boolean isExist = false;
for(int j =0;j<oldTypeFields.size();j++){
FieldDeclaration of = oldTypeFields.get(j);
isExist = false;
for (Field f : newFields){
if(of.getVariables().get(0).getName().toString().equals(f.getName())) {
isExist = true;
break;
}
}
if(!isExist){
Field nf = new Field();
nf.setName(of.getVariables().get(0).getName().toString());
nf.setType(new FullyQualifiedJavaType(of.getElementType().toString()));
/* javaParser's Type can not get the FullyQualified Name, so it must import the type in import part, and use the type shortname in code.*/
SetModifiers(of.getModifiers(), nf);
if(of.getVariables().get(0).getInitializer().isPresent()) {
nf.setInitializationString(of.getVariables().get(0).getInitializer().get().toString());
}
NodeList<AnnotationExpr> fas = of.getAnnotations();
for (int k = 0; k < fas.size();k++){
AnnotationExpr ae = fas.get(k);
nf.addAnnotation(ae.toString());
}
if(cu instanceof Interface ){
Interface interfaze = (Interface)cu;
interfaze.addField(nf);
}
else if (cu instanceof TopLevelClass){
TopLevelClass topLevelClass = (TopLevelClass)cu;
topLevelClass.addField(nf);
}
}
}
}
private void AddImports(NodeList<ImportDeclaration> oldImports, org.mybatis.generator.api.dom.java.CompilationUnit cu){
boolean isExist = false;
Set<String> staticImports = cu.getStaticImports();
Set<FullyQualifiedJavaType> imports = cu.getImportedTypes();
for(int i =0; i < oldImports.size();i++){
ImportDeclaration oldImport = oldImports.get(i);
String oi = oldImport.getNameAsString() + (oldImport.isAsterisk() ? ".*" :"");
isExist = false;
if(oldImport.isStatic()){
for(String nt: staticImports){
if (oi.equals(nt)){
isExist = true;
break;
}
}
}else {
for(FullyQualifiedJavaType nt: imports){
if (oi.equals(nt.getFullyQualifiedName())){
isExist = true;
break;
}
}
}
if(!isExist){
if(oldImport.isStatic()){
cu.addStaticImport(oi);
} else {
cu.addImportedType(new FullyQualifiedJavaType(oi));
}
}
}
}
private void AddMethods(List<MethodDeclaration> oldTypeMethods, org.mybatis.generator.api.dom.java.CompilationUnit cu){
/*in my case, the generated code have no methods, so ignore judging if methods exist in new class*/
for (int j=0;j<oldTypeMethods.size();j++){
MethodDeclaration omd = oldTypeMethods.get(j);
Method method = new Method(omd.getNameAsString()); // method name
SetModifiers(omd.getModifiers(), method); // method modifiers
method.setReturnType(new FullyQualifiedJavaType(omd.getType().toString())); // method return type
// method body
if(omd.getBody().isPresent()) {
String[] body = omd.getBody().get().toString().split("\r\n");
for (int k = 1; k < body.length - 1; k++){
body[k] = body[k].substring(4);
}
method.addBodyLines(Arrays.asList(Arrays.copyOfRange(body, 1, body.length -1)));
}
// method parameters
NodeList<Parameter> ops = omd.getParameters();
for(int k = 0; k < ops.size(); k ++){
Parameter op = ops.get(k);
org.mybatis.generator.api.dom.java.Parameter parameter = new org.mybatis.generator.api.dom.java.Parameter(
new FullyQualifiedJavaType(op.getType().toString()),
op.getName().toString());
NodeList<AnnotationExpr> opAn = op.getAnnotations();
for(int n = 0; n < opAn.size(); n++){
parameter.addAnnotation(opAn.get(n).toString());
}
method.addParameter(parameter);
}
// method annotations
NodeList<AnnotationExpr> mae = omd.getAnnotations();
for (int k = 0; k < mae.size();k++){
method.addAnnotation(mae.get(k).toString());
}
// method exceptions
NodeList<ReferenceType> exceptions = omd.getThrownExceptions();
for(int k = 0; k < exceptions.size(); k++){
method.addException(new FullyQualifiedJavaType(exceptions.get(k).toString()));
}
if(cu instanceof Interface){
((Interface)cu).addMethod(method);
}
else if(cu instanceof TopLevelClass){
((TopLevelClass)cu).addMethod(method);
}
}
}