Các cuộc gọi tĩnh trong Java có đắt hơn hay ít tốn kém hơn các cuộc gọi không tĩnh không?


Câu trả lời:


74

Đầu tiên: bạn không nên lựa chọn tĩnh và không tĩnh trên cơ sở hiệu suất.

Thứ hai: trong thực tế, nó sẽ không tạo ra bất kỳ sự khác biệt nào. Hotspot có thể chọn tối ưu hóa theo những cách làm cho các cuộc gọi tĩnh nhanh hơn cho một phương thức, các cuộc gọi không tĩnh nhanh hơn cho một phương thức khác.

Thứ ba: nhiều huyền thoại xung quanh tĩnh và không tĩnh đều dựa trên các JVM rất cũ (không thực hiện được ở bất kỳ đâu gần với sự tối ưu hóa mà Hotspot thực hiện) hoặc một số câu đố đã nhớ về C ++ (trong đó một lệnh gọi động sử dụng thêm một quyền truy cập bộ nhớ hơn một cuộc gọi tĩnh).


1
Bạn hoàn toàn đúng, bạn không nên thích các phương thức tĩnh chỉ dựa trên điều này. Tuy nhiên, trong trường hợp các phương thức tĩnh phù hợp với thiết kế, sẽ rất hữu ích khi biết rằng chúng ít nhất cũng nhanh hơn, nếu không phải là nhanh hơn các phương thức instance và không nên loại trừ dựa trên hiệu suất.
Sẽ

2
@AaronDigulla -.- điều gì sẽ xảy ra nếu tôi nói với bạn rằng tôi đến đây vì tôi đang tối ưu hóa ngay bây giờ, không phải sớm, nhưng khi tôi thực sự cần nó? Bạn cho rằng OP muốn tối ưu hóa quá sớm, nhưng bạn biết rằng trang web này là toàn cầu ... Đúng không? Tôi không muốn trở nên thô lỗ, nhưng xin đừng giả định những điều như thế này vào lần sau.
Dalibor Filus

1
@DaliborFilus Tôi cần tìm số dư. Sử dụng các phương thức tĩnh gây ra tất cả các loại vấn đề, vì vậy cần tránh sử dụng chúng, đặc biệt là khi bạn không biết mình đang làm gì. Thứ hai, hầu hết mã "chậm" là do thiết kế (xấu), không phải vì Ngôn ngữ lựa chọn chậm. Nếu mã của bạn chậm, các phương thức tĩnh có thể sẽ không lưu nó trừ khi các phương thức gọi của nó hoàn toàn không có tác dụng gì . Trong hầu hết các trường hợp, mã trong các phương thức sẽ làm giảm chi phí cuộc gọi.
Aaron Digulla

6
Bị phản đối. Điều này không trả lời câu hỏi. Câu hỏi đặt ra về lợi ích hiệu suất. Nó không hỏi ý kiến ​​về các nguyên tắc thiết kế.
Colm Bhandal

4
Nếu tôi huấn luyện một con vẹt để nói rằng "ăn mòn sớm là gốc rễ của mọi điều xấu xa", tôi sẽ nhận được 1000 phiếu bầu từ những người biết nhiều về hiệu suất như con vẹt.
rghome

62

Bốn năm sau...

Được rồi, với hy vọng giải quyết câu hỏi này một lần và mãi mãi, tôi đã viết một điểm chuẩn cho thấy các loại lệnh gọi khác nhau (ảo, không ảo, tĩnh) so sánh với nhau như thế nào.

Tôi đã chạy nó trên Ideone , và đây là những gì tôi nhận được:

(Số lần lặp càng lớn thì càng tốt.)

    Success time: 3.12 memory: 320576 signal:0
  Name          |  Iterations
    VirtualTest |  128009996
 NonVirtualTest |  301765679
     StaticTest |  352298601
Done.

Như mong đợi, các cuộc gọi phương thức ảo là chậm nhất, các cuộc gọi phương thức không phải ảo nhanh hơn và các cuộc gọi phương thức tĩnh thậm chí còn nhanh hơn.

Điều tôi không ngờ là sự khác biệt rõ rệt như vậy: Các cuộc gọi phương thức ảo được đo để chạy với tốc độ thấp hơn một nửa so với các lệnh gọi phương thức không ảo, lần lượt được đo để chạy chậm hơn 15% so với các lệnh gọi tĩnh. Đó là những gì các phép đo này cho thấy; sự khác biệt thực tế trên thực tế phải rõ ràng hơn một chút, vì đối với mỗi lệnh gọi phương thức ảo, không ảo và tĩnh, mã điểm chuẩn của tôi có thêm một chi phí không đổi bổ sung là tăng một biến số nguyên, kiểm tra một biến boolean và lặp lại nếu không đúng.

Tôi cho rằng kết quả sẽ khác nhau giữa các CPU và từ JVM sang JVM, vì vậy hãy thử và xem bạn nhận được gì:

import java.io.*;

class StaticVsInstanceBenchmark
{
    public static void main( String[] args ) throws Exception
    {
        StaticVsInstanceBenchmark program = new StaticVsInstanceBenchmark();
        program.run();
    }

    static final int DURATION = 1000;

    public void run() throws Exception
    {
        doBenchmark( new VirtualTest( new ClassWithVirtualMethod() ), 
                     new NonVirtualTest( new ClassWithNonVirtualMethod() ), 
                     new StaticTest() );
    }

    void doBenchmark( Test... tests ) throws Exception
    {
        System.out.println( "  Name          |  Iterations" );
        doBenchmark2( devNull, 1, tests ); //warmup
        doBenchmark2( System.out, DURATION, tests );
        System.out.println( "Done." );
    }

    void doBenchmark2( PrintStream printStream, int duration, Test[] tests ) throws Exception
    {
        for( Test test : tests )
        {
            long iterations = runTest( duration, test );
            printStream.printf( "%15s | %10d\n", test.getClass().getSimpleName(), iterations );
        }
    }

    long runTest( int duration, Test test ) throws Exception
    {
        test.terminate = false;
        test.count = 0;
        Thread thread = new Thread( test );
        thread.start();
        Thread.sleep( duration );
        test.terminate = true;
        thread.join();
        return test.count;
    }

    static abstract class Test implements Runnable
    {
        boolean terminate = false;
        long count = 0;
    }

    static class ClassWithStaticStuff
    {
        static int staticDummy;
        static void staticMethod() { staticDummy++; }
    }

    static class StaticTest extends Test
    {
        @Override
        public void run()
        {
            for( count = 0;  !terminate;  count++ )
            {
                ClassWithStaticStuff.staticMethod();
            }
        }
    }

    static class ClassWithVirtualMethod implements Runnable
    {
        int instanceDummy;
        @Override public void run() { instanceDummy++; }
    }

    static class VirtualTest extends Test
    {
        final Runnable runnable;

        VirtualTest( Runnable runnable )
        {
            this.runnable = runnable;
        }

        @Override
        public void run()
        {
            for( count = 0;  !terminate;  count++ )
            {
                runnable.run();
            }
        }
    }

    static class ClassWithNonVirtualMethod
    {
        int instanceDummy;
        final void nonVirtualMethod() { instanceDummy++; }
    }

    static class NonVirtualTest extends Test
    {
        final ClassWithNonVirtualMethod objectWithNonVirtualMethod;

        NonVirtualTest( ClassWithNonVirtualMethod objectWithNonVirtualMethod )
        {
            this.objectWithNonVirtualMethod = objectWithNonVirtualMethod;
        }

        @Override
        public void run()
        {
            for( count = 0;  !terminate;  count++ )
            {
                objectWithNonVirtualMethod.nonVirtualMethod();
            }
        }
    }

    static final PrintStream devNull = new PrintStream( new OutputStream() 
    {
        public void write(int b) {}
    } );
}

Cần lưu ý rằng sự khác biệt về hiệu suất này chỉ áp dụng cho mã không làm gì khác ngoài việc gọi các phương thức không tham số. Bất kỳ mã nào khác mà bạn có giữa các lệnh gọi sẽ làm loãng sự khác biệt và điều này bao gồm cả việc truyền tham số. Trên thực tế, sự khác biệt 15% giữa các cuộc gọi tĩnh và không ảo có lẽ đã được giải thích đầy đủ bởi thực tế là thiscon trỏ không cần phải được chuyển đến phương thức tĩnh. Vì vậy, sẽ chỉ cần một lượng mã khá nhỏ thực hiện những việc vặt vãnh giữa các lệnh gọi để sự khác biệt giữa các loại lệnh gọi khác nhau được pha loãng đến mức không có tác động thực nào.

Ngoài ra, các cuộc gọi phương thức ảo tồn tại vì một lý do; chúng có mục đích phục vụ và chúng được thực hiện bằng cách sử dụng các phương tiện hiệu quả nhất do phần cứng bên dưới cung cấp. (Tập lệnh CPU.) Nếu bạn muốn loại bỏ chúng bằng cách thay thế chúng bằng các lệnh gọi tĩnh hoặc không ảo, cuối cùng bạn phải thêm càng nhiều càng tốt iota mã bổ sung để mô phỏng chức năng của chúng, thì chi phí ròng kết quả của bạn sẽ bị ràng buộc không ít hơn, nhưng nhiều hơn. Rất có thể, nhiều, rất nhiều, không thể tin được nhiều, hơn thế nữa.


7
'Virtual' là một thuật ngữ C ++. Không có phương thức ảo nào trong Java. Có những phương thức thông thường, là phương thức đa hình thời gian chạy, và phương thức tĩnh hoặc phương thức cuối cùng, thì không.
Zhenya

16
@levgen vâng, đối với một người có quan điểm hẹp như tổng quan cấp cao chính thức về ngôn ngữ, thì chính xác như bạn nói. Nhưng tất nhiên các khái niệm cấp cao được thực hiện bằng cách sử dụng các cơ chế cấp thấp được thiết lập tốt, được phát minh từ rất lâu trước khi java ra đời, và các phương thức ảo là một trong số đó. Nếu bạn chỉ nhìn thoáng qua bên dưới, bạn sẽ thấy ngay điều này là như vậy: docs.oracle.com/javase/specs/jvms/se7/html/…
Mike Nakis

13
Cảm ơn bạn đã trả lời câu hỏi mà không đưa ra giả định về việc tối ưu hóa quá sớm. Câu trả lời chính xác.
vegemite4me

3
Vâng, đó chính xác là những gì tôi muốn nói. Dù sao tôi cũng chỉ chạy thử nghiệm trên máy của mình. Ngoài sự rung lắc mà bạn có thể mong đợi cho một điểm chuẩn như vậy, không có sự khác biệt nào VirtualTest | 488846733 -- NonVirtualTest | 480530022 -- StaticTest | 484353198về tốc độ: khi cài đặt OpenJDK của tôi. FTR: Điều đó thậm chí đúng nếu tôi loại bỏ công cụ finalsửa đổi. Btw. Tôi phải ra terminatesân volatile, nếu không bài kiểm tra sẽ không kết thúc.
Marten

4
FYI, tôi nhận được kết quả khá ngạc nhiên trên Nexus 5 chạy Android 6 : VirtualTest | 12451872 -- NonVirtualTest | 12089542 -- StaticTest | 8181170. Không chỉ vậy OpenJDK trên máy tính xách tay của tôi còn thực hiện nhiều lần lặp hơn 40 lần, thử nghiệm tĩnh luôn có thông lượng ít hơn khoảng 30%. Đây có thể là một hiện tượng ART cụ thể, vì tôi nhận được một kết quả mong đợi trên một máy tính bảng Android 4.4:VirtualTest | 138183740 -- NonVirtualTest | 142268636 -- StaticTest | 161388933
Marten

46

Chà, không thể ghi đè các cuộc gọi tĩnh (vì vậy luôn là ứng cử viên cho nội dòng) và không yêu cầu bất kỳ kiểm tra tính rỗng nào. HotSpot thực hiện một loạt các tối ưu hóa thú vị cho các phương thức ví dụ có thể phủ nhận những ưu điểm này, nhưng chúng có thể là lý do tại sao một cuộc gọi tĩnh có thể nhanh hơn.

Tuy nhiên, điều đó sẽ không ảnh hưởng đến thiết kế của bạn - mã theo cách dễ đọc, tự nhiên nhất - và chỉ lo lắng về loại tối ưu hóa vi mô này nếu bạn có lý do (điều mà bạn gần như không bao giờ làm).


Đó là những lý do có thể khiến cuộc gọi tĩnh có thể nhanh hơn. Bạn có thể giải thích cho tôi những lý do đó không?
JavaTechnical

6
@JavaTechnical: Câu trả lời giải thích những lý do đó - không ghi đè (có nghĩa là bạn không cần phải thực hiện để sử dụng mỗi lần bạn có thể nội dòng) và bạn không cần kiểm tra xem bạn có đang gọi phương thức trên một tham chiếu rỗng.
Jon Skeet,

6
@JavaTechnical: Tôi không hiểu. Tôi vừa cung cấp cho bạn những thứ không cần tính toán / kiểm tra các phương thức tĩnh, cùng với cơ hội nội tuyến. Không làm việc một lợi ích về hiệu suất. Những gì còn lại để hiểu?
Jon Skeet,

Các biến tĩnh có được truy xuất nhanh hơn các biến không tĩnh không?
JavaTechnical

1
@JavaTechnical: Không có kiểm tra tính vô hiệu để thực hiện - nhưng nếu trình biên dịch JIT có thể loại bỏ kiểm tra đó (sẽ dành riêng cho ngữ cảnh), tôi sẽ không mong đợi nhiều sự khác biệt. Những thứ như liệu bộ nhớ có trong bộ nhớ đệm hay không sẽ quan trọng hơn nhiều.
Jon Skeet,

18

Nó là trình biên dịch / VM cụ thể.

  • Về lý thuyết , một lệnh gọi tĩnh có thể được thực hiện hiệu quả hơn một chút vì nó không cần phải thực hiện tra cứu hàm ảo và nó cũng có thể tránh được chi phí của tham số ẩn "this".
  • Trong thực tế , nhiều trình biên dịch sẽ tối ưu hóa điều này.

Do đó, nó có lẽ không đáng bận tâm trừ khi bạn đã xác định đây là một vấn đề hiệu suất thực sự nghiêm trọng trong ứng dụng của mình. Tối ưu hóa sớm là gốc rễ của mọi điều xấu xa, v.v.

Tuy nhiên, tôi đã thấy sự tối ưu hóa này làm tăng hiệu suất đáng kể trong tình huống sau:

  • Phương pháp thực hiện một phép tính toán học rất đơn giản mà không cần truy cập bộ nhớ
  • Phương thức được gọi hàng triệu lần mỗi giây trong một vòng lặp chặt chẽ bên trong
  • Ứng dụng ràng buộc CPU nơi mọi bit hiệu suất đều quan trọng

Nếu những điều trên áp dụng cho bạn, nó có thể đáng để thử nghiệm.

Ngoài ra còn có một lý do tốt khác (và thậm chí có khả năng quan trọng hơn!) Để sử dụng phương thức tĩnh - nếu phương thức thực sự có ngữ nghĩa tĩnh (nghĩa là về mặt logic không được kết nối với một phiên bản nhất định của lớp) thì bạn nên làm cho nó tĩnh để phản ánh thực tế này. Các lập trình viên Java có kinh nghiệm sau đó sẽ chú ý đến công cụ sửa đổi tĩnh và ngay lập tức nghĩ rằng "aha! Phương thức này là tĩnh nên nó không cần thể hiện và có lẽ không thao tác trạng thái cụ thể của đối tượng". Vì vậy, bạn sẽ truyền đạt bản chất tĩnh của phương pháp một cách hiệu quả ....


14

Như các áp phích trước đây đã nói: Đây có vẻ như là một sự tối ưu hóa quá sớm.

Tuy nhiên, có một điểm khác biệt (một phần từ thực tế là các lệnh gọi không tĩnh yêu cầu đẩy thêm một đối tượng callee vào ngăn xếp toán hạng):

Vì không thể ghi đè các phương thức tĩnh, nên sẽ không có bất kỳ tra cứu ảo nào trong thời gian chạy cho một cuộc gọi phương thức tĩnh. Điều này có thể dẫn đến sự khác biệt có thể quan sát được trong một số trường hợp.

Sự khác biệt ở mức mã byte là một lệnh gọi phương thức không tĩnh được thực hiện thông qua INVOKEVIRTUAL, INVOKEINTERFACEhoặc INVOKESPECIALtrong khi một lệnh gọi phương thức tĩnh được thực hiện thông qua INVOKESTATIC.


2
Tuy nhiên, một phương thức cá thể riêng tư (ít nhất là thường) được gọi bằng cách sử dụng invokespecialvì nó không phải là ảo.
Mark Peters

Ah, thật thú vị, tôi chỉ có thể nghĩ đến các hàm tạo, đó là lý do tại sao tôi đã bỏ qua nó! Cảm ơn! (đang cập nhật câu trả lời)
aioobe

2
JVM sẽ tối ưu hóa nếu có một loại được khởi tạo. Nếu B mở rộng A và không có phiên bản nào của B được khởi tạo, các cuộc gọi phương thức trên A sẽ không cần tra cứu bảng ảo.
Steve Kuo

13

Không thể tin được rằng bất kỳ sự khác biệt nào về hiệu suất của các cuộc gọi tĩnh so với không tĩnh đang tạo ra sự khác biệt trong ứng dụng của bạn. Hãy nhớ rằng "tối ưu hóa quá sớm là gốc rễ của mọi điều ác".



bạn vui lòng giải thích thêm "" tối ưu hóa quá sớm là gốc rễ của mọi điều ác "."
user2121

Câu hỏi là "Có bất kỳ lợi ích hiệu suất nào theo cách này hay cách khác không?", Và điều này trả lời chính xác câu hỏi đó.
DJClayworth

13

7 năm sau ...

Tôi không tin tưởng lắm vào kết quả mà Mike Nakis đã tìm thấy vì chúng không giải quyết một số vấn đề phổ biến liên quan đến tối ưu hóa Hotspot. Tôi đã công cụ điểm chuẩn bằng cách sử dụng JMH và nhận thấy chi phí của một phương thức phiên bản là khoảng 0,75% trên máy của tôi so với một cuộc gọi tĩnh. Với chi phí thấp, tôi nghĩ rằng ngoại trừ trong các hoạt động nhạy cảm với độ trễ nhất, nó được cho là không phải là mối quan tâm lớn nhất trong thiết kế ứng dụng. Kết quả tóm tắt từ điểm chuẩn JMH của tôi như sau;

java -jar target/benchmark.jar

# -- snip --

Benchmark                        Mode  Cnt          Score         Error  Units
MyBenchmark.testInstanceMethod  thrpt  200  414036562.933 ± 2198178.163  ops/s
MyBenchmark.testStaticMethod    thrpt  200  417194553.496 ± 1055872.594  ops/s

Bạn có thể xem mã ở đây trên Github;

https://github.com/nfisher/svsi

Bản thân điểm chuẩn khá đơn giản nhưng nhằm mục đích giảm thiểu việc loại bỏ mã chết và việc gấp liên tục. Có thể có những cách tối ưu khác mà tôi đã bỏ qua / bỏ qua và những kết quả này có thể sẽ khác nhau tùy theo bản phát hành JVM và hệ điều hành.

package ca.junctionbox.svsi;

import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.Scope;
import org.openjdk.jmh.annotations.State;
import org.openjdk.jmh.infra.Blackhole;

class InstanceSum {
    public int sum(final int a, final int b) {
        return a + b;
    }
}

class StaticSum {
    public static int sum(final int a, final int b) {
        return a + b;
    }
}

public class MyBenchmark {
    private static final InstanceSum impl = new InstanceSum();

    @State(Scope.Thread)
    public static class Input {
        public int a = 1;
        public int b = 2;
    }

    @Benchmark
    public void testStaticMethod(Input i, Blackhole blackhole) {
        int sum = StaticSum.sum(i.a, i.b);
        blackhole.consume(sum);
    }

    @Benchmark
    public void testInstanceMethod(Input i, Blackhole blackhole) {
        int sum = impl.sum(i.a, i.b);
        blackhole.consume(sum);
    }
}

1
Ở đây hoàn toàn có hứng thú học tập. Tôi tò mò về bất kỳ lợi ích tiềm năng nào mà loại tối ưu hóa vi mô này có thể có trên các chỉ số khác ngoài ops/schủ yếu trong môi trường ART (ví dụ: sử dụng bộ nhớ, giảm kích thước tệp .oat, v.v.). Bạn có biết về bất kỳ công cụ / cách tương đối đơn giản nào mà người ta có thể thử để chuẩn các chỉ số này không?
Ryan Thomas

Hotspot chỉ ra rằng không có phần mở rộng nào cho InstanceSum trong classpath. Hãy thử thêm một lớp khác mở rộng InstanceSum và ghi đè phương thức.
milan

12

Đối với quyết định nếu một phương thức phải là tĩnh, khía cạnh hiệu suất sẽ không liên quan. Nếu bạn gặp vấn đề về hiệu suất, việc tạo nhiều phương thức tĩnh sẽ không giúp bạn tiết kiệm thời gian. Điều đó nói rằng, các phương thức tĩnh gần như chắc chắn không chậm hơn bất kỳ phương thức phiên bản nào, trong hầu hết các trường hợp, nhanh hơn một chút :

1.) Các phương thức tĩnh không phải là đa hình, vì vậy JVM có ít quyết định hơn để tìm mã thực để thực thi. Đây là một điểm tranh luận trong Age of Hotspot, vì Hotspot sẽ tối ưu hóa các lệnh gọi phương thức phiên bản chỉ có một trang web triển khai, vì vậy chúng sẽ hoạt động giống nhau.

2.) Một sự khác biệt nhỏ khác là các phương thức tĩnh rõ ràng là không có tham chiếu "this". Điều này dẫn đến một khung ngăn xếp nhỏ hơn một khe của một phương thức thể hiện có cùng chữ ký và nội dung ("cái này" được đặt trong vị trí 0 của các biến cục bộ ở mức bytecode, trong khi đối với phương thức tĩnh, vị trí 0 được sử dụng cho đầu tiên tham số của phương thức).


5

Có thể có sự khác biệt và nó có thể xảy ra theo cả hai cách đối với bất kỳ đoạn mã cụ thể nào và nó có thể thay đổi ngay cả khi một bản phát hành nhỏ của JVM.

Đây chắc chắn là một phần trong số 97% hiệu quả nhỏ mà bạn nên quên .


2
Sai lầm. Bạn không thể giả định bất cứ điều gì. Nó có thể là một vòng lặp chặt chẽ cần thiết cho giao diện người dùng giao diện người dùng, điều này có thể tạo ra sự khác biệt lớn về mức độ "linh hoạt" của giao diện người dùng. Ví dụ: tìm kiếm TableViewhàng triệu bản ghi.
bộ ba

0

Về lý thuyết, ít tốn kém hơn.

Khởi tạo tĩnh sẽ được thực hiện ngay cả khi bạn tạo một thể hiện của đối tượng, trong khi các phương thức tĩnh sẽ không thực hiện bất kỳ khởi tạo nào thường được thực hiện trong một phương thức khởi tạo.

Tuy nhiên, tôi chưa thử nghiệm điều này.


1
@R. Bemrose, khởi tạo tĩnh liên quan gì đến câu hỏi này?
Kirk Woll

@Kirk Woll: Bởi vì Khởi tạo tĩnh được thực hiện lần đầu tiên lớp được tham chiếu ... kể cả trước lần gọi phương thức tĩnh đầu tiên.
Powerlord

@R. Bemrose, chắc chắn, như đang tải lớp vào máy ảo để bắt đầu. Có vẻ như không phải trình tự, IMO.
Kirk Woll

0

Như Jon lưu ý, các phương thức tĩnh không thể bị ghi đè, vì vậy chỉ cần gọi một phương thức tĩnh có thể - trên thời gian chạy Java đủ ngây thơ - nhanh hơn so với việc gọi một phương thức thể hiện.

Nhưng sau đó, ngay cả khi bạn đang ở thời điểm mà bạn quan tâm đến việc xáo trộn thiết kế của mình để tiết kiệm một vài nano giây, điều đó chỉ đưa ra một câu hỏi khác: bạn có cần phương pháp ghi đè chính mình không? Nếu bạn thay đổi mã của mình để biến một phương thức phiên bản thành một phương thức tĩnh để lưu một nano giây ở đây và ở đó, sau đó quay lại và triển khai trình điều phối của riêng bạn trên đó, điều phối viên của bạn gần như chắc chắn sẽ kém hiệu quả hơn so với phương thức được xây dựng vào thời gian chạy Java của bạn rồi.


-2

Tôi muốn thêm vào các câu trả lời tuyệt vời khác ở đây rằng nó cũng phụ thuộc vào luồng của bạn, ví dụ:

Public class MyDao {

   private String sql = "select * from MY_ITEM";

   public List<MyItem> getAllItems() {
       springJdbcTemplate.query(sql, new MyRowMapper());
   };
};

Hãy chú ý rằng bạn tạo một đối tượng MyRowMapper mới cho mỗi cuộc gọi.
Thay vào đó, tôi đề nghị sử dụng ở đây một trường tĩnh.

Public class MyDao {

   private static RowMapper myRowMapper = new MyRowMapper();
   private String sql = "select * from MY_ITEM";

   public List<MyItem> getAllItems() {
       springJdbcTemplate.query(sql, myRowMapper);
   };
};
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.