From 8e4bedb4ca8db9b7c6bb4cfb429ef017f337079e Mon Sep 17 00:00:00 2001 From: Ilya Litosh Date: Sun, 19 Jul 2020 11:55:52 +0100 Subject: [PATCH] Prevent ExtendedBeanSerializer from generating 2 toString methods --- CHANGELOG.md | 1 + .../sql/codegen/ExtendedBeanSerializer.java | 37 +++++- .../codegen/ExtendedBeanSerializerTest.java | 112 ++++++++++++++++++ 3 files changed, 144 insertions(+), 6 deletions(-) create mode 100644 querydsl-sql-codegen/src/test/java/com/querydsl/sql/codegen/ExtendedBeanSerializerTest.java diff --git a/CHANGELOG.md b/CHANGELOG.md index 7b8b0bb7f..0553ef710 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -43,6 +43,7 @@ A huge thanks goes out to all contributors that made this release possible in th * [#2504](https://github.com/querydsl/querydsl/issues/2504) - Work around issues with `AbstractJPAQuery#fetchResults` and `AbstractJPAQuery#fetchCount` in a query with multiple group by expressions by using an in-memory calculation. * [#2663](https://github.com/querydsl/querydsl/issues/2663) - Fix issues with the JPA implementation of `InsertClause`. * [#2706](https://github.com/querydsl/querydsl/pull/2706) - Fix a memory leak in `TemplateFactory`. +* [#2467](https://github.com/querydsl/querydsl/issues/2467) - Prevent `ExtendedBeanSerializer` from generating `toString` method twice #### Breaking changes diff --git a/querydsl-sql-codegen/src/main/java/com/querydsl/sql/codegen/ExtendedBeanSerializer.java b/querydsl-sql-codegen/src/main/java/com/querydsl/sql/codegen/ExtendedBeanSerializer.java index 0cee23cb9..ed62b221e 100644 --- a/querydsl-sql-codegen/src/main/java/com/querydsl/sql/codegen/ExtendedBeanSerializer.java +++ b/querydsl-sql-codegen/src/main/java/com/querydsl/sql/codegen/ExtendedBeanSerializer.java @@ -54,7 +54,6 @@ public class ExtendedBeanSerializer extends BeanSerializer { StringBuilder anyColumnIsNull = new StringBuilder(); StringBuilder columnEquals = new StringBuilder(); - StringBuilder toString = new StringBuilder(); List properties = new ArrayList(); for (PrimaryKeyData pk : primaryKeys) { for (String column : pk.getColumns()) { @@ -63,13 +62,9 @@ public class ExtendedBeanSerializer extends BeanSerializer { if (anyColumnIsNull.length() > 0) { anyColumnIsNull.append(" || "); columnEquals.append(" && "); - toString.append("+ \";\" + "); - } else { - toString.append("\"").append(model.getSimpleName()).append("#\" + "); } anyColumnIsNull.append(propName).append(" == null"); columnEquals.append(propName).append(".equals(obj.").append(propName).append(")"); - toString.append(propName); properties.add(propName); } } @@ -101,6 +96,37 @@ public class ExtendedBeanSerializer extends BeanSerializer { writer.line("return result;"); writer.end(); + + } + + @Override + protected void addToString(EntityType model, CodeWriter writer) throws IOException { + Collection primaryKeys = (Collection) model.getData().get(PrimaryKeyData.class); + + if (primaryKeys == null || primaryKeys.isEmpty()) { + super.addToString(model, writer); + return; + } + + StringBuilder toString = new StringBuilder(); + Map columnToProperty = new HashMap(); + for (Property property : model.getProperties()) { + columnToProperty.put(property.getAnnotation(Column.class).value(), property); + } + + for (PrimaryKeyData pk : primaryKeys) { + for (String column : pk.getColumns()) { + Property property = columnToProperty.get(column); + String propName = property.getEscapedName(); + if (toString.length() > 0) { + toString.append("+ \";\" + "); + } else { + toString.append("\"" + model.getSimpleName() + "#\" + "); + } + toString.append(propName); + } + } + // toString writer.annotation(Override.class); writer.beginPublicMethod(Types.STRING, "toString"); @@ -109,7 +135,6 @@ public class ExtendedBeanSerializer extends BeanSerializer { // writer.line("}"); writer.line("return ", toString + ";"); writer.end(); - } } diff --git a/querydsl-sql-codegen/src/test/java/com/querydsl/sql/codegen/ExtendedBeanSerializerTest.java b/querydsl-sql-codegen/src/test/java/com/querydsl/sql/codegen/ExtendedBeanSerializerTest.java new file mode 100644 index 000000000..b9b943462 --- /dev/null +++ b/querydsl-sql-codegen/src/test/java/com/querydsl/sql/codegen/ExtendedBeanSerializerTest.java @@ -0,0 +1,112 @@ +package com.querydsl.sql.codegen; + +import com.querydsl.codegen.EntityType; +import com.querydsl.codegen.Property; +import com.querydsl.codegen.SimpleSerializerConfig; +import com.querydsl.codegen.utils.JavaWriter; +import com.querydsl.codegen.utils.SimpleCompiler; +import com.querydsl.codegen.utils.model.ClassType; +import com.querydsl.codegen.utils.model.SimpleType; +import com.querydsl.codegen.utils.model.Type; +import com.querydsl.codegen.utils.model.TypeCategory; +import com.querydsl.sql.ColumnImpl; +import com.querydsl.sql.codegen.support.PrimaryKeyData; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; + +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.lang.reflect.Method; +import java.net.URL; +import java.net.URLClassLoader; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; + +public class ExtendedBeanSerializerTest { + + private static final String CLASS_NAME = "DomainClass"; + private static final String[] PATH = {"com", "querydsl", "test", "gen"}; + private static final String PACKAGE = String.join(".", PATH); + private static final String FULL_NAME = PACKAGE + "." + CLASS_NAME; + + @Rule + public TemporaryFolder compileFolder = new TemporaryFolder(); + + private EntityType type; + + private File srcFile; + + @Before + public void setUp() throws IOException { + Type typeModel = new SimpleType(TypeCategory.ENTITY, FULL_NAME, PACKAGE, CLASS_NAME, false, false); + type = new EntityType(typeModel); + File srcFolder = compileFolder.newFolder(PATH); + srcFile = new File(srcFolder, CLASS_NAME + ".java"); + } + + @Test + public void equals_hashcode_tostring() throws Exception { + Property idCol = new Property(type, "id", new ClassType(Integer.class)); + idCol.addAnnotation(new ColumnImpl("ID")); + Property subIdCol = new Property(type, "sub_id", new ClassType(String.class)); + subIdCol.addAnnotation(new ColumnImpl("SUB_ID")); + Property nameCol = new Property(type, "name", new ClassType(String.class)); + nameCol.addAnnotation(new ColumnImpl("NAME")); + + type.addProperty(idCol); + type.addProperty(subIdCol); + type.addProperty(nameCol); + + type.getData().put(PrimaryKeyData.class, Arrays.asList( + new PrimaryKeyData("PK", new String[]{"ID", "SUB_ID"}))); + + ExtendedBeanSerializer extendedBeanSerializer = new ExtendedBeanSerializer(); + extendedBeanSerializer.setAddToString(true); + + FileWriter fw = new FileWriter(srcFile); + extendedBeanSerializer.serialize(type, SimpleSerializerConfig.DEFAULT, new JavaWriter(fw)); + fw.close(); + + URLClassLoader classLoader = URLClassLoader.newInstance(new URL[] {compileFolder.getRoot().toURI().toURL()}); + int retCode = new SimpleCompiler().run(null, System.out, System.err, srcFile.getAbsolutePath()); + assertEquals("The generated source should compile", 0, retCode); + + Class cls = Class.forName(FULL_NAME, true, classLoader); + ReflectionHelper reflection = new ReflectionHelper(cls); + Object obj1 = cls.newInstance(); + Object obj1a = cls.newInstance(); + Object obj2 = cls.newInstance(); + reflection.setValues(obj1, 1, "##", "X"); + reflection.setValues(obj1a, 1, "##", null); + reflection.setValues(obj2, 2, "--", "Y"); + + assertEquals(obj1, obj1a); + assertEquals(obj1.hashCode(), obj1a.hashCode()); + assertNotEquals(obj1, obj2); + + assertEquals(obj1.toString(), "DomainClass#1;##"); + } + + private static class ReflectionHelper { + private final Map methodByName = new HashMap(); + + public ReflectionHelper(Class cls) { + for (Method m : cls.getDeclaredMethods()) { + methodByName.put(m.getName(), m); + } + } + + private void setValues(Object instance, int id, String subId, String name) throws Exception { + methodByName.get("setId").invoke(instance, id); + methodByName.get("setSub_id").invoke(instance, subId); + methodByName.get("setName").invoke(instance, name); + } + } +}