• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

mybatis / generator / 2136

02 Apr 2026 07:17PM UTC coverage: 91.775% (+1.4%) from 90.382%
2136

Pull #1485

github

web-flow
Merge 516685a9a into 18f1f002d
Pull Request #1485: Code Cleanup and Coverage

2425 of 3124 branches covered (77.62%)

191 of 235 new or added lines in 54 files covered. (81.28%)

7 existing lines in 4 files now uncovered.

11884 of 12949 relevant lines covered (91.78%)

0.92 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

83.55
/core/mybatis-generator-core/src/main/java/org/mybatis/generator/plugins/EqualsHashCodePlugin.java
1
/*
2
 *    Copyright 2006-2026 the original author or authors.
3
 *
4
 *    Licensed under the Apache License, Version 2.0 (the "License");
5
 *    you may not use this file except in compliance with the License.
6
 *    You may obtain a copy of the License at
7
 *
8
 *       https://www.apache.org/licenses/LICENSE-2.0
9
 *
10
 *    Unless required by applicable law or agreed to in writing, software
11
 *    distributed under the License is distributed on an "AS IS" BASIS,
12
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
 *    See the License for the specific language governing permissions and
14
 *    limitations under the License.
15
 */
16
package org.mybatis.generator.plugins;
17

18
import static org.mybatis.generator.internal.util.JavaBeansUtil.getGetterMethodName;
19
import static org.mybatis.generator.internal.util.StringUtility.isTrue;
20

21
import java.util.Iterator;
22
import java.util.List;
23
import java.util.Properties;
24

25
import org.mybatis.generator.api.IntrospectedColumn;
26
import org.mybatis.generator.api.IntrospectedTable;
27
import org.mybatis.generator.api.PluginAdapter;
28
import org.mybatis.generator.api.dom.OutputUtilities;
29
import org.mybatis.generator.api.dom.java.FullyQualifiedJavaType;
30
import org.mybatis.generator.api.dom.java.JavaVisibility;
31
import org.mybatis.generator.api.dom.java.Method;
32
import org.mybatis.generator.api.dom.java.Parameter;
33
import org.mybatis.generator.api.dom.java.TopLevelClass;
34

35
/**
36
 * This plugin adds equals() and hashCode() methods to the generated model
37
 * classes. It demonstrates the process of adding methods to generated classes
38
 *
39
 * <p>The <code>equals</code> method generated by this class is correct in most cases,
40
 * but will probably NOT be correct if you have specified a rootClass - because
41
 * our equals method only checks the fields it knows about.
42
 *
43
 * <p>Similarly, the <code>hashCode</code> method generated by this class only relies
44
 * on fields it knows about. Anything you add, or fields in a super class will
45
 * not be factored into the hash code.
46
 *
47
 * @author Jeff Butler
48
 *
49
 */
50
public class EqualsHashCodePlugin extends PluginAdapter {
1✔
51

52
    private boolean useEqualsHashCodeFromRoot;
53

54
    @Override
55
    public void setProperties(Properties properties) {
56
        super.setProperties(properties);
1✔
57
        useEqualsHashCodeFromRoot = isTrue(properties.getProperty("useEqualsHashCodeFromRoot")); //$NON-NLS-1$
1✔
58
    }
1✔
59

60
    /**
61
     * This plugin is always valid - no properties are required.
62
     */
63
    @Override
64
    public boolean validate(List<String> warnings) {
65
        return true;
1✔
66
    }
67

68
    @Override
69
    public boolean modelBaseRecordClassGenerated(TopLevelClass topLevelClass,
70
            IntrospectedTable introspectedTable) {
71
        List<IntrospectedColumn> columns;
72
        if (introspectedTable.getRules().generateRecordWithBLOBsClass()) {
1✔
73
            columns = introspectedTable.getNonBLOBColumns();
1✔
74
        } else {
75
            columns = introspectedTable.getAllColumns();
1✔
76
        }
77

78
        generateEquals(topLevelClass, columns, introspectedTable);
1✔
79
        generateHashCode(topLevelClass, columns, introspectedTable);
1✔
80

81
        return true;
1✔
82
    }
83

84
    @Override
85
    public boolean modelPrimaryKeyClassGenerated(TopLevelClass topLevelClass,
86
            IntrospectedTable introspectedTable) {
87
        generateEquals(topLevelClass, introspectedTable.getPrimaryKeyColumns(),
1✔
88
                introspectedTable);
89
        generateHashCode(topLevelClass, introspectedTable
1✔
90
                .getPrimaryKeyColumns(), introspectedTable);
1✔
91

92
        return true;
1✔
93
    }
94

95
    @Override
96
    public boolean modelRecordWithBLOBsClassGenerated(
97
            TopLevelClass topLevelClass, IntrospectedTable introspectedTable) {
98
        generateEquals(topLevelClass, introspectedTable.getAllColumns(),
1✔
99
                introspectedTable);
100
        generateHashCode(topLevelClass, introspectedTable.getAllColumns(),
1✔
101
                introspectedTable);
102

103
        return true;
1✔
104
    }
105

106
    /**
107
     * Generates an <code>equals</code> method that does a comparison of all fields.
108
     *
109
     * <p>The generated <code>equals</code> method will be correct unless:
110
     *
111
     * <ul>
112
     *   <li>Other fields have been added to the generated classes</li>
113
     *   <li>A <code>rootClass</code> is specified that holds state</li>
114
     * </ul>
115
     *
116
     * @param topLevelClass
117
     *            the class to which the method will be added
118
     * @param introspectedColumns
119
     *            column definitions of this class and any superclass of this
120
     *            class
121
     * @param introspectedTable
122
     *            the table corresponding to this class
123
     */
124
    protected void generateEquals(TopLevelClass topLevelClass,
125
            List<IntrospectedColumn> introspectedColumns,
126
            IntrospectedTable introspectedTable) {
127
        Method method = new Method("equals"); //$NON-NLS-1$
1✔
128
        method.setVisibility(JavaVisibility.PUBLIC);
1✔
129
        method.setReturnType(FullyQualifiedJavaType
1✔
130
                .getBooleanPrimitiveInstance());
1✔
131
        method.addParameter(new Parameter(FullyQualifiedJavaType
1✔
132
                .getObjectInstance(), "that")); //$NON-NLS-1$
1✔
133
        method.addAnnotation("@Override"); //$NON-NLS-1$
1✔
134

135
        commentGenerator.addGeneralMethodAnnotation(method, introspectedTable, topLevelClass.getImportedTypes());
1✔
136

137
        method.addBodyLine("if (this == that) {"); //$NON-NLS-1$
1✔
138
        method.addBodyLine("return true;"); //$NON-NLS-1$
1✔
139
        method.addBodyLine("}"); //$NON-NLS-1$
1✔
140

141
        method.addBodyLine("if (that == null) {"); //$NON-NLS-1$
1✔
142
        method.addBodyLine("return false;"); //$NON-NLS-1$
1✔
143
        method.addBodyLine("}"); //$NON-NLS-1$
1✔
144

145
        method.addBodyLine("if (getClass() != that.getClass()) {"); //$NON-NLS-1$
1✔
146
        method.addBodyLine("return false;"); //$NON-NLS-1$
1✔
147
        method.addBodyLine("}"); //$NON-NLS-1$
1✔
148

149
        StringBuilder sb = new StringBuilder();
1✔
150
        sb.append(topLevelClass.getType().getShortName());
1✔
151
        sb.append(" other = ("); //$NON-NLS-1$
1✔
152
        sb.append(topLevelClass.getType().getShortName());
1✔
153
        sb.append(") that;"); //$NON-NLS-1$
1✔
154
        method.addBodyLine(sb.toString());
1✔
155

156
        if (useEqualsHashCodeFromRoot && topLevelClass.getSuperClass().isPresent()) {
1!
157
            method.addBodyLine("if (!super.equals(other)) {"); //$NON-NLS-1$
×
158
            method.addBodyLine("return false;"); //$NON-NLS-1$
×
159
            method.addBodyLine("}"); //$NON-NLS-1$
×
160
        }
161

162
        boolean first = true;
1✔
163
        Iterator<IntrospectedColumn> iter = introspectedColumns.iterator();
1✔
164
        while (iter.hasNext()) {
1✔
165
            IntrospectedColumn introspectedColumn = iter.next();
1✔
166

167
            sb.setLength(0);
1✔
168

169
            if (first) {
1✔
170
                sb.append("return ("); //$NON-NLS-1$
1✔
171
                first = false;
1✔
172
            } else {
173
                OutputUtilities.javaIndent(sb, 1);
1✔
174
                sb.append("&& ("); //$NON-NLS-1$
1✔
175
            }
176

177
            String getterMethod = getGetterMethodName(
1✔
178
                    introspectedColumn.getJavaProperty(), introspectedColumn
1✔
179
                            .getFullyQualifiedJavaType());
1✔
180

181
            if (introspectedColumn.getFullyQualifiedJavaType().isPrimitive()) {
1✔
182
                sb.append("this."); //$NON-NLS-1$
1✔
183
                sb.append(getterMethod);
1✔
184
                sb.append("() == "); //$NON-NLS-1$
1✔
185
                sb.append("other."); //$NON-NLS-1$
1✔
186
                sb.append(getterMethod);
1✔
187
                sb.append("())"); //$NON-NLS-1$
1✔
188
            } else if (introspectedColumn.getFullyQualifiedJavaType().isArray()) {
1✔
189
                topLevelClass.addImportedType("java.util.Arrays"); //$NON-NLS-1$
1✔
190
                sb.append("Arrays.equals(this."); //$NON-NLS-1$
1✔
191
                sb.append(getterMethod);
1✔
192
                sb.append("(), "); //$NON-NLS-1$
1✔
193
                sb.append("other."); //$NON-NLS-1$
1✔
194
                sb.append(getterMethod);
1✔
195
                sb.append("()))"); //$NON-NLS-1$
1✔
196
            } else {
197
                sb.append("this."); //$NON-NLS-1$
1✔
198
                sb.append(getterMethod);
1✔
199
                sb.append("() == null ? other."); //$NON-NLS-1$
1✔
200
                sb.append(getterMethod);
1✔
201
                sb.append("() == null : this."); //$NON-NLS-1$
1✔
202
                sb.append(getterMethod);
1✔
203
                sb.append("().equals(other."); //$NON-NLS-1$
1✔
204
                sb.append(getterMethod);
1✔
205
                sb.append("()))"); //$NON-NLS-1$
1✔
206
            }
207

208
            if (!iter.hasNext()) {
1✔
209
                sb.append(';');
1✔
210
            }
211

212
            method.addBodyLine(sb.toString());
1✔
213
        }
1✔
214

215
        topLevelClass.addMethod(method);
1✔
216
    }
1✔
217

218
    /**
219
     * Generates a <code>hashCode</code> method that includes all fields.
220
     *
221
     * <p>Note that this implementation is based on the eclipse foundation hashCode
222
     * generator.
223
     *
224
     * @param topLevelClass
225
     *            the class to which the method will be added
226
     * @param introspectedColumns
227
     *            column definitions of this class and any superclass of this
228
     *            class
229
     * @param introspectedTable
230
     *            the table corresponding to this class
231
     */
232
    protected void generateHashCode(TopLevelClass topLevelClass,
233
            List<IntrospectedColumn> introspectedColumns,
234
            IntrospectedTable introspectedTable) {
235
        Method method = new Method("hashCode"); //$NON-NLS-1$
1✔
236
        method.setVisibility(JavaVisibility.PUBLIC);
1✔
237
        method.setReturnType(FullyQualifiedJavaType.getIntInstance());
1✔
238
        method.addAnnotation("@Override"); //$NON-NLS-1$
1✔
239

240
        commentGenerator.addGeneralMethodAnnotation(method, introspectedTable, topLevelClass.getImportedTypes());
1✔
241

242
        method.addBodyLine("final int prime = 31;"); //$NON-NLS-1$
1✔
243
        method.addBodyLine("int result = 1;"); //$NON-NLS-1$
1✔
244

245
        if (useEqualsHashCodeFromRoot && topLevelClass.getSuperClass().isPresent()) {
1!
246
            method.addBodyLine("result = prime * result + super.hashCode();"); //$NON-NLS-1$
×
247
        }
248

249
        StringBuilder sb = new StringBuilder();
1✔
250
        boolean hasTemp = false;
1✔
251
        for (IntrospectedColumn introspectedColumn : introspectedColumns) {
1✔
252
            FullyQualifiedJavaType fqjt = introspectedColumn.getFullyQualifiedJavaType();
1✔
253
            String getterMethod = getGetterMethodName(introspectedColumn.getJavaProperty(), fqjt);
1✔
254

255
            sb.setLength(0);
1✔
256
            if (fqjt.isPrimitive()) {
1✔
257
                switch (fqjt.getFullyQualifiedName()) {
1!
258
                case "boolean": //$NON-NLS-1$
259
                    sb.append("result = prime * result + ("); //$NON-NLS-1$
1✔
260
                    sb.append(getterMethod);
1✔
261
                    sb.append("() ? 1231 : 1237);"); //$NON-NLS-1$
1✔
262
                    method.addBodyLine(sb.toString());
1✔
263
                    break;
1✔
264
                case "byte", "char", "int", "short": //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
265
                    sb.append("result = prime * result + "); //$NON-NLS-1$
1✔
266
                    sb.append(getterMethod);
1✔
267
                    sb.append("();"); //$NON-NLS-1$
1✔
268
                    method.addBodyLine(sb.toString());
1✔
269
                    break;
1✔
270
                case "double": //$NON-NLS-1$
271
                    if (!hasTemp) {
×
272
                        method.addBodyLine("long temp;"); //$NON-NLS-1$
×
273
                        hasTemp = true;
×
274
                    }
275
                    sb.append("temp = Double.doubleToLongBits("); //$NON-NLS-1$
×
276
                    sb.append(getterMethod);
×
277
                    sb.append("());"); //$NON-NLS-1$
×
278
                    method.addBodyLine(sb.toString());
×
NEW
279
                    method.addBodyLine("result = prime * result + (int) (temp ^ (temp >>> 32));"); //$NON-NLS-1$
×
NEW
280
                    break;
×
281
                case "float": //$NON-NLS-1$
NEW
282
                    sb.append("result = prime * result + Float.floatToIntBits("); //$NON-NLS-1$
×
283
                    sb.append(getterMethod);
×
284
                    sb.append("());"); //$NON-NLS-1$
×
285
                    method.addBodyLine(sb.toString());
×
NEW
286
                    break;
×
287
                case "long": //$NON-NLS-1$
288
                    sb.append("result = prime * result + (int) ("); //$NON-NLS-1$
×
289
                    sb.append(getterMethod);
×
290
                    sb.append("() ^ ("); //$NON-NLS-1$
×
291
                    sb.append(getterMethod);
×
292
                    sb.append("() >>> 32));"); //$NON-NLS-1$
×
293
                    method.addBodyLine(sb.toString());
×
NEW
294
                    break;
×
295
                default:
296
                    // all bases are covered above
297
                    break;
298
                }
1✔
299
            } else if (fqjt.isArray()) {
1✔
300
                // Arrays is already imported by the generateEquals method, we don't need
301
                // to do it again
302
                sb.append("result = prime * result + (Arrays.hashCode("); //$NON-NLS-1$
1✔
303
                sb.append(getterMethod);
1✔
304
                sb.append("()));"); //$NON-NLS-1$
1✔
305
                method.addBodyLine(sb.toString());
1✔
306
            } else {
307
                sb.append("result = prime * result + (("); //$NON-NLS-1$
1✔
308
                sb.append(getterMethod);
1✔
309
                sb.append("() == null) ? 0 : "); //$NON-NLS-1$
1✔
310
                sb.append(getterMethod);
1✔
311
                sb.append("().hashCode());"); //$NON-NLS-1$
1✔
312
                method.addBodyLine(sb.toString());
1✔
313
            }
314
        }
1✔
315

316
        method.addBodyLine("return result;"); //$NON-NLS-1$
1✔
317

318
        topLevelClass.addMethod(method);
1✔
319
    }
1✔
320
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc