Use PreparedStatement para implementar operações CRUD

Capítulo 3: Usando PreparedStatement para implementar operações CRUD

3.1 Operação e acesso ao banco de dados

  • A conexão com o banco de dados é usada para enviar comandos e instruções SQL para o servidor de banco de dados e aceitar os resultados retornados pelo servidor de banco de dados. Na verdade, uma conexão de banco de dados é uma conexão Socket.

  • Existem três interfaces no pacote java.sql que definem diferentes maneiras de chamar o banco de dados:

    • Instrução: um objeto usado para executar uma instrução SQL estática e retornar o resultado gerado.
    • PrepatedStatement: A instrução SQL é pré-compilada e armazenada neste objeto.Este objeto pode ser usado para executar a instrução de forma eficiente várias vezes.
    • CallableStatement: usado para executar procedimentos armazenados SQL

Insira a descrição da imagem aqui

3.2 Desvantagens de usar a declaração para manipular tabelas de dados

  • Crie o objeto chamando o método createStatement () do objeto Connection. Este objeto é usado para executar instruções SQL estáticas e retornar os resultados da execução.

  • Os métodos a seguir são definidos na interface de instrução para executar instruções SQL:

    int excuteUpdate(String sql):执行更新操作INSERTUPDATEDELETE
    ResultSet executeQuery(String sql):执行查询操作SELECT
    
  • No entanto, existem desvantagens em usar a instrução para manipular tabelas de dados:

    • Pergunta 1: há uma operação de ortografia complicada
    • Problema 2: há um problema de injeção de SQL
  • A injeção de SQL é o uso de certos sistemas que não verificam totalmente os dados de entrada do usuário e injetam segmentos de instrução SQL ilegais ou comandos nos dados de entrada do usuário (como: SELECT usuário, senha FROM user_table WHERE user = 'a' OR 1 = 'AND password =' ​​OR '1' = '1'), de modo a usar o mecanismo SQL do sistema para concluir ações maliciosas.

  • Para Java, para evitar injeção de SQL, basta usar PreparedStatement (estendido de Instrução) para substituir Instrução.

  • Demonstração de código:

public class StatementTest {
    
    

	// 使用Statement的弊端:需要拼写sql语句,并且存在SQL注入的问题
	@Test
	public void testLogin() {
    
    
		Scanner scan = new Scanner(System.in);

		System.out.print("用户名:");
		String userName = scan.nextLine();
		System.out.print("密   码:");
		String password = scan.nextLine();

		// SELECT user,password FROM user_table WHERE USER = '1' or ' AND PASSWORD = '='1' or '1' = '1';
		String sql = "SELECT user,password FROM user_table WHERE USER = '" + userName + "' AND PASSWORD = '" + password
				+ "'";
		User user = get(sql, User.class);
		if (user != null) {
    
    
			System.out.println("登陆成功!");
		} else {
    
    
			System.out.println("用户名或密码错误!");
		}
	}

	// 使用Statement实现对数据表的查询操作
	public <T> T get(String sql, Class<T> clazz) {
    
    
		T t = null;

		Connection conn = null;
		Statement st = null;
		ResultSet rs = null;
		try {
    
    
			// 1.加载配置文件
			InputStream is = StatementTest.class.getClassLoader().getResourceAsStream("jdbc.properties");
			Properties pros = new Properties();
			pros.load(is);

			// 2.读取配置信息
			String user = pros.getProperty("user");
			String password = pros.getProperty("password");
			String url = pros.getProperty("url");
			String driverClass = pros.getProperty("driverClass");

			// 3.加载驱动
			Class.forName(driverClass);

			// 4.获取连接
			conn = DriverManager.getConnection(url, user, password);

			st = conn.createStatement();

			rs = st.executeQuery(sql);

			// 获取结果集的元数据
			ResultSetMetaData rsmd = rs.getMetaData();

			// 获取结果集的列数
			int columnCount = rsmd.getColumnCount();

			if (rs.next()) {
    
    

				t = clazz.newInstance();

				for (int i = 0; i < columnCount; i++) {
    
    
					// //1. 获取列的名称
					// String columnName = rsmd.getColumnName(i+1);

					// 1. 获取列的别名
					String columnName = rsmd.getColumnLabel(i + 1);

					// 2. 根据列名获取对应数据表中的数据
					Object columnVal = rs.getObject(columnName);

					// 3. 将数据表中得到的数据,封装进对象
					Field field = clazz.getDeclaredField(columnName);
					field.setAccessible(true);
					field.set(t, columnVal);
				}
				return t;
			}
		} catch (Exception e) {
    
    
			e.printStackTrace();
		} finally {
    
    
			// 关闭资源
			if (rs != null) {
    
    
				try {
    
    
					rs.close();
				} catch (SQLException e) {
    
    
					e.printStackTrace();
				}
			}
			if (st != null) {
    
    
				try {
    
    
					st.close();
				} catch (SQLException e) {
    
    
					e.printStackTrace();
				}
			}

			if (conn != null) {
    
    
				try {
    
    
					conn.close();
				} catch (SQLException e) {
    
    
					e.printStackTrace();
				}
			}
		}

		return null;
	}
}

Resumindo:
Insira a descrição da imagem aqui

3.3 Uso de PreparedStatement

3.3.1 Introdução ao PreparedStatement

  • O objeto PreparedStatement pode ser obtido chamando o método readyStatement (String sql) do objeto Connection

  • A interface PreparedStatement é uma subinterface de Statement, que representa uma instrução SQL pré-compilada

  • Os parâmetros na instrução SQL representados pelo objeto PreparedStatement são representados por pontos de interrogação (?). Chame o método setXxx () do objeto PreparedStatement para definir esses parâmetros. O método setXxx () tem dois parâmetros, o primeiro parâmetro é o SQL instrução a ser definida O índice do parâmetro em (começando em 1), o segundo é o valor do parâmetro na instrução SQL definida

3.3.2 Declaração Preparada vs Declaração

  • A legibilidade e manutenção do código.

  • PreparedStatement pode maximizar o desempenho:

    • A instrução DBServer pré-compilada fornece otimização de desempenho. Como a instrução preparada pode ser chamada repetidamente, o código de execução da instrução após ser compilado pelo compilador DBServer é armazenado em cache, portanto, desde que seja a mesma instrução preparada na próxima vez que for chamada, não precisa ser compilada, contanto que os parâmetros sejam passados ​​diretamente em A instrução compilada será executada no código de execução.
    • Na instrução da instrução, mesmo que seja a mesma operação, mas porque o conteúdo dos dados é diferente, a instrução inteira em si não pode ser correspondida e não há significado de armazenar a instrução em cache. O fato é que nenhum banco de dados armazenará o código de execução depois a compilação da declaração comum. Dessa forma, a instrução de entrada deve ser compilada toda vez que for executada.
    • (Verificação de sintaxe, verificação semântica, tradução em comando binário, cache)
  • PreparedStatement pode evitar injeção de SQL

3.3.3 Java e SQL tabela de conversão de tipo de dados correspondente

Tipo Java Tipo SQL
boleano MORDEU
byte TINYINT
baixo PEQUENO
int INTEIRO
longo BIGINT
Fragmento CHAR, VARCHAR, LONGVARCHAR
matriz de bytes BINARY, VAR BINARY
java.sql.Date ENCONTRO
java.sql.Time TEMPO
java.sql.Timestamp TIMESTAMP

3.3.4 Use PreparedStatement para adicionar, excluir e modificar operações

	//通用的增、删、改操作(体现一:增、删、改 ; 体现二:针对于不同的表)
	public void update(String sql,Object ... args){
    
    
		Connection conn = null;
		PreparedStatement ps = null;
		try {
    
    
			//1.获取数据库的连接
			conn = JDBCUtils.getConnection();
			
			//2.获取PreparedStatement的实例 (或:预编译sql语句)
			ps = conn.prepareStatement(sql);
			//3.填充占位符
			for(int i = 0;i < args.length;i++){
    
    
				ps.setObject(i + 1, args[i]);
			}
			
			//4.执行sql语句
			ps.execute();
		} catch (Exception e) {
    
    
			
			e.printStackTrace();
		}finally{
    
    
			//5.关闭资源
			JDBCUtils.closeResource(conn, ps);
			
		}
	}

3.3.5 Use PreparedStatement para implementar operações de consulta

	// 通用的针对于不同表的查询:返回一个对象 (version 1.0)
	public <T> T getInstance(Class<T> clazz, String sql, Object... args) {
    
    

		Connection conn = null;
		PreparedStatement ps = null;
		ResultSet rs = null;
		try {
    
    
			// 1.获取数据库连接
			conn = JDBCUtils.getConnection();

			// 2.预编译sql语句,得到PreparedStatement对象
			ps = conn.prepareStatement(sql);

			// 3.填充占位符
			for (int i = 0; i < args.length; i++) {
    
    
				ps.setObject(i + 1, args[i]);
			}

			// 4.执行executeQuery(),得到结果集:ResultSet
			rs = ps.executeQuery();

			// 5.得到结果集的元数据:ResultSetMetaData
			ResultSetMetaData rsmd = rs.getMetaData();

			// 6.1通过ResultSetMetaData得到columnCount,columnLabel;通过ResultSet得到列值
			int columnCount = rsmd.getColumnCount();
			if (rs.next()) {
    
    
				T t = clazz.newInstance();
				for (int i = 0; i < columnCount; i++) {
    
    // 遍历每一个列

					// 获取列值
					Object columnVal = rs.getObject(i + 1);
					// 获取列的别名:列的别名,使用类的属性名充当
					String columnLabel = rsmd.getColumnLabel(i + 1);
					// 6.2使用反射,给对象的相应属性赋值
					Field field = clazz.getDeclaredField(columnLabel);
					field.setAccessible(true);
					field.set(t, columnVal);

				}

				return t;

			}
		} catch (Exception e) {
    
    

			e.printStackTrace();
		} finally {
    
    
			// 7.关闭资源
			JDBCUtils.closeResource(conn, ps, rs);
		}

		return null;

	}

Descrição: a operação de consulta implementada por PreparedStatement pode substituir a operação de consulta implementada por Statement e resolver o problema de stringing de declaração e injeção de SQL.

3.4 ResultSet 与 ResultSetMetaData

3.4.1 ResultSet

  • A consulta precisa chamar o método executeQuery () de PreparedStatement e o resultado da consulta é um objeto ResultSet

  • O objeto ResultSet encapsula o conjunto de resultados da operação do banco de dados na forma de uma tabela lógica, e a interface ResultSet é implementada pelo fornecedor do banco de dados

  • O que o ResultSet retorna é na verdade uma tabela de dados. Existe um ponteiro para a frente do primeiro registro na tabela de dados.

  • O objeto ResultSet mantém um cursor apontando para a linha de dados atual.Inicialmente, o cursor está antes da primeira linha e pode ser movido para a próxima linha por meio do método next () do objeto ResultSet. Chame o método next () para verificar se a próxima linha é válida. Se for válido, o método retorna verdadeiro e o ponteiro se move para baixo. É equivalente a uma combinação dos métodos hasNext () e next () do objeto Iterator.

  • Quando o ponteiro aponta para uma linha, você pode obter o valor de cada coluna chamando getXxx (int index) ou getXxx (int columnName).

    • Por exemplo: getInt (1), getString ("nome")
    • Nota: Todos os índices na API Java relevante envolvida na interação entre Java e o banco de dados começam em 1.
  • Métodos comuns da interface ResultSet:

    • próximo booleano ()

    • getString ()


    • Insira a descrição da imagem aqui

3.4.2 ResultSetMetaData

  • Um objeto que pode ser usado para obter informações sobre os tipos e propriedades de colunas no objeto ResultSet

  • ResultSetMetaData meta = rs.getMetaData ();

    • getColumnName (int column): obtém o nome da coluna especificada

    • getColumnLabel (int column): obtém o alias da coluna especificada

    • getColumnCount (): Retorna o número de colunas no objeto ResultSet atual.

    • getColumnTypeName (int column): Recupere o nome do tipo específico do banco de dados da coluna especificada.

    • getColumnDisplaySize (int column): Indica a largura padrão máxima da coluna especificada, em caracteres.

    • isNullable (coluna int): indica se o valor na coluna especificada pode ser nulo.

    • isAutoIncrement (int column): indica se numerar automaticamente as colunas especificadas para que essas colunas ainda sejam somente leitura.

Insira a descrição da imagem aqui

Pergunta 1: Depois de obter o conjunto de resultados, como posso saber quais colunas estão no conjunto de resultados? Qual é o nome da coluna?

Precisa usar um objeto que descreva o ResultSet, ou seja, ResultSetMetaData

Pergunta 2: Sobre ResultSetMetaData

  1. Como obter ResultSetMetaData : basta chamar o método getMetaData () de ResultSet
  2. Obtenha quantas colunas estão no ResultSet: chame o método getColumnCount () de ResultSetMetaData
  3. Obtenha o alias de cada coluna do ResultSet: chame o método getColumnLabel () de ResultSetMetaData

Insira a descrição da imagem aqui

3.5 Liberação de recursos

  • Libere o ResultSet, a instrução e a conexão.
  • A conexão com o banco de dados (Connection) é um recurso muito raro e deve ser liberado imediatamente após o esgotamento.Se a conexão não puder ser encerrada a tempo e corretamente, o sistema ficará inativo. O princípio de usar o Connection é criar o mais tarde possível e liberar o mais cedo possível.
  • Ele pode ser fechado finalmente, para garantir que outros códigos sejam anormais no tempo, e o recurso deve ser fechado.

3.6 Resumo da API JDBC

  • Dois pensamentos

    • Idéias de programação orientada a interface

    • Idéia ORM (mapeamento relacional de objetos)

      • Uma tabela de dados corresponde a uma classe java
      • Um registro na tabela corresponde a um objeto da classe java
      • Um campo na tabela corresponde a um atributo da classe java

    SQL precisa ser escrito em combinação com nomes de colunas e nomes de atributos de tabelas. Preste atenção aos apelidos.

  • Duas tecnologias

    • Metadados do conjunto de resultados JDBC: ResultSetMetaData
      • Obtenha o número de colunas: getColumnCount ()
      • Obtenha o alias da coluna: getColumnLabel ()
    • Por meio da reflexão, crie um objeto da classe especificada, obtenha os atributos especificados e atribua valores

Acho que você gosta

Origin blog.csdn.net/qq_44346427/article/details/112441585
Recomendado
Clasificación