Verwenden Sie PreparedStatement, um CRUD-Operationen zu implementieren

Kapitel 3: Verwenden von PreparedStatement zum Implementieren von CRUD-Operationen

3.1 Betrieb und Zugriff auf die Datenbank

  • Die Datenbankverbindung wird verwendet, um Befehle und SQL-Anweisungen an den Datenbankserver zu senden und die vom Datenbankserver zurückgegebenen Ergebnisse zu akzeptieren. Tatsächlich ist eine Datenbankverbindung eine Socket-Verbindung.

  • Das Paket java.sql enthält drei Schnittstellen, die verschiedene Arten des Aufrufs der Datenbank definieren:

    • Anweisung: Ein Objekt, mit dem eine statische SQL-Anweisung ausgeführt und das generierte Ergebnis zurückgegeben wird.
    • PrepatedStatement: Die SQL-Anweisung wird vorkompiliert und in diesem Objekt gespeichert. Mit diesem Objekt kann die Anweisung mehrmals effizient ausgeführt werden.
    • CallableStatement: Wird zum Ausführen gespeicherter SQL-Prozeduren verwendet

Fügen Sie hier eine Bildbeschreibung ein

3.2 Nachteile der Verwendung von Statement zum Bearbeiten von Datentabellen

  • Erstellen Sie das Objekt, indem Sie die Methode createStatement () des Verbindungsobjekts aufrufen. Dieses Objekt wird verwendet, um statische SQL-Anweisungen auszuführen und die Ausführungsergebnisse zurückzugeben.

  • Die folgenden Methoden sind in der Anweisungsschnittstelle definiert, um SQL-Anweisungen auszuführen:

    int excuteUpdate(String sql):执行更新操作INSERTUPDATEDELETE
    ResultSet executeQuery(String sql):执行查询操作SELECT
    
  • Die Verwendung von Statement zum Bearbeiten von Datentabellen hat jedoch Nachteile:

    • Frage 1: Es gibt eine umständliche Rechtschreiboperation
    • Problem 2: Es liegt ein SQL-Injection-Problem vor
  • SQL-Injection ist die Verwendung bestimmter Systeme, die die Benutzereingabedaten nicht vollständig überprüfen und unzulässige SQL-Anweisungssegmente oder -befehle in die Benutzereingabedaten einfügen (z. B.: SELECT user, Kennwort FROM user_table WHERE user = 'a' OR 1 = 'AND password =' ​​OR '1' = '1'), um die SQL-Engine des Systems zum Ausführen böswilliger Aktionen zu verwenden.

  • Um die SQL-Injection zu verhindern, verwenden Sie für Java einfach PreparedStatement (erweitert von Statement), um Statement zu ersetzen.

  • 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 Summe:
Fügen Sie hier eine Bildbeschreibung ein

3.3 Verwendung von PreparedStatement

3.3.1 Einführung in PreparedStatement

  • Das PreparedStatement-Objekt kann durch Aufrufen der prepareStatement- Methode (String sql) des Connection- Objekts abgerufen werden

  • Die PreparedStatement-Schnittstelle ist eine Unterschnittstelle von Statement, die eine vorkompilierte SQL-Anweisung darstellt

  • Die Parameter in der SQL-Anweisung, die durch das PreparedStatement-Objekt dargestellt werden, werden durch Fragezeichen (?) Dargestellt. Rufen Sie die setXxx () -Methode des PreparedStatement-Objekts auf, um diese Parameter festzulegen. Die setXxx () -Methode verfügt über zwei Parameter. Der erste Parameter ist SQL Zu setzende Anweisung Der Index des Parameters in (ab 1), der zweite ist der Wert des Parameters in der gesetzten SQL-Anweisung

3.3.2 PreparedStatement vs Statement

  • Die Lesbarkeit und Wartbarkeit des Codes.

  • PreparedStatement kann die Leistung maximieren:

    • Die vorkompilierte Anweisung DBServer bietet Leistungsoptimierung. Da die vorbereitete Anweisung möglicherweise wiederholt aufgerufen wird, wird der Ausführungscode der Anweisung nach dem Kompilieren durch den DBServer-Compiler zwischengespeichert. Solange es sich beim nächsten Aufruf um dieselbe vorbereitete Anweisung handelt, muss sie nicht kompiliert werden. Solange die Parameter direkt übergeben werden Die kompilierte Anweisung wird im Ausführungscode ausgeführt.
    • In der Anweisungsanweisung kann die gesamte Anweisung selbst nicht abgeglichen werden, auch wenn es sich um dieselbe Operation handelt, aber da der Dateninhalt unterschiedlich ist, und es hat keine Bedeutung, die Anweisung zwischenzuspeichern. Tatsache ist, dass keine Datenbank den Ausführungscode danach zwischenspeichert die Zusammenstellung der ordentlichen Aussage. Auf diese Weise muss die eingehende Anweisung bei jeder Ausführung kompiliert werden.
    • (Syntaxprüfung, semantische Prüfung, Übersetzung in einen Binärbefehl, Cache)
  • PreparedStatement kann die SQL-Injection verhindern

3.3.3 Java- und SQL-entsprechende Datentypkonvertierungstabelle

Java-Typ SQL-Typ
Boolescher Wert BISSCHEN
Byte TINYINT
kurz SMALLINT
int GANZE ZAHL
lange BIGINT
String CHAR, VARCHAR, LONGVARCHAR
Byte-Array BINARY, VAR BINARY
java.sql.Date DATUM
java.sql.Time ZEIT
java.sql.Timestamp TIMESTAMP

3.3.4 Verwenden Sie PreparedStatement, um Vorgänge hinzuzufügen, zu löschen und zu ändern

	//通用的增、删、改操作(体现一:增、删、改 ; 体现二:针对于不同的表)
	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 Verwenden Sie PreparedStatement, um Abfrageoperationen zu implementieren

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

	}

Beschreibung: Die von PreparedStatement implementierte Abfrageoperation kann die von Statement implementierte Abfrageoperation ersetzen und das Problem der Anweisungszeichenfolge und der SQL-Injection lösen.

3.4 ResultSet 与 ResultSetMetaData

3.4.1 ResultSet

  • Die Abfrage muss die Methode executeQuery () von PreparedStatement aufrufen, und das Abfrageergebnis ist ein ResultSet-Objekt

  • Das ResultSet-Objekt kapselt die Ergebnismenge der Datenbankoperation in Form einer logischen Tabelle, und die ResultSet-Schnittstelle wird vom Datenbankanbieter implementiert

  • Was das ResultSet zurückgibt, ist tatsächlich eine Datentabelle. In der Datentabelle befindet sich ein Zeiger auf die Vorderseite des ersten Datensatzes.

  • Das ResultSet-Objekt behält einen Cursor bei , der auf die aktuelle Datenzeile zeigt . Zunächst befindet sich der Cursor vor der ersten Zeile und kann mit der next () -Methode des ResultSet-Objekts in die nächste Zeile verschoben werden. Rufen Sie die next () -Methode auf, um zu überprüfen, ob die nächste Zeile gültig ist. Wenn es gültig ist, gibt die Methode true zurück und der Zeiger bewegt sich nach unten. Dies entspricht einer Kombination der Methoden hasNext () und next () des Iterator-Objekts.

  • Wenn der Zeiger auf eine Zeile zeigt, können Sie den Wert jeder Spalte abrufen, indem Sie getXxx (int index) oder getXxx (int columnName) aufrufen.

    • Zum Beispiel: getInt (1), getString ("name")
    • Hinweis: Die Indizes in der relevanten Java-API, die an der Interaktion zwischen Java und der Datenbank beteiligt sind, beginnen alle bei 1.
  • Allgemeine Methoden der ResultSet-Schnittstelle:

    • boolean next ()

    • getString ()


    • Fügen Sie hier eine Bildbeschreibung ein

3.4.2 ResultSetMetaData

  • Ein Objekt, mit dem Informationen zu den Typen und Eigenschaften von Spalten im ResultSet-Objekt abgerufen werden können

  • ResultSetMetaData meta = rs.getMetaData ();

    • getColumnName (int column): Ruft den Namen der angegebenen Spalte ab

    • getColumnLabel (int column): Ruft den Alias ​​der angegebenen Spalte ab

    • getColumnCount (): Gibt die Anzahl der Spalten im aktuellen ResultSet-Objekt zurück.

    • getColumnTypeName (int column): Ruft den datenbankspezifischen Typnamen der angegebenen Spalte ab.

    • getColumnDisplaySize (int column): Gibt die maximale Standardbreite der angegebenen Spalte in Zeichen an.

    • isNullable (int column): Gibt an, ob der Wert in der angegebenen Spalte null sein kann.

    • isAutoIncrement (int column): Gibt an, ob die angegebenen Spalten automatisch nummeriert werden sollen, damit diese Spalten weiterhin schreibgeschützt sind.

Fügen Sie hier eine Bildbeschreibung ein

Frage 1: Woher weiß ich nach dem Abrufen der Ergebnismenge, welche Spalten in der Ergebnismenge enthalten sind? Wie lautet der Spaltenname?

Sie müssen ein Objekt verwenden, das das ResultSet beschreibt, nämlich ResultSetMetaData

Frage 2: Über ResultSetMetaData

  1. So erhalten Sie ResultSetMetaData : Rufen Sie einfach die Methode getMetaData () von ResultSet auf
  2. Abrufen der Anzahl der Spalten im ResultSet: Rufen Sie die Methode getColumnCount () von ResultSetMetaData auf
  3. Rufen Sie den Alias ​​jeder Spalte des ResultSet ab: Rufen Sie die Methode getColumnLabel () von ResultSetMetaData auf

Fügen Sie hier eine Bildbeschreibung ein

3.5 Freigabe von Ressourcen

  • Geben Sie das ResultSet, die Anweisung und die Verbindung frei.
  • Die Datenbankverbindung (Verbindung) ist eine sehr seltene Ressource und muss sofort nach ihrer Verwendung freigegeben werden. Wenn die Verbindung nicht rechtzeitig und korrekt geschlossen werden kann, ist das System ausgefallen. Das Prinzip der Verwendung von Connection besteht darin, so spät wie möglich zu erstellen und so früh wie möglich freizugeben.
  • Es kann endgültig geschlossen werden, um sicherzustellen, dass andere Codes zeitlich abnormal sind und die Ressource geschlossen werden muss.

3.6 Zusammenfassung der JDBC-API

  • Zwei Gedanken

    • Schnittstellenorientierte Programmierideen

    • ORM-Idee (objektrelationale Zuordnung)

      • Eine Datentabelle entspricht einer Java-Klasse
      • Ein Datensatz in der Tabelle entspricht einem Objekt der Java-Klasse
      • Ein Feld in der Tabelle entspricht einem Attribut der Java-Klasse

    SQL muss in Kombination mit Spaltennamen und Tabellenattributnamen geschrieben werden. Achten Sie auf Aliase.

  • Zwei Technologien

    • Metadaten der JDBC-Ergebnismenge: ResultSetMetaData
      • Ermitteln Sie die Anzahl der Spalten: getColumnCount ()
      • Ruft den Alias ​​der Spalte ab: getColumnLabel ()
    • Erstellen Sie durch Reflektion ein Objekt der angegebenen Klasse, rufen Sie die angegebenen Attribute ab und weisen Sie Werte zu

Ich denke du magst

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