Use PreparedStatement to implement CRUD operations

Chapter 3: Using PreparedStatement to implement CRUD operations

3.1 Operation and access to the database

  • The database connection is used to send commands and SQL statements to the database server and accept the results returned by the database server. In fact, a database connection is a Socket connection.

  • There are three interfaces in the java.sql package that define different ways of calling the database:

    • Statement: An object used to execute a static SQL statement and return the result it generates.
    • PrepatedStatement: The SQL statement is precompiled and stored in this object. This object can be used to execute the statement efficiently multiple times.
    • CallableStatement: used to execute SQL stored procedures

Insert picture description here

3.2 Disadvantages of using Statement to manipulate data tables

  • Create the object by calling the createStatement() method of the Connection object. This object is used to execute static SQL statements and return the execution results.

  • The following methods are defined in the Statement interface to execute SQL statements:

    int excuteUpdate(String sql):执行更新操作INSERTUPDATEDELETE
    ResultSet executeQuery(String sql):执行查询操作SELECT
    
  • However, there are drawbacks to using Statement to manipulate data tables:

    • Question 1: There is a cumbersome spelling operation
    • Problem 2: There is a SQL injection problem
  • SQL injection is the use of certain systems that do not fully check the user input data, and inject illegal SQL statement segments or commands into the user input data (such as: SELECT user, password FROM user_table WHERE user='a' OR 1 = 'AND password =' ​​OR '1' = '1'), so as to use the system's SQL engine to complete malicious actions.

  • For Java, to prevent SQL injection, just use PreparedStatement (extended from Statement) to replace Statement.

  • Code demo:

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;
	}
}

In summary:
Insert picture description here

3.3 Use of PreparedStatement

3.3.1 Introduction to PreparedStatement

  • The PreparedStatement object can be obtained by calling the preparedStatement(String sql) method of the Connection object

  • The PreparedStatement interface is a sub-interface of Statement, which represents a pre-compiled SQL statement

  • The parameters in the SQL statement represented by the PreparedStatement object are represented by question marks (?). Call the setXxx() method of the PreparedStatement object to set these parameters. The setXxx() method has two parameters, the first parameter is the SQL statement to be set The index of the parameter in (starting from 1), the second is the value of the parameter in the set SQL statement

3.3.2 PreparedStatement vs Statement

  • The readability and maintainability of the code.

  • PreparedStatement can maximize performance:

    • DBServer be precompiled statement provides performance optimization. Because the prepared statement may be called repeatedly, the execution code of the statement after being compiled by the DBServer compiler is cached, so as long as it is the same prepared statement the next time it is called, it does not need to be compiled, as long as the parameters are directly passed in The compiled statement will be executed in the execution code.
    • In the statement statement, even if it is the same operation but because the data content is different, the entire statement itself cannot be matched, and there is no meaning of caching the statement. The fact is that no database will cache the execution code after the compilation of the ordinary statement. In this way, the incoming statement must be compiled every time it is executed.
    • (Syntax check, semantic check, translation into binary command, cache)
  • PreparedStatement can prevent SQL injection

3.3.3 Java and SQL corresponding data type conversion table

Java type SQL type
boolean BIT
byte TINYINT
short SMALLINT
int INTEGER
long BIGINT
String CHAR,VARCHAR,LONGVARCHAR
byte array BINARY , VAR BINARY
java.sql.Date DATE
java.sql.Time TIME
java.sql.Timestamp TIMESTAMP

3.3.4 Use PreparedStatement to add, delete, and modify operations

	//通用的增、删、改操作(体现一:增、删、改 ; 体现二:针对于不同的表)
	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 to implement query operations

	// 通用的针对于不同表的查询:返回一个对象 (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;

	}

Description: The query operation implemented by PreparedStatement can replace the query operation implemented by Statement and solve the problem of Statement stringing and SQL injection.

3.4 ResultSet与ResultSetMetaData

3.4.1 ResultSet

  • The query needs to call the executeQuery() method of PreparedStatement, and the query result is a ResultSet object

  • The ResultSet object encapsulates the result set of the database operation in the form of a logical table, and the ResultSet interface is implemented by the database vendor

  • What the ResultSet returns is actually a data table. There is a pointer to the front of the first record in the data table.

  • The ResultSet object maintains a cursor that points to the current data row . Initially, the cursor is before the first row and can be moved to the next row through the next() method of the ResultSet object. Call the next() method to check whether the next line is valid. If it is valid, the method returns true and the pointer moves down. It is equivalent to a combination of the hasNext() and next() methods of the Iterator object.

  • When the pointer points to a row, you can get the value of each column by calling getXxx(int ​​index) or getXxx(int ​​columnName).

    • For example: getInt(1), getString("name")
    • Note: The indexes in the relevant Java API involved in the interaction between Java and the database all start from 1.
  • Common methods of the ResultSet interface:

    • boolean next()

    • getString()


    • Insert picture description here

3.4.2 ResultSetMetaData

  • An object that can be used to obtain information about the types and properties of the columns in the ResultSet object

  • ResultSetMetaData meta = rs.getMetaData();

    • getColumnName (int column): Get the name of the specified column

    • getColumnLabel (int column): Get the alias of the specified column

    • getColumnCount (): Returns the number of columns in the current ResultSet object.

    • getColumnTypeName(int column): Retrieve the database-specific type name of the specified column.

    • getColumnDisplaySize(int column): Indicates the maximum standard width of the specified column, in characters.

    • isNullable (int column): indicates whether the value in the specified column can be null.

    • isAutoIncrement(int column): Indicates whether to automatically number the specified columns so that these columns are still read-only.

Insert picture description here

Question 1: After getting the result set, how do I know which columns are in the result set? What is the column name?

​ Need to use an object describing the ResultSet, namely ResultSetMetaData

Question 2: About ResultSetMetaData

  1. How to get ResultSetMetaData : just call the getMetaData() method of ResultSet
  2. Get how many columns are in the ResultSet: call the getColumnCount() method of ResultSetMetaData
  3. Get the alias of each column of the ResultSet: call the getColumnLabel() method of ResultSetMetaData

Insert picture description here

3.5 Release of resources

  • Release the ResultSet, Statement, and Connection.
  • The database connection (Connection) is a very rare resource and must be released immediately after it is used up. If the Connection cannot be closed in time and correctly, the system will be down. The principle of using Connection is to create it as late as possible and release it as early as possible.
  • It can be closed in finally, to ensure that other codes are abnormal in time, and the resource must be closed.

3.6 Summary of JDBC API

  • Two thoughts

    • Interface-oriented programming ideas

    • ORM idea (object relational mapping)

      • A data table corresponds to a java class
      • A record in the table corresponds to an object of the java class
      • A field in the table corresponds to an attribute of the java class

    SQL needs to be written in combination with column names and table attribute names. Pay attention to aliases.

  • Two technologies

    • Metadata of JDBC result set: ResultSetMetaData
      • Get the number of columns: getColumnCount()
      • Get the alias of the column: getColumnLabel()
    • Through reflection, create an object of the specified class, get the specified attributes and assign values

Guess you like

Origin blog.csdn.net/qq_44346427/article/details/112441585