আমি কীভাবে একটি প্রস্তুতিমূলক স্টেটমেন্টের এসকিউএল পেতে পারি?


160

নিম্নলিখিত পদ্ধতিতে স্বাক্ষর সহ আমার একটি সাধারণ জাভা পদ্ধতি রয়েছে:

private static ResultSet runSQLResultSet(String sql, Object... queryParams)

এটি একটি সংযোগ খুলবে, PreparedStatementস্কেরিয়াল স্টেটমেন্ট এবং queryParamsভ্যারিয়েবল দৈর্ঘ্যের অ্যারেতে প্যারামিটারগুলি ব্যবহার করে তৈরি করে , এটি চালায়, ক্যাশে ResultSet(ক CachedRowSetImpl) সংযোগ বন্ধ করে, এবং ক্যাশেড ফলাফল সেট প্রদান করে।

ত্রুটি লগ করা পদ্ধতিটিতে আমার ব্যতিক্রম হ্যান্ডলিং রয়েছে। আমি স্কেল স্টেটমেন্টটি লগের অংশ হিসাবে লগ করি কারণ এটি ডিবাগিংয়ের জন্য খুব সহায়ক। আমার সমস্যাটি হ'ল স্ট্রিং ভেরিয়েবলের sqlলগ ইন করা টেমপ্লেট স্টেটমেন্টকে আসল মানগুলির পরিবর্তে লগ করে। আমি প্রকৃত বিবৃতিটি কার্যকর করতে পেরেছি (বা মৃত্যুদন্ড কার্যকর করার চেষ্টা করেছি) লগ করতে চাই ।

সুতরাং ... সত্যিকারের এসকিউএল বিবৃতিটি কোনও দ্বারা চালিত হওয়ার কোনও উপায় আছে কি PreparedStatement? ( ছাড়া নিজেকে গড়ে তোলার। আমি অ্যাক্সেস করার উপায় খুঁজে না পেলে PreparedStatement'sএসকিউএল আমি সম্ভবত নিজেকে আমার মধ্যে গড়ে শেষ করব, catchস্প্যানিশ ভাষায়।)


1
আপনি যদি সোজা জেডিবিসি কোডটি লিখছেন তবে আমি অ্যাপাচি কমন্স-ডুবিলস কমন্স.এপাচি.অর্গ / ডিবুটিলসটি দেখার জন্য সুপারিশ করব । এটি জেডিবিসি কোডকে ব্যাপকভাবে সরল করে।
কেন লিউ 21

উত্তর:


173

প্রস্তুত বিবৃতি ব্যবহার করে, কোনও "এসকিউএল কোয়েরি" নেই:

  • আপনার কাছে একটি বিবৃতি রয়েছে, এতে স্থানধারক রয়েছে
    • এটি ডিবি সার্ভারে প্রেরণ করা হয়
    • এবং সেখানে প্রস্তুত
    • যার অর্থ এসকিউএল স্টেটমেন্টটি "বিশ্লেষণ করা", পার্স করা, কিছু উপাত্ত-কাঠামো যা প্রতিনিধিত্ব করে তা মেমোরিতে প্রস্তুত
  • এবং, তারপরে, আপনার সীমাবদ্ধ ভেরিয়েবল রয়েছে
    • যা সার্ভারে প্রেরণ করা হয়
    • এবং প্রস্তুত বিবৃতি কার্যকর করা হয় - সেই তথ্যগুলিতে কাজ করে

তবে আসল আসল এসকিউএল কোয়েরিটির পুনর্নির্মাণ নেই - জাভা সাইডেও নয়, ডাটাবেসের দিক থেকেও।

সুতরাং, প্রস্তুত বিবৃতিটির এসকিউএল পাওয়ার কোনও উপায় নেই - কারণ এই জাতীয় কোনও এসকিউএল নেই।


ডিবাগিং উদ্দেশ্যে, সমাধানগুলি হয়:

  • স্থানধারক এবং ডেটা তালিকার সাথে স্টেটমেন্টের কোডটি আউটপুট করুন
  • অথবা হাতে "কিছু" এসকিউএল কোয়েরি "তৈরি করতে"।

22
যদিও এটি কার্যত সত্য, ইউটিলিটি কোডকে সমপরিমাণ অপ্রস্তুত বিবৃতি পুনর্গঠন থেকে বাধা দেওয়ার কিছুই নেই। উদাহরণস্বরূপ, log4jdbc তে: "লগড আউটপুটে, প্রস্তুত বিবৃতিগুলির জন্য, বাঁধন যুক্তিগুলি স্বয়ংক্রিয়ভাবে এসকিউএল আউটপুটে intoোকানো হয় This এটি অনেক ক্ষেত্রে পঠনযোগ্যতা এবং ডিবাগিংকে উন্নত করে।" ডিবাগিংয়ের জন্য খুব দরকারী, যতক্ষণ আপনি সচেতন হন যে ডিবি সার্ভারের দ্বারা স্টেটমেন্টটি বাস্তবে কার্যকর করা হচ্ছে তা এটি নয়।
পার্শ্ববর্তী

6
এটি বাস্তবায়নের উপরও নির্ভর করে। মাইএসকিউএলে - কমপক্ষে যে সংস্করণটি আমি কয়েক বছর আগে ব্যবহার করছিলাম - জেডিবিসি ড্রাইভার প্রকৃতপক্ষে টেমপ্লেট থেকে একটি প্রচলিত এসকিউএল কোয়েরি তৈরি করেছিলেন এবং ভেরিয়েবলগুলি বাঁধেন। আমার অনুমান যে মাইএসকিউএল এর সংস্করণটি প্রস্তুত জবানবন্দিগুলিকে সমর্থন করে না, তাই তারা এটিকে জেডিবিসি ড্রাইভারের মধ্যে প্রয়োগ করেছে।
জয়

@ পার্সিয়াল: এটাই আমি "হাত দিয়ে ক্যোয়ারী তৈরি" বলতে চাইছিলাম ; তবে আপনি এটা আমার চেয়ে ভাল বলেছিলেন ;;; @ জায়ে: আমাদের পিএইচপি-তে একই ধরণের মেকানিজম রয়েছে (সমর্থিত হলে সত্যিকারের প্রস্তুত বিবৃতি; তাদের সমর্থন না করে এমন সিডো-
পাস্কাল মার্টিন

6
আপনি java.sql.PreparedStatement preparedStatement উপর একটি সহজ .toString () ব্যবহার করেন, তাহলে উত্পন্ন এসকিউএল আমি করেছেন সেগুলিও অন্তর্ভুক্ত হবে 1.8.0_60 এই যাচাই
Pr0n

5
@ প্রিস্টন ফর ওরাকল ডিবি এর জন্য প্রস্তুতিমূলক স্টেটমেন্ট # টু স্ট্রিং () এসকিউএল দেখায় না। সুতরাং আমার ধারণা এটি ডিবি জেডিবিসি ড্রাইভারের কাছ থেকে নির্ভর করে।
মাইক আরগেরিও

57

এটি জেডিবিসি এপিআই চুক্তিতে কোথাও নির্ধারিত নেই, তবে আপনি ভাগ্যবান হলে, প্রশ্নযুক্ত জেডিবিসি ড্রাইভার কেবল কল করেই সম্পূর্ণ এসকিউএল ফিরিয়ে দিতে পারে PreparedStatement#toString()। অর্থাত

System.out.println(preparedStatement);

কমপক্ষে MySQL 5.x এবং PostgreSQL 8.x জেডিবিসি ড্রাইভাররা এটি সমর্থন করে। তবে, অন্যান্য বেশিরভাগ জেডিবিসি ড্রাইভার এটি সমর্থন করে না। আপনার যদি এরকম একটি থাকে তবে আপনার সেরা বাজিটি Log4jdbc বা P6Spy ব্যবহার করছে

বিকল্পভাবে, আপনি একটি জেনেরিক ফাংশনও লিখতে পারেন যা একটি Connection, একটি এসকিউএল স্ট্রিং এবং বিবৃতিটির মান নেয় এবং এসকিউএল স্ট্রিং এবং মানগুলি PreparedStatementলগ করার পরে একটি ফেরত দেয়। কিক অফ উদাহরণ:

public static PreparedStatement prepareStatement(Connection connection, String sql, Object... values) throws SQLException {
    PreparedStatement preparedStatement = connection.prepareStatement(sql);
    for (int i = 0; i < values.length; i++) {
        preparedStatement.setObject(i + 1, values[i]);
    }
    logger.debug(sql + " " + Arrays.asList(values));
    return preparedStatement;
}

এবং হিসাবে এটি ব্যবহার করুন

try {
    connection = database.getConnection();
    preparedStatement = prepareStatement(connection, SQL, values);
    resultSet = preparedStatement.executeQuery();
    // ...

আরেকটি বিকল্প একটি কাস্টম বাস্তবায়ন হয় PreparedStatementযা গোপন (decorates) বাস্তব PreparedStatement নির্মাণ এবং এটি পদ্ধতি আহ্বান সব পদ্ধতি ওভাররাইড করে তাই বাস্তব PreparedStatement এবং সমস্ত মান সংগ্রহ করা setXXX()পদ্ধতি ও প্রখর রৌদ্রে "প্রকৃত" এসকিউএল স্ট্রিং নির্মান যখনই এক executeXXX()পদ্ধতি (বেশ একটি কাজ, কিন্তু অধিকাংশ আইডিই এর, অন্ধকার করে প্রসাধক পদ্ধতি জন্য autogenerators প্রদান করে) বলা হয়। অবশেষে পরিবর্তে এটি ব্যবহার করুন। এটি মূলত পি 6 এসপি এবং বিক্রয়গুলি ইতিমধ্যে হুডগুলির নীচে কী করে।


এটি আমি যে পদ্ধতিটি ব্যবহার করছি তার সাথে মিল রয়েছে (আপনার রেডিস্টেটমেন্ট পদ্ধতি)। আমার প্রশ্ন এটি কীভাবে করবেন তা নয় - আমার প্রশ্নটি কীভাবে এসকিএল স্টেটমেন্টটি লগ করা যায়। আমি জানি যে আমি এটি করতে পারি logger.debug(sql + " " + Arrays.asList(values))- আমি এর মধ্যে ইতিমধ্যে সংহত প্যারামিটারগুলির সাথে এসকিএল স্টেটমেন্ট লগ করার একটি উপায় সন্ধান করছি। নিজেকে ফাঁকা না করে এবং প্রশ্ন চিহ্নগুলি প্রতিস্থাপন না করে।
21

তারপরে আমার উত্তরের শেষ অনুচ্ছেদে চলে যান বা পি 6 এসপি দেখুন। তারা আপনার জন্য "
দুষ্টু

পি 6 এসপির লিঙ্কটি এখন ভেঙে গেছে।
স্টিফেন পি

@ বালুসসি আমি জেডিবিসির নবাগত। আমার একটা সন্দেহ আছে আপনি যদি এর মতো জেনেরিক ফাংশনটি লিখেন তবে এটি PreparedStatementপ্রতিবার তৈরি হবে । কোজ পুরো বিন্দুটি এতটা দক্ষ নয় যে PreparedStatementসেগুলি একবার তৈরি করে সর্বত্র এগুলি পুনরায় ব্যবহার করতে হবে?
ভূষণ

এটি প্রস্তুত বিবৃতিটির জন্য পূর্ণ sql কোয়েরি পেতে ভোল্টডবি জেডিবিসি ড্রাইভারের সাথেও কাজ করে।
k0pernikus

37

আমি জাভা 8, মাইএসকিউএল সংযোগকারী বনাম 5.1.31 সহ জেডিবিসি ড্রাইভার ব্যবহার করছি।

আমি এই পদ্ধতিটি ব্যবহার করে সত্যিকারের এসকিউএল স্ট্রিং পেতে পারি:

// 1. make connection somehow, it's conn variable
// 2. make prepered statement template
PreparedStatement stmt = conn.prepareStatement(
    "INSERT INTO oc_manufacturer" +
    " SET" +
    " manufacturer_id = ?," +
    " name = ?," +
    " sort_order=0;"
);
// 3. fill template
stmt.setInt(1, 23);
stmt.setString(2, 'Google');
// 4. print sql string
System.out.println(((JDBC4PreparedStatement)stmt).asSql());

সুতরাং এটি এইভাবে ফিরে আসে:

INSERT INTO oc_manufacturer SET manufacturer_id = 23, name = 'Google', sort_order=0;

ওপিটির সন্ধান ঠিক এটির মতোই এটির সবগুলি আপভোট হওয়া উচিত।
হাকইট

পোস্টগ্র্রেস-ড্রাইভারের জন্য কি একই রকম ফাংশন রয়েছে?
ডেভিডেসম্যান

কিভাবে এটি পেতে Apache Derby?
গুণসেকর

এটা কাজ করে না এবং ছোঁড়ার ClassCastException : java.lang.ClassCastException: oracle.jdbc.driver.T4CPreparedStatement com.mysql.jdbc.JDBC4PreparedStatement কাস্ট করা যাবে না
Ercan থেকে

1
@ এরকানডুমান, আমার উত্তর সর্বজনীন নয়, এটি জাভা 8 এবং মাইএসকিউএল জেডিবিসি ড্রাইভারকে অন্তর্ভুক্ত করে।
ইউজারলন্ড

21

আপনি যদি কোয়েরিটি সম্পাদন করে এবং একটি ResultSet( যদি আপনি এই দৃশ্যে আছেন , অন্তত) প্রত্যাশা করে থাকেন তবে আপনি কেবল ResultSetএর getStatement()মতো কল করতে পারেন:

ResultSet rs = pstmt.executeQuery();
String executedQuery = rs.getStatement().toString();

ভেরিয়েবলটিতে executedQueryস্টেটমেন্টটি থাকবে যা তৈরি করতে ব্যবহৃত হয়েছিল ResultSet

এখন, আমি বুঝতে পারি যে এই প্রশ্নটি বেশ পুরানো, তবে আমি আশা করি এটি কারওর জন্য সহায়তা করে ..


6
@ এলাদ স্টারন এটি প্রিন্ট করে, oracle.jdbc.driver.OraclePreparedStatementWrapper@1b9ce4b এক্সিকিউটেড এসকিএল স্টেটমেন্ট প্রিন্ট করার পরিবর্তে! আমাদের গাইড করুন!
এভিএ

@ অ্যাভিএ, আপনি কি স্ট্রিং () ব্যবহার করেছেন?
এলাদ স্টার্ন

@ এলডস্টার টু স্ট্রিং () ব্যবহার করা হয়েছে!
এভিএ

@ অ্যাভিএ, ভাল আমি নিশ্চিত নই তবে এটি আপনার জেডিবিসি ড্রাইভারের সাথেও করতে পারে। আমি মাইএসকিএল-সংযোজক -5 সফলভাবে ব্যবহার করেছি।
এলাদ স্টার্ন

3
RSS.getStatement () কেবলমাত্র স্টেটমেন্ট অবজেক্টটি ফিরিয়েছে, তাই আপনি যে ড্রাইভারটি প্রয়োগ করছেন। টু স্ট্রিং () ব্যবহার করছেন কিনা তা নীচে নির্ধারণ করে যে আপনি এসকিউএল ফিরে পাবেন কিনা
Daz

3

আমি রেডিস্টেটমেন্ট.টোস্ট্রিং () আমার ক্ষেত্রে টু স্ট্রিং () স্ট্রিংকে এভাবে স্ট্রিং প্রদান করে প্রিপেইডস্টেটমেন্ট থেকে আমার এসকিএল বের করেছি ext

org.hsqldb.jdbc.JDBCPreparedStatement@7098b907[sql=[INSERT INTO 
TABLE_NAME(COLUMN_NAME, COLUMN_NAME, COLUMN_NAME) VALUES(?, ?, ?)],
parameters=[[value], [value], [value]]]

এখন আমি একটি পদ্ধতি তৈরি করেছি (জাভা 8), যা কোয়েরি এবং মান উভয়ই বের করতে এবং এগুলিকে মানচিত্রে রাখার জন্য রেজেক্স ব্যবহার করছে:

private Map<String, String> extractSql(PreparedStatement preparedStatement) {
    Map<String, String> extractedParameters = new HashMap<>();
    Pattern pattern = Pattern.compile(".*\\[sql=\\[(.*)],\\sparameters=\\[(.*)]].*");
    Matcher matcher = pattern.matcher(preparedStatement.toString());
    while (matcher.find()) {
      extractedParameters.put("query", matcher.group(1));
      extractedParameters.put("values", Stream.of(matcher.group(2).split(","))
          .map(line -> line.replaceAll("(\\[|])", ""))
          .collect(Collectors.joining(", ")));
    }
    return extractedParameters;
  }

এই পদ্ধতিটি মানচিত্রটি ফেরত দেয় যেখানে আমাদের কী-মানযুক্ত জোড়া রয়েছে:

"query" -> "INSERT INTO TABLE_NAME(COLUMN_NAME, COLUMN_NAME, COLUMN_NAME) VALUES(?, ?, ?)"
"values" -> "value,  value,  value"

এখন - আপনি যদি তালিকা হিসাবে মানগুলি চান তবে আপনি কেবল সহজভাবে ব্যবহার করতে পারেন:

List<String> values = Stream.of(yourExtractedParametersMap.get("values").split(","))
    .collect(Collectors.toList());

যদি আপনার প্রস্তুত স্টেটমেন্ট.টোস্ট্রিং () আমার ক্ষেত্রে আলাদা হয় তবে এটি "অ্যাডজাস্টিং" রেজেক্সের বিষয়।


2

অফিসিয়াল জাভা ড্রাইভারের সাথে PostgreSQL 9.6.x ব্যবহার করা হচ্ছে 42.2.4:

...myPreparedStatement.execute...
myPreparedStatement.toString()

?ইতিমধ্যে প্রতিস্থাপিত এসকিউএল প্রদর্শন করবে , যা আমি খুঁজছিলাম। পোস্টগ্রিস কেসটি কভার করার জন্য এই উত্তরটি জুড়েছে।

আমি কখনও ভাবিনি যে এটি এত সহজ হতে পারে।


1

আমি প্রিপারস্টেটমেন্ট থেকে এসকিউএল মুদ্রণের জন্য নিম্নলিখিত কোডটি প্রয়োগ করেছি

public void printSqlStatement(PreparedStatement preparedStatement, String sql) throws SQLException{
        String[] sqlArrya= new String[preparedStatement.getParameterMetaData().getParameterCount()];
        try {
               Pattern pattern = Pattern.compile("\\?");
               Matcher matcher = pattern.matcher(sql);
               StringBuffer sb = new StringBuffer();
               int indx = 1;  // Parameter begin with index 1
               while (matcher.find()) {
             matcher.appendReplacement(sb,String.valueOf(sqlArrya[indx]));
               }
               matcher.appendTail(sb);
              System.out.println("Executing Query [" + sb.toString() + "] with Database[" + "] ...");
               } catch (Exception ex) {
                   System.out.println("Executing Query [" + sql + "] with Database[" +  "] ...");
            }

    }

1

যুক্তিগুলির তালিকা সহ এসকিউএল প্রিপেইড স্ট্যামেন্টস রূপান্তর করতে কোড স্নিপেট। এটা আমার জন্য কাজ করে

  /**
         * 
         * formatQuery Utility function which will convert SQL
         * 
         * @param sql
         * @param arguments
         * @return
         */
        public static String formatQuery(final String sql, Object... arguments) {
            if (arguments != null && arguments.length <= 0) {
                return sql;
            }
            String query = sql;
            int count = 0;
            while (query.matches("(.*)\\?(.*)")) {
                query = query.replaceFirst("\\?", "{" + count + "}");
                count++;
            }
            String formatedString = java.text.MessageFormat.format(query, arguments);
            return formatedString;
        }

1

খুব দেরীতে :) তবে আপনি ওরেकलপ্রিয় স্টেটমেন্ট র‌্যাপার থেকে মূল এসকিউএলটি পেতে পারেন

((OraclePreparedStatementWrapper) preparedStatement).getOriginalSql();

1
যখন আমি oracle.jdbc.driver.OraclePreparedStatementWrapperমোড়কটি ব্যবহার করার চেষ্টা করি তখন এটিতে বলা হয়: oracle.jdbc.driver এ সর্বজনীন নয়। বাইরের প্যাকেজ থেকে অ্যাক্সেস করা যায় না। আপনি কিভাবে এই ক্লাস ব্যবহার করছেন?
বর্ণালী

0

আপনি যদি মাইএসকিউএল ব্যবহার করছেন তবে আপনি মাইএসকিউএল এর ক্যোয়ারী লগ ব্যবহার করে প্রশ্নগুলি লগ করতে পারেন । অন্যান্য বিক্রেতারা এই বৈশিষ্ট্যটি সরবরাহ করেন কিনা তা আমি জানি না, তবে সম্ভাবনা তারা কী করে।


-1

কেবল ফাংশন:

public static String getSQL (Statement stmt){
    String tempSQL = stmt.toString();

    //please cut everything before sql from statement
    //javadb...: 
    int i1 = tempSQL.indexOf(":")+2;
    tempSQL = tempSQL.substring(i1);

    return tempSQL;
}

এটি প্রস্তুত স্টেটমেন্টের জন্য ভাল।


.toString()অনভিজ্ঞ ব্যবহারকারীদের বোকা বানানোর জন্য এটি বেশ কয়েকটি অতিরিক্ত লাইন সহ কেবল একটি এবং এটি যুগে যুগে ইতিমধ্যে উত্তর দেওয়া হয়েছিল।
পেরে

-1

আমি ওরালস 11 জি ব্যবহার করছি এবং প্রিপারেশনস্টেটমেন্ট থেকে চূড়ান্ত এসকিউএল পাওয়ার ব্যবস্থা করতে পারি না। @ প্যাসাল মার্টিন উত্তর পড়ার পরে আমি বুঝতে পারি কেন।

আমি কেবল প্রস্তুতিমূলক স্টেটমেন্ট ব্যবহার করার ধারণাটি ত্যাগ করেছি এবং একটি সাধারণ পাঠ্য বিন্যাস ব্যবহার করেছি যা আমার প্রয়োজন অনুসারে ফিট করে। এখানে আমার উদাহরণ:

//I jump to the point after connexion has been made ...
java.sql.Statement stmt = cnx.createStatement();
String sqlTemplate = "SELECT * FROM Users WHERE Id IN ({0})";
String sqlInParam = "21,34,3434,32"; //some random ids
String sqlFinalSql = java.text.MesssageFormat(sqlTemplate,sqlInParam);
System.out.println("SQL : " + sqlFinalSql);
rsRes = stmt.executeQuery(sqlFinalSql);

আপনি চিহ্নিত করেছেন যে স্ক্যালিএনপ্যারামটি একটি (যেমন, যখন) লুপটিতে গতিশীলভাবে তৈরি করা যেতে পারে আমি এসকিউএল কোয়েরির জন্য স্ট্রিং টেম্পলেট ফরমেটার হিসাবে পরিবেশন করার জন্য মেসেজফর্ম্যাট ক্লাসটি ব্যবহার করার বিষয়টি সহজ করেছিলাম।


4
এই ধরণের তৈরি স্টেটমেন্টগুলি ব্যবহারের পুরো কারণটিকে ফুটিয়ে তোলে, যেমন স্কুয়েল ইঞ্জেকশন এড়ানো এবং উন্নত কর্মক্ষমতা।
টিকটক

1
আমি আপনার সাথে 100% একমত আমার স্পষ্ট করে দেওয়া উচিত ছিল যে আমি একটি বিশাল বাল্ক ডেটা ইন্টিগ্রেশনের জন্য বেশ কয়েকটি সময় এই কোডটি কার্যকর করতে পেরেছি এবং পুরো লগ 4 জে এনচিল্ডায় না গিয়ে কিছুটা লগ আউটপুট পাওয়ার জন্য তাত্ক্ষণিকভাবে প্রয়োজন ছিল যা কোনটির জন্য ওভারকিল হত? আমার দরকার ছিল. এটি প্রোডাকশন কোডে যাওয়া উচিত নয় :-)
ডিয়েগো টেরেসো

এটি ওরাকল ড্রাইভারের সাথে আমার জন্য কাজ করে (তবে এতে প্যারামিটার অন্তর্ভুক্ত নয়):((OraclePreparedStatementWrapper) myPreparedStatement).getOriginalSql()
লাতজ

-4

এটি করার জন্য আপনার একটি জেডিবিসি সংযোগ এবং / অথবা ড্রাইভার দরকার যা নিম্ন স্তরে স্কিএল লগিং সমর্থন করে।

Log4jdbcএকবার দেখুন


3
Log4jdbc একবার দেখুন এবং তারপর কি? তুমি এটা কিভাবে ব্যবহার কর? আপনি সেই সাইটে যান এবং কীভাবে প্রযুক্তি ব্যবহার করবেন সে সম্পর্কে কোনও স্পষ্ট উদাহরণ ছাড়াই প্রকল্পটি সম্পর্কে এলোমেলোভাবে ঝাঁকুনি দেখতে পান।
হুলি
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.