Understand and use lambda in Java

premise

Recall that jdk8 was officially released in 2014, More than five years have passed since (2020-02-08). Two powerful new features introduced by jdk8 are lambda expressions (lambda in the following specifically refers to the lambda provided by JDK) and stream. These two powerful features make functional programming flourish in java development. This article will introduce the whole picture of lambda from the perspectives of basic concepts, usage methods, implementation principles and practical scenarios, and also involve some functional programming concepts, JVM knowledge, etc.

Basic concepts

The following introduces some basic concepts and leads to the concept of lambda step by step.

Functional interface

Functional interfaces and interface default methods are new features introduced by jdk8. The concept of functional interface can be derived from Java From the API notes of lang.functionalinterface annotation:

Simply put, @ functionalinterface is an interface that provides information (in fact, it is an identification interface) to indicate that the corresponding interface type declaration is a functional interface defined by the Java language specification. Conceptually, a functional interface has and only has one extraction method, because the default methods of the interface must be implemented, and they are not abstract methods.

Therefore, a functional interface can be defined as follows: if there is only one abstract method when an interface is declared, it is a functional interface, which can be identified by @ functional interface annotation.

Many built-in functional interfaces have been defined in JDK, such as:

// java.lang.Runnable
@FunctionalInterface
public interface Runnable {

    public abstract void run();
}  

// java.util.function.supplier
@FunctionalInterface
public interface supplier<T> {

    T get();
}

You can also customize functional interfaces, such as:

@FunctionalInterface
public interface CustomFunctionalInterface {
    
    // 可以缩写为void process();  接口方法定义的时候,默认使用public abstract修饰
    public abstract void process();
}

Interface default method

See the corresponding chapter in the official java tutorial for the meaning of the interface default method. You can see the specific link in the resources at the end of the article:

Simply put: default methods allow you to add new functionality to interfaces in your class library and ensure that the new default methods are binary compatible with code written in earlier versions of these interfaces.

Interface default method (hereinafter referred to as the default method) the method body can be written directly in the interface through the default keyword declaration. That is, the default method not only declares the method, but also implements the method. This is very important. Before the emergence of the default method feature, in the Java programming language specification, the essence of the interface is the aggregation of the method declaration, and after the emergence of the default method feature, the essence of the interface Also changed. An example of the default method is as follows:

public interface DefaultMethod {

    default void defaultVoidMethod() {

    }

    default String sayHello(String name) {
        return String.format("%s say hello!",name);
    }

    static void main(String[] args) throws Exception {
        class Impl implements DefaultMethod {

        }
        DefaultMethod defaultMethod = new Impl();
        System.out.println(defaultMethod.sayHello("throwable"));  // throwable say hello!
    }
}

If you inherit an interface that defines a default method, you can do the following:

Combined with the functional interface mentioned in the previous section, we can draw a conclusion: functional interface, that is, the interface with only one abstract method, can define 0 or n (n > = 1) default methods. This is the theoretical basis for the introduction of stream feature. For example:

@FunctionalInterface
public interface CustomFunctionalInterface {

    public abstract void process();

    default void defaultVoidMethod() {

    }

    default String sayHello(String name) {
        return String.format("%s say hello!",name);
    }
}

Here's a digression.

When writing this article, the author remembered what a former colleague said to the following effect: in software engineering, if we start from scratch, the development of any new function is very simple. The difficulty is to iterate the new function on the premise of compatibility with all historical functions. Imagine that it has been more than ten years since Java iteration, The hotspot VM source code project is already huge (anyone who has manually compiled the source code of openjdk hotspot VM knows the pain of the process). Any new features should be forward compatible, otherwise many Java applications using historical versions will not be able to upgrade to the new JDK version. Java needs to give up both binary forward compatibility and iterative new features. The default method is an example. The interface must be omitted, and only extraction can be defined Like method, which has been deeply rooted in Java developers for many years, has captured the streaming programming system based on the default method implementation. The author sometimes thinks: if I were to develop the new feature stream, what would I do or what could I do?

Nested classes

Nested classes, to put it simply: if another class is defined in a class, the class defined in the class is a nested class, and the outermost class is generally called enclosing class. Nested classes are mainly divided into two types: static nested classes and non static nested classes, while non static nested classes are also called inner classes.

// 封闭类
class OuterClass {
    ...
    // 静态嵌套类
    static class StaticNestedClass {
        ...
    }
    
    // 内部类
    class InnerClass {
        ...
    }
}

Static nested classes can be accessed directly with closed class names, such as outerclass StaticNestedClass x = new OuterClass. StaticNestedClass();, This form of use is basically no different from general class instantiation.

The existence of an internal class instance must depend on the existence of a closed class instance, and the internal class can directly access any properties and methods of the closed class. In short, the instantiation of the internal class must be after the instantiation of the closed class and depends on the instance of the closed class. The declared syntax is a little strange:

public class OuterClass {

    int x = 1;

    static class StaticNestedClass {

    }

    class InnerClass {
        // 内部类可以访问封闭类的属性
        int y = x;
    }

    public static void main(String[] args) throws Exception {
        OuterClass outerClass = new OuterClass();

        // 必须这样实例化内部类 - 声明的语法相对奇特
        OuterClass.InnerClass innerClass = outerClass.new InnerClass();

        // 静态嵌套类可以一般实例化,形式为:封闭类.静态嵌套类
        OuterClass.StaticNestedClass staticNestedClass = new OuterClass.StaticNestedClass();

        // 如果main方法在封闭类内,可以直接使用静态嵌套类进行实例化
        StaticNestedClass x = new StaticNestedClass();
    }
}

There are two special types in inner classes: local classes and anonymous classes.

A local class is a class declared in any block, such as a code block, static code block, instance method or static method. It can access all member properties and methods of a closed class. Its scope is within the block and cannot be used outside the block. For example:

public class OuterClass {

    static int y = 1;
    
    {    
        // 本地类A
        class A{
            int z = y;
        }
        A a = new A();
    }

    static {
        // 本地类B
        class B{
            int z = y;
        }
        B b = new B();
    }

    private void method(){
        // 本地类C
        class C{
            int z = y;
        }
        C c = new C();
    }
}

Anonymous classes can make the code more concise and allow users to implement them while defining classes. The difference between anonymous classes and other internal classes is that it is an expression rather than a class declaration. For example:

public class OuterClass {

    interface In {

        void method(String value);
    }
    
    public void sayHello(){
        // 本地类 - 类声明
        class LocalClass{
            
        }
        
        // 匿名类 - 是一个表达式
        In in = new In() {
            
            @Override
            public void method(String value) {
                
            }
        };
    }
}

If you have done GUI development in Java, anonymous classes are widely used in event callbacks in swing or JavaFX. You often see codes like this:

JButton button = new JButton();
button.addActionListener(new AbstractAction() {
    @Override
    public void actionPerformed(ActionEvent e) {
        System.out.println("按钮事件被触发...");
    }
});

The type diagram of nested classes is as follows:

Nested Classes
  - Static Nested Classes
  - None Nested Classes
    - Local Classes
    - Anonymous Classes
    - Other Inner Classes

Lambda expression

The following is the definition of lambda expression from a search engine Encyclopedia:

Lambda expression is an anonymous function. Lambda expression is based on λ The name of the calculus directly corresponds to the lambda abstraction. It is an anonymous function, that is, a function without a function name. Lambda expressions can represent closures (note that they are different from the traditional sense of Mathematics).

Lambda expression in Java (hereinafter referred to as lambda) is similar to the above definition on the surface and is also an anonymous function in essence, but its implementation principle is different from the implementation of anonymous functions in general anonymous classes. It is a new syntax sugar introduced by jdk8.

Original intention of introducing lambda expression

If an interface contains only one method, the syntax of anonymous classes will become very clumsy and unclear, resulting in a large amount of template code. To sum up, code redundancy is the biggest disadvantage of anonymous classes. In programming, we often want to transfer functions as parameters to another method. Lambda is born for this. Lambda allows users to treat functions as method parameters and codes as data. The introduction of lambda brings the following advantages:

So far, it is also concluded that lambda is only applicable to the implementation of the unique abstract method corresponding to the functional interface.

Syntax definition of lambda expression

Lambda syntax is defined in detail as follows:

// en_US
InterfaceType interfaceObject = [Method Argument List] -> Method Body

// zh_CN
接口类型 接口实例 = [方法参数列表] -> 方法体

A more specific description should be:

Interface type interface instance temporary variable = (method parameter type X method parameter type X temporary variable, method parameter type Y method parameter type Y temporary variable...) - > {method body... Return interface abstract method return value corresponds to type instance;}

@H_ 404_ 300@

A lambda expression consists of five parts:

Some examples:

// Function - 具体
java.util.function.Function<String,Integer> functionY = (String string) -> {
    return Integer.parseInt(string);
};
// Function - 简化
java.util.function.Function<String,Integer> functionX = string -> Integer.parseInt(string);

// Runnable - 具体
Runnable runnableX = () -> {
    System.out.println("Hello World!");
};
// Runnable - 简化
Runnable runnableY = () -> System.out.println("Hello World!");

// 整数1-100的和 - 具体
int reduceX = IntStream.range(1,101).reduce(0,(int addend,int augend) -> {
    return addend + augend;
});
// 整数1-100的和 - 简化
int reduceY = IntStream.range(1,Integer::sum);

Target type and type inference

First introduce the following scenario:

// club.throwable.Runnable
@FunctionalInterface
public interface Runnable {

    void run();

    static void main(String[] args) throws Exception {
        java.lang.Runnable langRunnable = () -> {};
        club.throwable.Runnable customRunnable = () -> {};
        langRunnable.run();
        customRunnable.run();
    }
}

The author defines a and Java Lang. runnable is a fully consistent functional interface club throwable. Runnable, in the main () method above, we can see that the method body implementation of the lambda expression corresponding to the two interfaces is also completely consistent, but it is obvious that different types of interfaces can be used to receive the return value, that is, the types of the two lambda are different. The return value type of these two lambda expressions is the expected a data type of XX, so the lambda expression is the corresponding expected type, and the expected type is the target type of the lambda expression.

In order to determine the target type of lambda expression, the java compiler will conduct comprehensive derivation using context or scenario based on the corresponding lambda expression. One factor of judgment is the type expected for the lambda expression in the context. Therefore, lambda expressions can only be used when the java compiler can correctly infer the target type of lambda expressions. These scenarios include:

In addition to the target type, lambda expression also contains parameter list and method body, and the method body needs to be implemented by relying on the parameter list, so the method parameter is also a factor determining the target type.

The process of type derivation of method parameters mainly depends on two language features: overload resolution and type argument inference.

Overload resolution will find the most appropriate method declaration for a given method invocation (method declaration). Because different declarations have different signatures, when a lambda expression is used as a method parameter, overload resolution will affect the target type of the lambda expression. The compiler will make a decision based on its understanding of the information provided by the lambda expression. If the lambda expression has an explicit type (the parameter type is explicitly specified), the compiler can directly use the return type of the lambda expression; if the lambda expression has an implicit type (the parameter type is known by derivation), overload resolution will ignore the lambda expression function body and only depend on the number of lambda expression parameters.

for instance:

// 显式类型
Function<String,String> functionX = (String x) -> x;

// 隐式类型
Function<String,Integer> functionY = x -> Integer.parseInt(x);

If there is ambiguity when deriving the best method declaration depending on the type of method parameter, we need to use cast or explicit lambda expression to provide more type information, so as to the target type of lambda expression. For example:

// 编译不通过
Object runnableX = () -> {};

// 编译通过 - Cast
Object runnableY = (Runnable) () -> {};


// 静态方法入参类型是函数式接口
public static void function(java.util.function.Function function) {

}

function((Function<String,Long>) (x) -> Long.parseLong(x));

Scope

With regard to scope, just remember a few points:

For point < 1 >, for example:

public class LambdaThis {

    int x = 1;

    public void method() {
        Runnable runnable = () -> {
            int y = this.x;
            y++;
            System.out.println(y);
        };
        runnable.run();
    }

    public static void main(String[] args) throws Exception {
        LambdaThis lambdaThis = new LambdaThis();
        lambdaThis.method();   // 2
    }
}

For point < 2 >, for example:

public class LambdaScope {
    
    public void method() {
        int x = 1;
        Runnable runnable = () -> {
            // 编译不通过 - Lambda方法体外部已经定义了同名变量
            int x = 2;
        };
        runnable.run();
    }
}

For point < 3 >, for example:

public class LambdaValue {

    public void method() {
        (final) int x = 1;
        Runnable runnable = () -> {
            // 编译不通过 - 外部值类型使用了final
            x ++;
        };
        runnable.run();
    }
}

public class LambdaValue {

    public void method() {
        (final) IntHolder holder = new IntHolder();
        Runnable runnable = () -> {
            // 编译通过 - 使用了引用类型
            holder.x++;
        };
        runnable.run();
    }

    private static class IntHolder {

        int x = 1;
    }
}

Method reference

Method reference is an expression whose function is similar to lambda expression. It requires the target type and implements the functional interface, but this implementation is not through the method body, but through the method name (or keyword) associating with an existing method is essentially a compilation level technology, which aims to further simplify the implementation of lambda expression method body and some specific expressions. The types of method references are summarized as follows:

It can be seen that its basic form is: Method container:: method name or keyword.

Give some basic examples:

// 静态方法引用
public class StaticMethodRef {

    public static void main(String[] args) throws Exception {
        Function<String,Integer> function = StaticMethodRef::staticMethod;
        Integer result = function.apply("10086");
        System.out.println(result);  // 10086
    }

    public static Integer staticMethod(String value) {
        return Integer.parseInt(value);
    }
}

// 指定对象实例方法引用
public class ParticularInstanceRef {

    public Integer refMethod(String value) {
        return Integer.parseInt(value);
    }

    public static void main(String[] args) throws Exception{
        ParticularInstanceRef ref = new ParticularInstanceRef();
        Function<String,Integer> function = ref::refMethod;
        Integer result = function.apply("10086");
        System.out.println(result);  // 10086
    }
}

// 特定类型任意对象方法引用
String[] stringArray = {"C","a","B"};
Arrays.sort(stringArray,String::compareToIgnoreCase);
System.out.println(Arrays.toString(stringArray)); // [a,B,C]

// 超类方法引用
public class SupperRef {

    public static void main(String[] args) throws Exception {
        Sub sub = new Sub();
        System.out.println(sub.refMethod("10086")); // 10086
    }

    private static class Supper {

        private Integer supperRefMethod(String value) {
            return Integer.parseInt(value);
        }
    }

    private static class Sub extends Supper {

        private Integer refMethod(String value) {
            Function<String,Integer> function = super::supperRefMethod;
            return function.apply(value);
        }
    }
}

// 构造器方法引用
public class ConstructorRef {

    public static void main(String[] args) throws Exception {
        Function<String,Person> function = Person::new;
        Person person = function.apply("doge");
        System.out.println(person.getName()); // doge
    }

    private static class Person {

        private final String name;

        public Person(String name) {
            this.name = name;
        }

        public String getName() {
            return name;
        }
    }
}

// 数组构造器方法引用
Function<Integer,Integer[]> function = Integer[]::new;
Integer[] array = function.apply(10);
System.out.println(array.length); // 10

Underlying implementation principle of lambda in Java

The key point is to say three times:

Before further studying lambda expressions, the author once thought that lambda is the syntax sugar of anonymous classes:

// Lambda
Function<String,String> functionX = (String x) -> x;

// 错误认知
Function<String,String> functionX = new Function<String,String>() {
    @Override public Void apply(String x) {
        return x;
    }
}

Lambda is the syntax sugar of anonymous class, which is wrong. The following is an example to analyze the whole process of lambda expression compilation and execution from the perspective of source code and bytecode.

public class Sample {

    public static void main(String[] args) throws Exception {
        Runnable runnable = () -> {
            System.out.println("Hello World!");
        };
        runnable.run();
        String hello = "Hello ";
        Function<String,String> function = string -> hello + string;
        function.apply("Doge");
    }
}

Add VM parameter - djdk internal. lambda. dumpProxyClasses=. Run the sample#main () method above, and the project root directory dynamically generates two classes as follows:

import java.lang.invoke.LambdaForm.Hidden;

// $FF: synthetic class
final class Sample$$Lambda$14 implements Runnable {
    private Sample$$Lambda$14() {
    }

    @Hidden
    public void run() {
        Sample.lambda$main$0();
    }
}

import java.lang.invoke.LambdaForm.Hidden;
import java.util.function.Function;

// $FF: synthetic class
final class Sample$$Lambda$15 implements Function {
    private final String arg$1;

    private Sample$$Lambda$15(String var1) {
        this.arg$1 = var1;
    }

    private static Function get$Lambda(String var0) {
        return new Sample$$Lambda$15(var0);
    }

    @Hidden
    public Object apply(Object var1) {
        return Sample.lambda$main$1(this.arg$1,(String)var1);
    }
}

The bytecode of the two classes is inversely checked and the class modifier is found to be final synthetic. Next, look directly at the bytecode of the closed class sample:

public class club/throwable/Sample {
     <ClassVersion=52>
     <SourceFile=Sample.java>

     public Sample() { // <init> //()V
         <localVar:index=0,name=this,desc=Lclub/throwable/Sample;,sig=null,start=L1,end=L2>

         L1 {
             aload0 // reference to self
             invokespecial java/lang/Object.<init>()V
             return
         }
         L2 {
         }
     }

     public static main(java.lang.String[] arg0) throws java/lang/Exception { //([Ljava/lang/String;)V
         <localVar:index=0,name=args,desc=[Ljava/lang/String;,end=L2>
         <localVar:index=1,name=runnable,desc=Lclub/throwable/Runnable;,start=L3,end=L2>
         <localVar:index=2,name=hello,desc=Ljava/lang/String;,start=L4,end=L2>
         <localVar:index=3,name=function,desc=Ljava/util/function/Function;,sig=Ljava/util/function/Function<Ljava/lang/String;Ljava/lang/String;>;,start=L5,end=L2>

         L1 {
             invokedynamic java/lang/invoke/LambdaMetafactory.Metafactory(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite; : run()Lclub/throwable/Runnable; ()V club/throwable/Sample.lambda$main$0()V (6) ()V
             astore1
         }
         L3 {
             aload1
             invokeinterface club/throwable/Runnable.run()V
         }
         L6 {
             ldc "Hello " (java.lang.String)
             astore2
         }
         L4 {
             aload2
             invokedynamic java/lang/invoke/LambdaMetafactory.Metafactory(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite; : apply(Ljava/lang/String;)Ljava/util/function/Function; (Ljava/lang/Object;)Ljava/lang/Object; club/throwable/Sample.lambda$main$1(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String; (6) (Ljava/lang/String;)Ljava/lang/String;
             astore3
         }
         L5 {
             aload3
             ldc "Doge" (java.lang.String)
             invokeinterface java/util/function/Function.apply(Ljava/lang/Object;)Ljava/lang/Object;
             pop
         }
         L7 {
             return
         }
         L2 {
         }
     }

     private static synthetic lambda$main$1(java.lang.String arg0,java.lang.String arg1) { //(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;
         <localVar:index=0,name=string,end=L2>

         L1 {
             new java/lang/StringBuilder
             dup
             invokespecial java/lang/StringBuilder.<init>()V
             aload0 // reference to arg0
             invokevirtual java/lang/StringBuilder.append(Ljava/lang/String;)Ljava/lang/StringBuilder;
             aload1
             invokevirtual java/lang/StringBuilder.append(Ljava/lang/String;)Ljava/lang/StringBuilder;
             invokevirtual java/lang/StringBuilder.toString()Ljava/lang/String;
             areturn
         }
         L2 {
         }
     }

     private static synthetic lambda$main$0() { //()V
         L1 {
             getstatic java/lang/System.out:java.io.PrintStream
             ldc "Hello World!" (java.lang.String)
             invokevirtual java/io/PrintStream.println(Ljava/lang/String;)V
         }
         L2 {
             return
         }
     }
// The following inner classes Couldn't be decompiled: java/lang/invoke/MethodHandles$Lookup 
}

The above bytecode has been formatted by bytecode viewer tool, which is in line with people's reading habits. From the reading of bytecode and combined with the previous analysis, the following conclusions can be drawn:

Due to space limitation, the underlying principle of lambda expression is briefly combed here (the derivation process is limited to personal understanding, and the basis is not sufficient):

If you want to track the whole call life cycle of lambda expression, you can start debug with lambdametafactory #metafactory () method as the entry. The call link is very large and you need to be patient. To sum up, lambda expressions are based on the dynamic language call package Java. Com introduced by jsr-292 The lightweight template class defined by lang. invoke and unsafe #defineanonymousclass() is implemented, mainly using invokedynamic bytecode instructions, which are associated with relatively complex knowledge points such as method handle and call point callsite. It will not be expanded in detail here.

actual combat

Lightweight Dao encapsulation based on jdbctemplate

It is assumed that the DDL of the order table is as follows:

CREATE TABLE `t_order`
(
    id          BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,create_time DATETIME        NOT NULL DEFAULT CURRENT_TIMESTAMP,edit_time   DATETIME        NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,user_id     BIGINT UNSIGNED NOT NULL COMMENT '用户ID',order_id    VARCHAR(64)     NOT NULL COMMENT '订单ID',amount      DECIMAL(12,2)  NOT NULL DEFAULT 0 COMMENT '订单金额',INDEX idx_user_id (user_id),UNIQUE uniq_order_id (order_id)
) COMMENT '订单表';

The following encapsulates a lightweight orderdao based on the jdbctemplate:

// 辅助接口
@FunctionalInterface
public interface PreparedStatementProcessor {

    void process(PreparedStatement ps) throws sqlException;
}

@FunctionalInterface
public interface ResultSetConverter<T> {

    T convert(ResultSet resultSet) throws sqlException;
}

// OrderDao接口
public interface OrderDao {

    int insertSelective(Order record);

    int updateSelective(Order record);

    Order selectOneByOrderId(String orderId);

    List<Order> selectByUserId(Long userId);
}

// OrderDao实现
@Repository
@requiredArgsConstructor
public class MysqLOrderDao implements OrderDao {

    private final JdbcTemplate jdbcTemplate;

    private static final ResultSetConverter<Order> CONVERTER = r -> {
        Order order = new Order();
        order.setId(r.getLong("id"));
        order.setCreateTime(r.getTimestamp("create_time").toLocalDateTime());
        order.setEditTime(r.getTimestamp("edit_time").toLocalDateTime());
        order.setUserId(r.getLong("user_id"));
        order.setAmount(r.getBigDecimal("amount"));
        order.setOrderId(r.getString("order_id"));
        return order;
    };

    private static final ResultSetExtractor<List<Order>> MULTI = r -> {
        List<Order> list = new ArrayList<>();
        while (r.next()) {
            list.add(CONVERTER.convert(r));
        }
        return list;
    };

    private static final ResultSetExtractor<Order> SINGLE = r -> {
        if (r.next()) {
            return CONVERTER.convert(r);
        }
        return null;
    };

    @Override
    public int insertSelective(Order record) {
        List<PreparedStatementProcessor> processors = new ArrayList<>();
        StringBuilder sql = new StringBuilder("INSERT INTO t_order(");
        Cursor cursor = new Cursor();
        if (null != record.getId()) {
            int idx = cursor.add();
            sql.append("id,");
            processors.add(p -> p.setLong(idx,record.getId()));
        }
        if (null != record.getOrderId()) {
            int idx = cursor.add();
            sql.append("order_id,");
            processors.add(p -> p.setString(idx,record.getOrderId()));
        }
        if (null != record.getUserId()) {
            int idx = cursor.add();
            sql.append("user_id,record.getUserId()));
        }
        if (null != record.getAmount()) {
            int idx = cursor.add();
            sql.append("amount,");
            processors.add(p -> p.setBigDecimal(idx,record.getAmount()));
        }
        if (null != record.getCreateTime()) {
            int idx = cursor.add();
            sql.append("create_time,");
            processors.add(p -> p.setTimestamp(idx,Timestamp.valueOf(record.getCreateTime())));
        }
        if (null != record.getEditTime()) {
            int idx = cursor.add();
            sql.append("edit_time,Timestamp.valueOf(record.getEditTime())));
        }
        StringBuilder realsql = new StringBuilder(sql.substring(0,sql.lastIndexOf(",")));
        realsql.append(") VALUES (");
        int idx = cursor.idx();
        for (int i = 0; i < idx; i++) {
            if (i != idx - 1) {
                realsql.append("?,");
            } else {
                realsql.append("?");
            }
        }
        realsql.append(")");
        // 传入主键的情况
        if (null != record.getId()) {
            return jdbcTemplate.update(realsql.toString(),p -> {
                for (PreparedStatementProcessor processor : processors) {
                    processor.process(p);
                }
            });
        } else {
            // 自增主键的情况
            KeyHolder keyHolder = new GeneratedKeyHolder();
            int count = jdbcTemplate.update(p -> {
                PreparedStatement ps = p.prepareStatement(realsql.toString(),Statement.RETURN_GENERATED_KEYS);
                for (PreparedStatementProcessor processor : processors) {
                    processor.process(ps);
                }
                return ps;
            },keyHolder);
            record.setId(Objects.requireNonNull(keyHolder.getKey()).longValue());
            return count;
        }
    }

    @Override
    public int updateSelective(Order record) {
        List<PreparedStatementProcessor> processors = new ArrayList<>();
        StringBuilder sql = new StringBuilder("UPDATE t_order SET ");
        Cursor cursor = new Cursor();
        if (null != record.getOrderId()) {
            int idx = cursor.add();
            sql.append("order_id = ?,record.getOrderId()));
        }
        if (null != record.getUserId()) {
            int idx = cursor.add();
            sql.append("user_id = ?,record.getUserId()));
        }
        if (null != record.getAmount()) {
            int idx = cursor.add();
            sql.append("amount = ?,record.getAmount()));
        }
        if (null != record.getCreateTime()) {
            int idx = cursor.add();
            sql.append("create_time = ?,Timestamp.valueOf(record.getCreateTime())));
        }
        if (null != record.getEditTime()) {
            int idx = cursor.add();
            sql.append("edit_time = ?,")));
        int idx = cursor.add();
        processors.add(p -> p.setLong(idx,record.getId()));
        realsql.append(" WHERE id = ?");
        return jdbcTemplate.update(realsql.toString(),p -> {
            for (PreparedStatementProcessor processor : processors) {
                processor.process(p);
            }
        });
    }

    @Override
    public Order selectOneByOrderId(String orderId) {
        return jdbcTemplate.query("SELECT * FROM t_order WHERE order_id = ?",p -> p.setString(1,orderId),SINGLE);
    }

    @Override
    public List<Order> selectByUserId(Long userId) {
        return jdbcTemplate.query("SELECT * FROM t_order WHERE order_id = ?",p -> p.setLong(1,userId),MULTI);
    }

    private static class Cursor {

        private int idx;

        public int add() {
            idx++;
            return idx;
        }

        public int idx() {
            return idx;
        }
    }
}

Similar to the mybatis generator, the author has made a simple generator for the above Dao implementation. As long as the connection properties and table filtering rules of the data source are configured, the corresponding entity classes and Dao classes can be generated.

VO setting value based on optional

// 假设VO有多个层级,每个层级都不知道父节点是否为NULL,如下
// - OrderInfoVo
//   - UserInfoVo
//     - AddressInfoVo
//        - address(属性)
// 假设我要为address属性赋值,那么就会产生箭头型代码。

// 常规方法
String address = "xxx";
OrderInfoVo o = ...;
if(null != o){
    UserInfoVo uiv = o.getUserInfoVo();
    if (null != uiv){
        AddressInfoVo aiv = uiv.getAddressInfoVo();
        if (null != aiv){
            aiv.setAddress(address);
        }
    }
}

// 使用Optional和Lambda
String address = "xxx";
OrderInfoVo o = ...;
Optional.ofNullable(o).map(OrderInfoVo::getUserInfoVo).map(UserInfoVo::getAddressInfoVo).ifPresent(a -> a.setAddress(address));

Summary

Lambda is a sweet grammar candy in Java. It embraces lambda and functional programming. The author has also experienced the process of resisting, not optimistic, getting started and really fragrant. At present, stream and lambda are also widely used, which can simplify the code as much as possible and liberate the labor force on the premise of ensuring the performance. The times are progressing, and Java is also progressing. This is the belief that many people live and adhere to the cause of programming.

reference material:

Personal blog

(end of this paper e-a-20200208 c-5-d)

The official account of Technology (Throwable Digest), which is not regularly pushed to the original technical article (never copied or copied):

Entertainment official account ("sand sculpture"), select interesting sand sculptures, videos and videos, push them to relieve life and work stress.

The content of this article comes from the network collection of netizens. It is used as a learning reference. The copyright belongs to the original author.
THE END
分享
二维码
< <上一篇
下一篇>>