Рубрики
Без рубрики

Java ПОДГОТОВЛЕННОЕ утверждение В альтернативных предложениях

Подготовленное утверждение В альтернативном предложении, Java Подготовленное утверждение В альтернативных подходах

Автор оригинала: Pankaj Kumar.

Если вы используете API JDBC для выполнения запросов к базе данных, вы должны знать, что PreparedStatement-лучший выбор, чем оператор . Однако, поскольку API JDBC допускает только один литерал для одного параметра”?”, PreparedStatement не работает для запросов предложений IN.

ПОДГОТОВЛЕННОЕ заявление В пункте

Поэтому, если нам нужно выполнить запрос к базе данных в предложении, нам нужно найти какой-то альтернативный подход. Цель этого поста-проанализировать различные подходы, и вы можете выбрать тот, который соответствует вашим требованиям.

  1. Выполнение Отдельных Запросов
  2. Использование Хранимой Процедуры
  3. Динамическое создание запроса PreparedStatement
  4. Использование NULL в подготовленном запросе оператора

Давайте рассмотрим эти подходы один за другим. Но перед этим давайте создадим служебную программу для чтения конфигураций соединений с базой данных из файла свойств.

Давайте рассмотрим эти подходы один за другим. Но перед этим давайте создадим служебную программу для чтения конфигураций соединений с базой данных из файла свойств.

#mysql DB properties
DB_DRIVER_CLASS=com.mysql.jdbc.Driver
DB_URL=jdbc:mysql://localhost:3306/UserDB
DB_USERNAME=pankaj
DB_PASSWORD=pankaj123

#Oracle DB Properties
#DB_DRIVER_CLASS=oracle.jdbc.driver.OracleDriver
#DB_URL=jdbc:oracle:thin:@localhost:1521:orcl
#DB_USERNAME=hr
#DB_PASSWORD=oracle
package com.journaldev.jdbc.preparedstatement.in;

import java.io.FileInputStream;
import java.io.IOException;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.Properties;

public class DBConnection {

	public static Connection getConnection() {
		Properties props = new Properties();
		FileInputStream fis = null;
		Connection con = null;
		try {
			fis = new FileInputStream("db.properties");
			props.load(fis);

			// load the Driver Class
			Class.forName(props.getProperty("DB_DRIVER_CLASS"));

			// create the connection now
			con = DriverManager.getConnection(props.getProperty("DB_URL"),
					props.getProperty("DB_USERNAME"),
					props.getProperty("DB_PASSWORD"));
		} catch (SQLException e) {
			System.out.println("Check database is UP and configs are correct");
			e.printStackTrace();
		} catch (IOException e) {
			System.out.println("Looks like db.property file has some issues");
			e.printStackTrace();
		} catch (ClassNotFoundException e) {
			System.out.println("Please include JDBC API jar in classpath");
			e.printStackTrace();
		}finally{
			try {
				fis.close();
			} catch (IOException e) {
				System.out.println("File Close issue, lets ignore it.");
			}
		}
		return con;
	}
}

Убедитесь, что у вас есть JDBC-банки в пути сборки проекта.

Теперь давайте рассмотрим различные подходы и их анализ.

Выполнение Отдельных Запросов

Это самый простой подход. Мы можем получить входные данные и выполнить один подготовленный запрос оператора несколько раз. Пример программы с таким подходом будет выглядеть следующим образом.

Это самый простой подход. Мы можем получить входные данные и выполнить один подготовленный запрос оператора несколько раз. Пример программы с таким подходом будет выглядеть следующим образом.

package com.journaldev.jdbc.preparedstatement.in;

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

public class JDBCPreparedStatementSingle {

	private static final String QUERY = "select empid, name from Employee where empid = ?";
	
	public static void printData(int[] ids){
		Connection con = DBConnection.getConnection();
		PreparedStatement ps = null;
		ResultSet rs = null;
		try {
			ps = con.prepareStatement(QUERY);
			
			for(int empid : ids){
				ps.setInt(1, empid);
				rs = ps.executeQuery();
				
				while(rs.next()){
					System.out.println("Employee ID="+rs.getInt("empid")+", Name="+rs.getString("name"));
				}
				
				//close the resultset here
				try{
					rs.close();
				} catch(SQLException e){}
			}
		} catch (SQLException e) {
			e.printStackTrace();
		}finally{
			try {
				ps.close();
				con.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}
		}
	}
}

Подход прост, но он очень медленный, потому что при наличии 100 параметров он выполнит 100 вызовов базы данных. Это приведет к появлению 100 объектов набора результатов, которые перегрузят систему, а также снизят производительность. Поэтому такой подход не рекомендуется.

Использование Хранимой Процедуры

Мы можем написать хранимую процедуру и отправить входные данные в хранимую процедуру. Затем мы можем выполнять запросы один за другим в хранимой процедуре и получать результаты. Этот подход обеспечивает максимальную производительность, но, как мы все знаем, хранимые процедуры зависят от конкретной базы данных. Поэтому, если наше приложение имеет дело с несколькими типами баз данных, такими как Oracle, MySQL, то его будет сложно поддерживать. Мы должны использовать этот подход только в том случае, если мы работаем с одним типом базы данных и не планируем менять сервер базы данных. Поскольку написание хранимой процедуры выходит за рамки этого руководства, я не буду демонстрировать, как ее использовать.

Динамическое создание запроса PreparedStatement

Этот подход предполагает написание логики для динамического создания запроса PreparedStatement на основе размера элементов в предложении IN. Простой пример, показывающий, как его использовать, будет выглядеть следующим образом.

Этот подход предполагает написание логики для динамического создания запроса PreparedStatement на основе размера элементов в предложении IN. Простой пример, показывающий, как его использовать, будет выглядеть следующим образом.

package com.journaldev.jdbc.preparedstatement.in;

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

public class JDBCPreparedStatementDynamic {

	public static void printData(int[] ids){
		
		String query = createQuery(ids.length);
		
		System.out.println("Query="+query);
		Connection con = DBConnection.getConnection();
		PreparedStatement ps = null;
		ResultSet rs = null;
		try {
			ps = con.prepareStatement(query);
			
			for(int i = 1; i <=ids.length; i++){
				ps.setInt(i, ids[i-1]);
			}
			rs = ps.executeQuery();
				
			while(rs.next()){
				System.out.println("Employee ID="+rs.getInt("empid")+", Name="+rs.getString("name"));
			}
				
			//close the resultset here
			try{
				rs.close();
			} catch(SQLException e){}
			
		} catch (SQLException e) {
			e.printStackTrace();
		}finally{
			try {
				ps.close();
				con.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}
		}
	}

	private static String createQuery(int length) {
		String query = "select empid, name from Employee where empid in (";
		StringBuilder queryBuilder = new StringBuilder(query);
		for( int i = 0; i< length; i++){
			queryBuilder.append(" ?");
			if(i != length -1) queryBuilder.append(",");
		}
		queryBuilder.append(")");
		return queryBuilder.toString();
	}
}

Обратите внимание, что запрос создается динамически и будет выполняться идеально. Будет только один вызов базы данных, и производительность будет хорошей. Однако, если размер пользовательского ввода сильно варьируется, мы не получим преимущества PreparedStatement от кэширования и повторного использования плана выполнения. Если вы не беспокоитесь о кэшировании PreparedStatement и в предложении не так много запросов, то, похоже, это правильный путь.

Использование NULL в подготовленном запросе оператора

Если вы действительно хотите использовать функцию кэширования подготовленных операторов, то другой подход заключается в использовании NULL в параметрах PreparedStatement. Предположим, что максимально допустимые параметры в запросе равны 10, тогда мы можем написать нашу логику, как показано ниже.

Если вы действительно хотите использовать функцию кэширования подготовленных операторов, то другой подход заключается в использовании NULL в параметрах PreparedStatement. Предположим, что максимально допустимые параметры в запросе равны 10, тогда мы можем написать нашу логику, как показано ниже.

package com.journaldev.jdbc.preparedstatement.in;

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

public class JDBCPreparedStatementNULL {

	private static final String QUERY = "select empid, name from Employee where empid in ( ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
	private static final int PARAM_SIZE = 10;
	public static void printData(int[] ids){
		
		if(ids.length > PARAM_SIZE){
			System.out.println("Maximum input size supported is "+PARAM_SIZE);
			//in real life, we can write logic to execute in batches, for simplicity I am returning
			return;
		}
		Connection con = DBConnection.getConnection();
		PreparedStatement ps = null;
		ResultSet rs = null;
		try {
			ps = con.prepareStatement(QUERY);
			
			int i = 1;
			for(; i <=ids.length; i++){
				ps.setInt(i, ids[i-1]);
			}
			
			//set null for remaining ones
			for(; i<=PARAM_SIZE;i++){
				ps.setNull(i, java.sql.Types.INTEGER);
			}
			
			rs = ps.executeQuery();
				
			while(rs.next()){
				System.out.println("Employee ID="+rs.getInt("empid")+", Name="+rs.getString("name"));
			}
				
			//close the resultset here
			try{
				rs.close();
			} catch(SQLException e){}
			
		} catch (SQLException e) {
			e.printStackTrace();
		}finally{
			try {
				ps.close();
				con.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}
		}
	}
}

Обратите внимание, что вышеуказанная программа использует один и тот же запрос PreparedStatement для выполнения В операторе предложения и получит преимущества кэширования запросов и плана выполнения. Для простоты я просто возвращаю, если количество входных параметров больше, чем размер параметров в запросе, но мы можем легко расширить его для пакетного выполнения, чтобы разрешить любое количество входных данных.

Теперь давайте напишем простую тестовую программу для проверки результатов. Для моей тестовой программы я использую таблицу сотрудников, созданную в примере источника данных JDBC.

Наш код тестовой программы является;

Наш код тестовой программы является;

package com.journaldev.jdbc.preparedstatement.in;

public class JDBCPreparedStatementINTest {

	private static int[] ids = {1,2,3,4,5,6,7,8,9,10};
	
	public static void main(String[] args) {
		
		JDBCPreparedStatementSingle.printData(ids);
		
		System.out.println("*********");
		
		JDBCPreparedStatementDynamic.printData(ids);
		
		System.out.println("*********");
		
		JDBCPreparedStatementNULL.printData(new int[]{1,2,3,4,5});
	}

}

Когда мы выполняем его с некоторыми тестовыми данными в таблице сотрудников, мы получаем результат ниже.

Employee ID=1, Name=Pankaj
Employee ID=2, Name=David
Employee ID=3, Name=Ram
Employee ID=4, Name=Leela
Employee ID=5, Name=Lisa
Employee ID=6, Name=Saurabh
Employee ID=7, Name=Mani
Employee ID=8, Name=Avinash
Employee ID=9, Name=Vijay
*********
Query=select empid, name from Employee where empid in ( ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
Employee ID=1, Name=Pankaj
Employee ID=2, Name=David
Employee ID=3, Name=Ram
Employee ID=4, Name=Leela
Employee ID=5, Name=Lisa
Employee ID=6, Name=Saurabh
Employee ID=7, Name=Mani
Employee ID=8, Name=Avinash
Employee ID=9, Name=Vijay
*********
Employee ID=1, Name=Pankaj
Employee ID=2, Name=David
Employee ID=3, Name=Ram
Employee ID=4, Name=Leela
Employee ID=5, Name=Lisa

Это все для различных опций, для которых мы должны использовать PreparedStatement в предложении IN в запросах. Вы можете использовать любой из них в зависимости от требований вашего проекта.