জেডিবিসি-র সাথে কীভাবে চেষ্টা করার চেষ্টা করব?


148

জেডিবিসির সাথে একটি ডাটাবেস থেকে ব্যবহারকারীদের পাওয়ার জন্য আমার একটি পদ্ধতি রয়েছে:

public List<User> getUser(int userId) {
    String sql = "SELECT id, name FROM users WHERE id = ?";
    List<User> users = new ArrayList<User>();
    try {
        Connection con = DriverManager.getConnection(myConnectionURL);
        PreparedStatement ps = con.prepareStatement(sql); 
        ps.setInt(1, userId);
        ResultSet rs = ps.executeQuery();
        while(rs.next()) {
            users.add(new User(rs.getInt("id"), rs.getString("name")));
        }
        rs.close();
        ps.close();
        con.close();
    } catch (SQLException e) {
        e.printStackTrace();
    }
    return users;
}

এই কোডটি উন্নত করতে আমার কীভাবে জাভা 7 ব্যবহারের চেষ্টা করব ?

আমি নীচের কোডটি দিয়ে চেষ্টা করেছি, তবে এটি অনেকগুলি tryব্লক ব্যবহার করে, এবং পাঠযোগ্যতার বেশি উন্নতি করে না । আমার try-with-resourcesঅন্য উপায়ে ব্যবহার করা উচিত ?

public List<User> getUser(int userId) {
    String sql = "SELECT id, name FROM users WHERE id = ?";
    List<User> users = new ArrayList<>();
    try {
        try (Connection con = DriverManager.getConnection(myConnectionURL);
             PreparedStatement ps = con.prepareStatement(sql);) {
            ps.setInt(1, userId);
            try (ResultSet rs = ps.executeQuery();) {
                while(rs.next()) {
                    users.add(new User(rs.getInt("id"), rs.getString("name")));
                }
            }
        }
    } catch (SQLException e) {
        e.printStackTrace();
    }
    return users;
}


2
@ আলেকজান্ডার ফারবার দুর্ভাগ্যক্রমে, ড্রাইভারদের সাথে কুখ্যাত সমস্যা রয়েছে যা নিজেরাই সংস্থান বন্ধ করতে ব্যর্থ হয়েছে। হার্ড Knocks স্কুল স্পষ্টভাবে সবসময় সব ঘনিষ্ঠ JDBC এর সম্পদ আমাদের শিক্ষা দেয়, প্রায় চেষ্টা-সম্পদ সঙ্গে ব্যবহার সহজ করে Connection, PreparedStatementএবং ResultSetখুব। বাস্তবে না করার কোনও কারণ নেই, কারণ চেষ্টা-সংস্থানগুলি এটিকে এত সহজ করে তোলে এবং আমাদের কোডগুলিকে আমাদের উদ্দেশ্য হিসাবে আরও স্ব-ডকুমেন্টিং করে তোলে।
তুলসী বার্ক

উত্তর:


85

আপনার উদাহরণে বাহ্যিক চেষ্টা করার দরকার নেই, তাই কমপক্ষে আপনি কমপক্ষে 3 থেকে 2 এ নামতে পারেন এবং এছাড়াও আপনার ;উত্স তালিকার শেষে বন্ধ হওয়ার দরকার নেই । দুটি চেষ্টা ব্লক ব্যবহার করার সুবিধাটি হ'ল আপনার সমস্ত কোড সামনে উপস্থিত রয়েছে যাতে আপনাকে একটি পৃথক পদ্ধতিতে উল্লেখ করতে হবে না:

public List<User> getUser(int userId) {
    String sql = "SELECT id, username FROM users WHERE id = ?";
    List<User> users = new ArrayList<>();
    try (Connection con = DriverManager.getConnection(myConnectionURL);
         PreparedStatement ps = con.prepareStatement(sql)) {
        ps.setInt(1, userId);
        try (ResultSet rs = ps.executeQuery()) {
            while(rs.next()) {
                users.add(new User(rs.getInt("id"), rs.getString("name")));
            }
        }
    } catch (SQLException e) {
        e.printStackTrace();
    }
    return users;
}

5
কিভাবে ফোন করবেন Connection::setAutoCommit? এই ধরনের একটি কল মধ্যে অনুমোদিত নয় tryমধ্যে con = এবং ps =। কোনও ডেটা সোর্স থেকে কোনও সংযোগ পাওয়ার সময় যখন কোনও সংযোগ পুলের সাহায্যে ব্যাক করা যায়, তখন আমরা ধরে নিতে পারি না যে কীভাবে অটোকমিট সেট করা আছে।
তুলিল বাউর্ক

1
আপনি সাধারণত পদ্ধতিতে সংযোগটি ইনজেকশান করবেন (ওপির প্রশ্নে প্রদর্শিত অ্যাড-হক পদ্ধতির বিপরীতে), আপনি একটি সংযোগ পরিচালন ক্লাস ব্যবহার করতে পারেন যা সংযোগ সরবরাহ করতে বা বন্ধ করার জন্য ডাকা হবে (এটি পুল করা হোক বা না হোক)। সেই ব্যবস্থাপকটিতে আপনি নিজের সংযোগের আচরণটি নির্দিষ্ট করতে পারেন
svarog

@ বাসিলবার্ক আপনি এমন DriverManager.getConnection(myConnectionURL)পদ্ধতিতে যেতে পারেন যা অটোকমিট পতাকাও সেট করে এবং সংযোগটি ফেরত দেয় (বা createPreparedStatementপূর্ববর্তী উদাহরণে পদ্ধতির সমতুল্য সেট করে ...)
রোজারডপ্যাক ১

@ আরগারডপ্যাক হ্যাঁ, এটি বোধগম্য হয়। আপনার নিজস্ব বাস্তবায়ন আছে DataSourceযেখানে getConnectionপদ্ধতি হিসাবে আপনি সংযোগ পেতে বলুন এবং প্রয়োজন যেমন কনফিগার, তারপর সংযোগ ক্ষণস্থায়ী আছে।
বেসিল Bourque

1
উত্তরটি পরিষ্কার করার জন্য @ আরগারডপ্যাক ধন্যবাদ। আমি এটি নির্বাচিত উত্তরে আপডেট করেছি।
জোনাস

187

আমি বুঝতে পেরেছি এটির উত্তর অনেক আগেই দেওয়া হয়েছিল তবে একটি অতিরিক্ত পদ্ধতির পরামর্শ দিতে চাই যা নেস্টেড ট্রাই-উইথ রিসোর্সগুলি ডাবল ব্লক এড়িয়ে চলে।

public List<User> getUser(int userId) {
    try (Connection con = DriverManager.getConnection(myConnectionURL);
         PreparedStatement ps = createPreparedStatement(con, userId); 
         ResultSet rs = ps.executeQuery()) {

         // process the resultset here, all resources will be cleaned up

    } catch (SQLException e) {
        e.printStackTrace();
    }
}

private PreparedStatement createPreparedStatement(Connection con, int userId) throws SQLException {
    String sql = "SELECT id, username FROM users WHERE id = ?";
    PreparedStatement ps = con.prepareStatement(sql);
    ps.setInt(1, userId);
    return ps;
}

24
না, এটি কভার করা হয়েছে, সমস্যাটি হ'ল উপরের কোডটি এমন একটি পদ্ধতির অভ্যন্তর থেকে রেডিস্টেটমেন্টকে কল করছে যা এসকিএলএক্সসেপশন নিক্ষেপ করার ঘোষণা দেয় না। এছাড়াও, উপরের কোডটিতে কমপক্ষে একটি পাথ রয়েছে যেখানে প্রস্তুত বিবৃতিটি বন্ধ না করে ব্যর্থ হতে পারে (যদি সেটআপকে ফোন করার সময় কোনও এসকিএলএক্সেপশন হয় occurs)
ট্রেজকাজ

1
প্রিপেইড স্টেটমেন্ট বন্ধ না করার সম্ভাবনা সম্পর্কে @ ট্রেজকাজ ভালো পয়েন্ট। আমি এটা ভেবে দেখিনি, তবে আপনি ঠিক বলেছেন!
জিনে বোয়ারস্কি

2
@ আরতুুরতেনা হ্যাঁ - আদেশটির নিশ্চয়তা রয়েছে
জিনে বোয়ারস্কি

2
@ জিয়ানবোয়ারস্কি এটি করার আরও একটি উপায় আছে? যদি না হয় তবে প্রতিটি বর্গ বাক্যটির জন্য আমার একটি নির্দিষ্ট ক্রিয়েপ্রেড স্টেটমেন্ট পদ্ধতি তৈরি করতে হবে
জন আলেকজান্ডার বেটস

1
ট্রেজকাজের মন্তব্য সম্পর্কে, createPreparedStatementআপনি এটি কীভাবে ব্যবহার করবেন না কেন তা নিরাপদ নয়। এটির সমাধানের জন্য আপনাকে সেটইন্ট (...) এর চারপাশে একটি ট্র্যাচ-ক্যাচ যুক্ত করতে হবে, যে কোনওটিকে ধরতে হবে SQLException, এবং যখন এটি ঘটে তখন ps.close () কল করুন এবং ব্যতিক্রমটি পুনরায় পাঠাবেন। তবে এর ফলে ওপি যে কোডটিকে উন্নত করতে চেয়েছিল তার কোড প্রায় দীর্ঘ এবং অপ্রয়োজনীয় হয়ে উঠবে।
ফ্লোরিয়ান এফ

4

বাইরের চেষ্টাতে সমস্ত কিছু ফিট করার জন্য ল্যাম্বডাস এবং জেডিকে 8 সরবরাহকারী ব্যবহার করার জন্য এখানে একটি সংক্ষিপ্ত উপায় রয়েছে:

try (Connection con = DriverManager.getConnection(JDBC_URL, prop);
    PreparedStatement stmt = ((Supplier<PreparedStatement>)() -> {
    try {
        PreparedStatement s = con.prepareStatement("SELECT userid, name, features FROM users WHERE userid = ?");
        s.setInt(1, userid);
        return s;
    } catch (SQLException e) { throw new RuntimeException(e); }
    }).get();
    ResultSet resultSet = stmt.executeQuery()) {
}

5
@Bpgergo দ্বারা বর্ণিত "ক্লাসিক পদ্ধতির" চেয়ে এটি আরও সংক্ষিপ্ত? আমি এটি মনে করি না এবং কোডটি বোঝা আরও কঠিন। সুতরাং এই পদ্ধতির সুবিধা ব্যাখ্যা করুন।
rmuller

আমি মনে করি না, এই ক্ষেত্রে আপনাকে স্পষ্টতই এসকিউএলএক্সসেপশন ধরতে হবে। এটি আসলে ব্যবহারের সংস্থানগুলির উপর "optionচ্ছিক"। অন্য কোনও উত্তর এটির উল্লেখ করে না। সুতরাং, আপনি সম্ভবত এটি আরও সহজ করতে পারেন।
জাঙ্গোফান

ড্রাইভারমনেজ.জেটকনেকশন (জেডিবিসি_আরএল, প্রপ) যদি কী হয়; নাল ফেরায়?
গৌরব

2

অতিরিক্ত মোড়কের ক্লাস তৈরির বিষয়ে কী?

package com.naveen.research.sql;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

public abstract class PreparedStatementWrapper implements AutoCloseable {

    protected PreparedStatement stat;

    public PreparedStatementWrapper(Connection con, String query, Object ... params) throws SQLException {
        this.stat = con.prepareStatement(query);
        this.prepareStatement(params);
    }

    protected abstract void prepareStatement(Object ... params) throws SQLException;

    public ResultSet executeQuery() throws SQLException {
        return this.stat.executeQuery();
    }

    public int executeUpdate() throws SQLException {
        return this.stat.executeUpdate();
    }

    @Override
    public void close() {
        try {
            this.stat.close();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}


তারপরে কলিং ক্লাসে আপনি রেডিস্টেটমেন্ট পদ্ধতিটি প্রয়োগ করতে পারেন:

try (Connection con = DriverManager.getConnection(JDBC_URL, prop);
    PreparedStatementWrapper stat = new PreparedStatementWrapper(con, query,
                new Object[] { 123L, "TEST" }) {
            @Override
            protected void prepareStatement(Object... params) throws SQLException {
                stat.setLong(1, Long.class.cast(params[0]));
                stat.setString(2, String.valueOf(params[1]));
            }
        };
        ResultSet rs = stat.executeQuery();) {
    while (rs.next())
        System.out.println(String.format("%s, %s", rs.getString(2), rs.getString(1)));
} catch (SQLException e) {
    e.printStackTrace();
}


2
উপরের মন্তব্যে কোনও কিছুই কখনও তা বলে না।
ট্রেজকাজ

2

অন্যরা যেমন বলেছে, আপনার কোডটি মূলত সঠিক যদিও tryবাইরেরটি অপ্রয়োজনীয়। এখানে আরও কয়েকটি চিন্তাভাবনা রয়েছে।

DataSource

এখানে অন্যান্য উত্তর সঠিক এবং ভাল, যেমন বিপিগারগো দ্বারা গৃহীত উত্তর । তবে শো-এর DataSourceকোনওটিই DriverManagerআধুনিক জাভাতে সাধারণত ব্যবহারের চেয়ে প্রস্তাবিত নয় ।

সুতরাং সম্পূর্ণতার জন্য, এখানে একটি সম্পূর্ণ উদাহরণ যা ডেটাবেস সার্ভার থেকে বর্তমান তারিখটি নিয়ে আসে। এখানে ব্যবহৃত ডাটাবেসটি পোস্টগ্রিস । অন্য কোনও ডাটাবেস একইভাবে কাজ করবে। আপনি আপনার ডাটাবেসে উপযুক্ত org.postgresql.ds.PGSimpleDataSourceপ্রয়োগের ব্যবহারটি প্রতিস্থাপন করবেন DataSource। কোনও বাস্তবায়ন সম্ভবত আপনার নির্দিষ্ট ড্রাইভার বা সংযোগ পুল দ্বারা সরবরাহ করা হয় যদি আপনি সেই পথে যান।

একটি DataSourceবাস্তবায়ন বন্ধ করা দরকার না , কারণ এটি কখনই "খোলা" হয় না। এ DataSourceকোনও সংস্থান নয়, এটি ডাটাবেসের সাথে সংযুক্ত নয়, সুতরাং এটি ডাটাবেস সার্ভারে নেটওয়ার্কিং সংযোগ বা সংস্থান রাখছে না। একটি DataSourceহ'ল ডাটাবেসটিতে সংযোগ করার সময় প্রয়োজনীয় তথ্য, ডাটাবেস সার্ভারের নেটওয়ার্ক নাম বা ঠিকানা, ব্যবহারকারীর নাম, ব্যবহারকারীর পাসওয়ার্ড এবং বিভিন্ন সংযোগের পরে আপনি সংযুক্ত হওয়ার পরে নির্দিষ্ট করতে চান। সুতরাং আপনার DataSourceবাস্তবায়নের অবজেক্টটি আপনার ব্যবহারের সাথে সংস্থানগুলির প্রথম বন্ধনীর ভিতরে চলে না

নেস্টেড চেষ্টা সঙ্গে সংস্থান

আপনার কোডটি নেস্টেড ট্রাই-উইথ রিসোর্সের বিবৃতিগুলির যথাযথ ব্যবহার করে।

নীচের উদাহরণ কোডটিতে লক্ষ্য করুন যে আমরা চেষ্টা-সহ-সংস্থানগুলি সিনট্যাক্সটিও দু'বার ব্যবহার করি , একজনের মধ্যে অন্যটির অভ্যন্তরে ested বাইরের tryদুটি সংস্থান সংজ্ঞা দেয়: Connectionএবং PreparedStatement। অভ্যন্তরীণ সংস্থান tryসংজ্ঞা দেয় ResultSet। এটি একটি সাধারণ কোড কাঠামো।

যদি অভ্যন্তরীণ থেকে কোনও ব্যতিক্রম ছুঁড়ে ফেলা হয় এবং সেখানে ধরা না পড়ে তবে উত্সটি ResultSetস্বয়ংক্রিয়ভাবে বন্ধ হয়ে যাবে (যদি এটি উপস্থিত থাকে তবে নাল নয়)। এর পরে, PreparedStatementবন্ধ হয়ে যাবে এবং শেষ অবধি এটি Connectionবন্ধ থাকবে। রিসোর্সগুলি বিপরীত ক্রমে স্বয়ংক্রিয়ভাবে বন্ধ হয়ে যায় যেখানে তারা সংস্থান-সংস্থান-সংক্রান্ত বিবৃতিতে ঘোষিত হয়েছিল।

এখানে উদাহরণ কোড অত্যধিক সরল। লিখিত হিসাবে, এটি একক চেষ্টা-সংস্থান-সংক্রান্ত বিবৃতি দিয়ে কার্যকর করা যেতে পারে। তবে একটি আসল কাজে আপনি সম্ভবত নেস্টেড জোড় tryকলের মধ্যে আরও কাজ করছেন । উদাহরণস্বরূপ, আপনি আপনার ব্যবহারকারীর ইন্টারফেস বা কোনও পোজো থেকে মানগুলি বের করে আনতে পারেন এবং তারপরে পদ্ধতিগুলিতে ?কলগুলির মাধ্যমে আপনার এসকিউএল এর মধ্যে স্থানধারকগুলি পূরণ করতে সেগুলি পাস করছেন PreparedStatement::set…

সিনট্যাক্স নোট

অর্ধকোলন ট্রেলিং

লক্ষ্য করুন যে-সংস্থানসমূহের চেষ্টা করে বন্ধুত্বের মধ্যে সর্বশেষ সংস্থান বিবরণীর পিছনে থাকা সেমিকোলনটি isচ্ছিক। আমি এটি দুটি কারণে আমার নিজের কাজে অন্তর্ভুক্ত করেছি: ধারাবাহিকতা এবং এটি সম্পূর্ণ দেখায় এবং লাইন-এর শেষ সেমিকোলনের বিষয়ে চিন্তা না করেই লাইনগুলির মিশ্রণটি অনুলিপি-পেস্ট করা সহজ করে দেয়। আপনার আইডিই সর্বশেষ সেমিকোলনকে অতিরিক্ত অতিরিক্ত হিসাবে চিহ্নিত করতে পারে তবে এটি ছেড়ে দেওয়ার কোনও ক্ষতি নেই harm

জাভা 9 - ব্যবহারের সাথে সংস্থানগুলিতে বিদ্যমান ভার্স ব্যবহার করুন

রিসোর্স সিনট্যাক্স চেষ্টা করে জাভা 9 -এ নতুন। আমরা এখন tryবিবৃতিটির প্রথম বন্ধনের বাইরে সংস্থানগুলি ঘোষণা করতে এবং পপুলেট করতে পারি । আমি জেডিবিসি সংস্থার জন্য এখনও এটি দরকারী খুঁজে পাই নি, তবে এটি আপনার নিজের কাজের ক্ষেত্রে মনে রাখবেন।

ResultSet নিজেই বন্ধ করা উচিত, কিন্তু না

একটি আদর্শ বিশ্বে ResultSetডকুমেন্টেশন প্রতিশ্রুতি হিসাবে নিজেকে বন্ধ করবে:

রেজাল্টসেট অবজেক্টটি স্বয়ংক্রিয়ভাবে বন্ধ হয়ে যায় যখন স্টেটমেন্ট অবজেক্ট যা এটি উত্পন্ন করে তা বন্ধ হয়ে যায়, পুনরায় কার্যকর করা হয় বা একাধিক ফলাফলের ক্রম থেকে পরবর্তী ফলাফল পুনরুদ্ধার করতে ব্যবহৃত হয়।

দুর্ভাগ্যক্রমে, অতীতে কিছু জেডিবিসি ড্রাইভার এই প্রতিশ্রুতিটি পালনে অবিচ্ছিন্নভাবে ব্যর্থ হয়েছিল। ফলস্বরূপ, অনেক JDBC এর প্রোগ্রামাররা সহ স্পষ্টভাবে সব ঘনিষ্ঠ তাদের JDBC এর সম্পদ শিখেছি Connection, PreparedStatementএবং ResultSetখুব। রিসোর্স সিন্ট্যাক্স-সহ আধুনিক চেষ্টাটি এত সহজ করে তুলেছে এবং আরও কমপ্যাক্ট কোড দিয়ে। লক্ষ করুন যে, জাভা দল গড়েছে বিব্রত করতে গিয়ে ResultSetযেমন AutoCloseable, এবং আমি আপনাকে পরামর্শ দিচ্ছি আমরা যে ব্যবহার করতে। আপনার সমস্ত জেডিবিসি সংস্থার আশেপাশে একটি চেষ্টা সহ-সংস্থান ব্যবহার করা আপনার কোডগুলিকে আপনার উদ্দেশ্য হিসাবে আরও স্ব-ডকুমেন্টিং করে।

কোড উদাহরণ

package work.basil.example;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.time.LocalDate;
import java.util.Objects;

public class App
{
    public static void main ( String[] args )
    {
        App app = new App();
        app.doIt();
    }

    private void doIt ( )
    {
        System.out.println( "Hello World!" );

        org.postgresql.ds.PGSimpleDataSource dataSource = new org.postgresql.ds.PGSimpleDataSource();

        dataSource.setServerName( "1.2.3.4" );
        dataSource.setPortNumber( 5432 );

        dataSource.setDatabaseName( "example_db_" );
        dataSource.setUser( "scott" );
        dataSource.setPassword( "tiger" );

        dataSource.setApplicationName( "ExampleApp" );

        System.out.println( "INFO - Attempting to connect to database: " );
        if ( Objects.nonNull( dataSource ) )
        {
            String sql = "SELECT CURRENT_DATE ;";
            try (
                    Connection conn = dataSource.getConnection() ;
                    PreparedStatement ps = conn.prepareStatement( sql ) ;
            )
            {
                … make `PreparedStatement::set…` calls here.
                try (
                        ResultSet rs = ps.executeQuery() ;
                )
                {
                    if ( rs.next() )
                    {
                        LocalDate ld = rs.getObject( 1 , LocalDate.class );
                        System.out.println( "INFO - date is " + ld );
                    }
                }
            }
            catch ( SQLException e )
            {
                e.printStackTrace();
            }
        }

        System.out.println( "INFO - all done." );
    }
}
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.