Pair<Long, String> receives the result of mybatis query and stores BigInteger

Recently, I encountered an incredible problem when working on a project.

code show as below:

My map is converted from List<Pair<Long, String>>:

When the for loop is executed, an exception is thrown:

Tell me that BigInteger cannot be converted to Long? Confused.

It should be that the key of this map is BigInteger. I used Long to receive it and needed type conversion, so I reported an error, so I changed the wording to confirm and see what type the key is:

Really BigInteger!

What I want to do here is to get the two fields in the table. Since the data corresponds one by one, I want to use Map to receive it directly, but Mybatis does not support direct conversion into the Map I want. I have to handle it myself. There are about 3 on the Internet. methods:

1. Write an annotation to implement it yourself,

2. Write a Handler and get it from it

3. Use Pair to receive, this seems the easiest, so I chose this solution.

Looking at the database table structure, it is true that ORG_ID is BigInt, and there is no problem with receiving Bigint with Long before. Why is it converted to BigInteger this time, and there is no error when receiving it, and it does not report an error until it is traversed?

In Mapper: 

Table Structure:

 Mapper.xml

Discussed with colleagues and learned:

But this field in my table is not defined as unsigned, why is it converted to BigInteger for me?

So far, I have two very puzzling doubts:

1. Why is the bigint type field in mysql automatically converted to Biginteger by mybatis when unsigned is not defined? According to the official document, bigint should correspond to Long. Only when it is defined as unsigned, it will become BigInteger if it exceeds the range of Long.

2. Why is it that Long is defined in Pair to receive BigInteger but no error is reported, and no error is reported until it is traversed?

After some hard investigation, I got the result:

1. In the sql statement, the ORG_ID of the select comes from the O table, and the other fields come from the T table. When I checked, I checked the ORG_ID field in the T table. Looking at the O table again, it is true that the ORG_ID field in the O table is defined as unsigned unsigned, so when it is received, mybatis converts it to Biginteger.

2. But there is still a problem, why is there no error when using Long to receive BigInteger, and it does not report an error until it runs to the for loop traversal?

This is because of the type erasure mechanism of JAVA. Here is a reprint of the information I saw:

What is type erasure?

This is a "feature" that is unique to Java. At runtime, Java does not preserve generic types.

For example, you can put a dog in a group of cats:

LinkedList<Cat> cats = new LinkedList<Cat>();
LinkedList list = cats;  // 注意我在这里把范型去掉了,但是list和cats是同一个链表!
list.add(new Dog());  // 完全没问题!

Why can you play like this? Because Java's generic type only exists in the source code, it will statically check whether the generic type is correct for you when compiling, but it will not be checked at runtime. The above code looks no different from the following paragraph in the JRE (Java Runtime Environment):

LinkedList cats = new LinkedList();  // 注意:没有范型!
LinkedList list = cats;
list.add(new Dog());

Why type erasure?

For backward compatibility.

Java 5 only introduced generic types, and there were no generic types before that. At that time, the generic type was introduced only to solve the problem of manual forced transformation when traversing the collection , such as code like this:

List cats = loadCats();  // 5之前没有范型哦

for (Object obj : cats) {
  Cat cat = (Cat) obj;  // 所以这里总是需要强转
  ...
}

In order to keep the JVM backward compatible, type erasure is a bad idea.

Consequences of type erasure

Type erasure has a lot of bad consequences. Here are three that I know of (the name is my own, not an official name):

reflection dilemma

Because there is no generic type at runtime, many reflection-based implementations need to provide additional generic type information. For example, Gson will be confused when deserializing JSON strings to the following types of objects:

public class Foo {
  private LinkedList<Bar> bars;
  // 省略 get 和 set 方法
}

Why? Because Gson can't get the type of elements in bars through reflection at runtime! So for this case, you need more convoluted and counter-intuitive code to solve the problem.

type number explosion

The second problem with type erasure is the type count explosion. For example, why does the built-in functional interface of Java 8 have Supplier, Function and BiFunction? It is because Java cannot use the number of generic types to distinguish types, because the generic types are erased. C# does not have type erasure, so it can be used to Func<R>represent one type, Func<T, R>another type, Func<T1, T2, R>and another type.

Basic Data Type Discrimination

A less obvious consequence of this mechanism is that basic data types are discriminated against.

Since generics are erased at runtime, the runtime cannot calculate the memory size required by the generic type. How to do it? The Java design team thought of another crooked trick: the generic type can only be a reference type! Anyway, no matter what type of reference occupies the same memory size. Basic data types are not allowed to be used because each has its own size.

Solve one problem and introduce a new one: What should I do if I need to throw basic data types into the collection? Pack! So there are packaging classes for various basic data types (by the way, the packaging class for long is Long, the packaging class for double is Double, and the packaging class for int is not Int?), what else is automatic boxing and unpacking? The "property" of the box.

By the way, C# 's generic type will be preserved until runtime, so C#'s generic type can be a value type (similar to Java's basic data type but more powerful), and there is no need for boxing and unboxing operations.

Continuing to complain, the discrimination of basic data types will also aggravate the explosion of the number of types, because it and the number of paradigms are not in the same dimension, so the explosion coefficients caused by each must be multiplied! For example, the Supplier mentioned above, if you want to return an int, you can’t use it Supplier<int>(because the generic type cannot be a basic data type) but must use it IntSupplier, and you must use it if you want to return a double DoubleSupplier. Similarly, there are IntFunction, DoubleFunction, LongFunction, ToLongFunction, IntToDoubleFunctionetc., and each combination of parameters and return types must define an interface separately. Java you are really hopeless. (Further complaints, the function that returns boolean is not called BooleanFunction, but Predicate! According to the urine design of Java functional interface, BiPredicate, IntPredicate, LongPredicate, DoublePredicate are derived, really drunk)

I counted java.util.function43 functional interfaces in the package! And this is not the end, because not all basic data types are covered, such as byte , char and short are not covered. If you need support for these types, sorry, you'll have to write the functional interface yourself! And even if it is written, Java's stream interface still doesn't recognize it!

conclusion

The Java paradigm is like an unwelcome illegitimate child. Its birth was just a quick result, and it has not been cared by its biological father since then, which has caused various subsequent social problems. It can be said that C++ templates are better than it, not to mention the true paradigm of C#. I think that in order for Java to make great progress in language features , it is necessary to clean up these historical issues at some point, instead of blindly insisting on backward compatibility without principle, otherwise it will only become more and more ugly.

Author: Aetherus
Link: https://www.zhihu.com/question/452958728/answer/1817841881

Guess you like

Origin blog.csdn.net/qq_16382227/article/details/126423554