#background
Two days ago, Tiezi asked such a question in the group:
I have stepped on this pit before; earlier, the Lombok plug-in was introduced into the project, which really liberated my hands and replaced some repetitive simple work (writing methods such as Getter, Setter, and toString).
However, in the process of using it, I also found some problems. At the beginning, I didn’t realize that it was a problem with Lombok. Later, I tracked the source code of other corresponding components and found out that it was a problem with Lombok!
# The pit of the Setter-Getter method
Problem discovery
We mainly use the annotation of Lombok’s Setter-Getter method in the project, that is, the combined annotation @Data, but in the process of using Mybatis to insert data, a problem occurred. The problem description is as follows:
We have an entity class:
@Data public class NMetaVerify{ private NMetaType nMetaType; private Long id; ...other attributes }
When we use Mybatis to insert data, we find that other attributes can be inserted normally, but the nMetaType attribute is always null in the database.
Resolve
When I debugged the project code to call the corresponding method of inserting SQL in Mybatis, I saw that the nMetaType attribute of the NMetaVerify object still had data, but after the insertion was executed, the nMetaType field of the database was always null. I thought It is because my enumeration type is written incorrectly. After looking at other fields with the same enumeration type, they can also be inserted into the database normally, which makes me feel even more confused.
So, I tracked the source code of Mybatis and found that Mybatis used reflection when obtaining this nMetaType attribute, using the getxxxx method to obtain it, but I found that the get method of nMetaType seems a bit longer than the getxxxx method required by Mybatis It seems to be different, the problem has been found!
Reason
The get-set method generated by Lombok for attributes with lowercase first letter and uppercase second letter is different from that generated by Mybatis and idea or the officially recognized get-set method of Java:
Get-Set method generated by Lombok
@Data public class NMetaVerify { private Long id; private NMetaType nMetaType; private Date createTime; public void lombokFound(){ NMetaVerify nMetaVerify = new NMetaVerify(); nMetaVerify.setNMetaType(NMetaType.TWO); //Note: The set method of nMetaType is setNMetaType, the first n letter is capitalized, nMetaVerify.getNMetaType(); //getxxxx method is also capitalized } }
The official default behavior of idea, Mybatis, and Java is:
public class NMetaVerify { private Long id; private NMetaType nMetaType; private Date createTime; public Long getId() { return id; } public void setId(Long id) { this.id = id; } public NMetaType getnMetaType() {//Note: the first letter of the nMetaType attribute is lowercase return nMetaType; } public void setnMetaType(NMetaType nMetaType) {//Note: the first letter of the nMetaType attribute is lowercase this.nMetaType = nMetaType; } public Date getCreateTime() { return createTime; } public void setCreateTime(Date createTime) { this.createTime = createTime; } }
Mybatis (version 3.4.6) parses the get-set method to obtain the source code of the attribute name:
package org.apache.ibatis.reflection.property; import java.util.Locale; import org.apache.ibatis.reflection.ReflectionException; public final class PropertyNamer { private PropertyNamer() { // Prevent Instantiation of Static Class } public static String methodToProperty(String name) { if (name.startsWith("is")) {//The beginning of is is generally bool type, directly intercepted from the second (index) (simple and rude) name = name. substring(2); } else if (name.startsWith("get") || name.startsWith("set")) {//set-get starts to intercept from the third (index) name = name. substring(3); } else { throw new ReflectionException("Error parsing property name '" + name + "'. Didn't start with 'is', 'get' or 'set'."); } //The following judgment is very important. It can be divided into two sentences to start the explanation. The explanation is as follows //First sentence: name.length()==1 // For properties with only one letter, such as private int x; // The corresponding get-set method is getX();setX(int x); //Second sentence: name.length() > 1 & amp; & amp; !Character.isUpperCase(name.charAt(1))) // The length of the attribute name is greater than 1, and the second (charAt(1) in the code, this 1 is the array subscript) letter is lowercase // If the second char is uppercase, then return name directly if (name. length() == 1 || (name. length() > 1 & amp; & amp; !Character. isUpperCase(name. charAt(1)))) { name = name.substring(0, 1).toLowerCase(Locale.ENGLISH) + name.substring(1);//Make the first letter of the attribute name lowercase, and then add the following content } return name; } public static boolean isProperty(String name) { return name.startsWith("get") || name.startsWith("set") || name.startsWith("is"); } public static boolean isGetter(String name) { return name.startsWith("get") || name.startsWith("is"); } public static boolean isSetter(String name) { return name.startsWith("set"); } }
Mybatis parses the get-set method as an attribute name test
@Test public void foundPropertyNamer() { String isName = "isName"; String getName = "getName"; String getnMetaType = "getnMetaType"; String getNMetaType = "getNMetaType"; Stream.of(isName, getName, getnMetaType, getNMetaType) .forEach(methodName->System.out.println("The method name is:" + methodName + "Property name:" + PropertyNamer.methodToProperty(methodName))); }
The output is as follows:
The method name is: isName attribute name: name The method name is: getName attribute name: name The method name is: getnMetaType Attribute name: nMetaType //The second letter of this and the following attributes is capitalized, so return name directly The method name is: getNMetaType Attribute name: NMetaType
Solution
-
Modify the attribute name, make the second letter lowercase, or stipulate that the first two letters of all attributes must be lowercase
-
If the database has been designed, and the front-end and back-end interfaces are well connected, and you don’t want to modify it, then use idea to generate the get-set method for this special attribute
# @Accessor(chain = true) annotation problem
Problem discovery
When using easyexcel to export, I found that the previous entity class export was normal, but now the newly added entity class is not normal. After comparison, I found that the newly added entity class added @Accessor(chain = true) Note, our purpose is mainly to facilitate our chain call set method:
new UserDto() .setUserName("") .setAge(10) ?… .setBirthday(new Date());
Reason
The bottom layer of easyexcel uses cglib as the reflection toolkit:
Line 130 of class com.alibaba.excel.read.listener.ModelBuildEventListener BeanMap.create(resultModel).putAll(map); The bottom layer is the method call of cglib's BeanMap abstract public Object put(Object bean, Object key, Object value);
But cglib uses a method of the Introspector class in Java’s rt.jar:
Introspector.java line 520
if (int. class. equals(argTypes[0]) & amp; & amp; name. startsWith(GET_PREFIX)) { pd = new IndexedPropertyDescriptor(this. beanClass, name. substring(3), null, null, method, null); //The following line of judgment only gets the setxxxx method whose return value is void type } else if (void. class. equals(resultType) & amp; & amp; name. startsWith(SET_PREFIX)) { // Simple setter pd = new PropertyDescriptor(this. beanClass, name. substring(3), null, method); if (throwsException(method, PropertyVetoException. class)) { pd.setConstrained(true); } }
Solution
-
Remove the Accessor annotation.
-
Either wait for the author of easyexcel to replace the underlying cglib or something else, anyway, just copy the code to support the setxxx method whose return value is not void.
Do you really know the function and principle of SpringBoot Starter?
An Online Fault: Thoughts on Database Connection Pool Leakage
Interviewer: What are the key points in designing a high-traffic and high-concurrency system?
What is the fuse mechanism of Spring Cloud microservices and the meaning of fuse?
Java project online JVM tuning practice, FullGC greatly reduced
SpringBoot integrates Canal and RabbitMQ to monitor data changes
Reply【Dry goods】Get selected dry goods video tutorials
Reply【Add group】Join the exchange group for solving difficult problems
Reply【mat】A detailed document tutorial for memory overflow problem analysis
Reply【Make money】Get a WeChat robot that can make money in java
Reply【Sideline】Get a programmer’s sideline strategy
Good article, please like + share
The knowledge points of the article match the official knowledge files, and you can further learn relevant knowledge Java skill tree Using JDBC to operate the databaseDatabase operation 118281 people are learning the system