JDBC 笔记(二)Statement、PreparedStatement预编译与原生增删查改、通用增删查改

1、预编译:statement 与 PreparedStatement

1.1 sql提供访问的接口:

  • 数据库连接被用于向数据库服务器发送命令和 SQL 语句,在连接建立后,需要对数据库进行访问,执行 sql 语句
  • 在 java.sql 包中有 3 个接口分别定义了对数据库的调用的不同方式:
     |-----   Statement

            |------PreparedStatement

                   |------CallableStatement

使用statement 进行预编译可能会导致SQL注入的发生

1.2 SQL 注入攻击

  • SQL 注入是利用某些系统没有对用户输入的数据进行充分的检查,而在用户输入数据中注入非法的 SQL 语句段或命令(如:SELECT user, password FROM user_table WHERE user='a' OR 1 = ' AND password = ' OR '1' = '1') ,从而利用系统的 SQL 引擎完成恶意行为的做法
  • 对于 Java 而言,要防范 SQL 注入,只要用 PreparedStatement(从Statement扩展而来) 取代 Statement 就可以了

1.3 PreparedStatement

  • 可以通过调用 Connection 对象的 preparedStatement() 方法获取 PreparedStatement 对象
  • PreparedStatement 接口是 Statement 的子接口,它表示一条预编译过的 SQL 语句
  • PreparedStatement 对象所代表的 SQL 语句中的参数用问号(?)来表示,调用 PreparedStatement 对象的 setXxx() 方法来设置这些参数. setXxx() 方法有两个参数,第一个参数是要设置的 SQL 语句中的参数的索引(从 1 开始),第二个是设置的 SQL 语句中的参数的值

1.4 PreparedStatement vs Statement

  • 代码的可读性和可维护性.
  • PreparedStatement 能最大可能提高性能:
  1. DBServer会对预编译语句提供性能优化。因为预编译语句有可能被重复调用,所以语句在被DBServer的编译器编译后的执行代码被缓存下来,那么下次调用时只要是相同的预编译语句就不需要编译,只要将参数直接传入编译过的语句执行代码中就会得到执行。

  2. 在statement语句中,即使是相同操作但因为数据内容不一样,所以整个语句本身不能匹配,没有缓存语句的意义.事实是没有数据库会对普通语句编译后的执行代码缓存.这样每执行一次都要对传入的语句编译一次.  

  3. (语法检查,语义检查,翻译成二进制命令,缓存)

  • PreparedStatement 可以防止 SQL 注入防止 SQL 注入

1.5 数据类型转换表

             

1.6 连接数据库、操作表的步骤:

           

注意:

  • 释放ResultSet, Statement,Connection。
  • 数据库连接(Connection)是非常稀有的资源,用完后必须马上释放,如果Connection不能及时正确的关闭将导致系统宕机。Connection的使用原则是尽量晚创建,尽量早的释放。

2 、ResultSet

2.1 ResultSet 的作用:

  • 通过调用 PreparedStatement 对象的 excuteQuery() 方法创建该对象
  • ResultSet 对象以逻辑表格的形式封装了执行数据库操作的结果集,ResultSet 接口由数据库厂商实现
  • ResultSet 对象维护了一个指向当前数据行的游标,初始的时候,游标在第一行之前,可以通过 ResultSet 对象的 next() 方法移动到下一行

ResultSet 接口的常用方法:

  1. boolean next()
    getString( )

2.2 案例

(1)

(2)

    

2.3 ResultSet的说明

   1. 查询需要调用Prepared Statement 的 executeQuery() 方法,查询结果是一个 ResultSet 对象
   2. 关于 ResultSet:代表结果集

  • ResultSet: 结果集. 封装了使用 JDBC 进行查询的结果.
  • 调用 PreparedStatement 对象的 executeQuery() 可以得到结果集.
  • ResultSet 返回的实际上就是一张数据表. 有一个指针指向数据表的第一条记录的前面.

   3.可以调用 next() 方法检测下一行是否有效. 若有效该方法返回 true, 且指针下移. 相当于Iterator 对象的 hasNext() 和 next() 方法的结合体
   4.当指针指向一行时, 可以通过调用 getXxx(int index) 或 getXxx(int columnName) 获取每一列的值.
         例如: getInt(1), getString("name")
   5.ResultSet 当然也需要进行关闭.

3、 普通的增删查改

3.1 创建一个类customer

public class Customer {

	private int id;
	private String name;
	private String email;
	private Date birth;

	public Customer() {
		super();
	}
		
	public Customer(int id, String name, String email, Date birth) {
		super();
		this.id = id;
		this.name = name;
		this.email = email;
		this.birth = birth;
	}

3.2 创建jdbc连接,并封装为方法

/*	 * 获取数据库的连接
	 */
	public static Connection getConnection() {
		// 数据库的连接
		Connection connection = null;
		InputStream is = null;
		try {
			is = JDBCUtils.class.getClassLoader().getResourceAsStream("sqlDriver.properties");
			Properties ps = new Properties();
			ps.load(is);
			String user = ps.getProperty("user");
			String password = ps.getProperty("password");
			String driverClass = ps.getProperty("driverClass");
			String url = ps.getProperty("url");

			// 创建Driver对象
			Class clazz = Class.forName(driverClass);
			Object obj = clazz.newInstance();
			Driver driver = (Driver) obj;
			// 注册驱动
			DriverManager.registerDriver(driver);
			connection = DriverManager.getConnection(url, user, password);
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			if (is != null) {
				try {
					is.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}

		return connection;
	}

3.3 在操作完成后要进行连接的关闭

	public static void close(Connection connection, PreparedStatement ps) {
		if (connection != null) {
			try {
				connection.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}
		}

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

	public static void close(Connection connection, PreparedStatement ps, ResultSet rs) {
		close(connection,ps);
		
		if(rs != null){
			try {
				rs.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}
		}		
	}

3.4 增删查改操作

/*
 * 对数据库的  增 ,删,改,查
 */
public class CRUDTest {

	/*
	 * 从数据库中查找一条数据
	 */
	@Test
	public void select() throws Exception {

		Customer customer = getCustomerById();
		System.out.println(customer);
		
		List<Customer> customers = getCustomers();
		for (Customer customer2 : customers) {
			System.out.println(customer2);
		}

	}

	/*
	 * 查询多条数据
	 */
	public List<Customer> getCustomers() throws Exception {
		// 1.获取数据库的连接
		Connection connection = JDBCUtils.getConnection();

		String sql = "select * from customers";

		// 2.预编译  -- 如果没有占位符可以不填充数据
		PreparedStatement ps = connection.prepareStatement(sql);

		// 4.执行sql语句
		ResultSet rs = ps.executeQuery(); // 查找的结果都已经放到ResultSet中了

		List<Customer> list = new ArrayList<>();
	
		while(rs.next()){
			//获取数据
			int id = rs.getInt("id");
			String name = rs.getString("name");
			String email = rs.getString("email");
			Date birth = rs.getDate("birth");
			
			//将每条数据放入集合中
			list.add(new Customer(id, name, email, birth));
		}

		// 6.关闭资源
		JDBCUtils.close(connection, ps, rs);
		return list;
	}

	/*
	 * 查询一条数据
	 */
	public Customer getCustomerById() throws Exception {
		// 1.获取数据库的连接
		Connection connection = JDBCUtils.getConnection();

		String sql = "select * from customers where id = ?";

		// 2.预编译
		PreparedStatement ps = connection.prepareStatement(sql);

		// 3.填充数据
		ps.setInt(1, 20);

		// 4.执行sql语句
		ResultSet rs = ps.executeQuery(); // 查找的结果都已经放到ResultSet中了

		Customer customer = null;

		// 5.取数据
		if (rs.next()) { // 如果有数据就返回true,否则返回false
			// 获取列上值的第一种方式
			// int id = rs.getInt(1); //根据列的索引获取对应的列上的值
			// String name = rs.getString(2);
			// String email = rs.getString(3);
			// Date date = rs.getDate(4);

			// 获取列上值的第二种方式
			int id = rs.getInt("id");
			String name = rs.getString("name");
			String email = rs.getString("email");
			Date birth = rs.getDate("birth");

			// 考虑将数据进行封装
			customer = new Customer(id, name, email, birth);

			// 输出获取到的内容
			System.out.println(id + " " + name + " " + email + " " + birth);
		}

		// 6.关闭资源
		JDBCUtils.close(connection, ps, rs);
		return customer;
	}

	/*
	 * 向数据库中插入一条数据
	 */
	@Test
	public void insert() {
		Connection connection = null;
		PreparedStatement ps = null;
		try {
			// 1 : 连接数据库
			connection = JDBCUtils.getConnection();
			// sql语句
			String sql = "INSERT INTO customers(NAME,email,birth) " + "VALUES(?,?,?);";
			// 2.预编译
			ps = connection.prepareStatement(sql);
			// 3.设置内容
			ps.setString(1, "强哥"); // 第一个参数:第几个问号 第二个参数:内容
			ps.setString(2, "[email protected]");
			// 获取一个sql下的Date
			Date date = new Date(new java.util.Date().getTime());
			ps.setDate(3, date);
			// 4.执行sql语句
			ps.execute(); // 如果是查询语句返回true
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			// 5.关闭资源
			JDBCUtils.close(connection, ps);
		}
	}

	/*
	 * 删除一条件数
	 */
	@Test
	public void delete() throws Exception {
		// 1. 获取数据库的连接
		Connection connection = JDBCUtils.getConnection();

		String sql = "DELETE FROM customers WHERE id = ?";
		// 2. 预编译
		PreparedStatement ps = connection.prepareStatement(sql);

		// 3.填充数据
		ps.setInt(1, 19);

		// 4.执行sql语句
		int executeUpdate = ps.executeUpdate();
		System.out.println(executeUpdate + "条受到影响");

		// 5.关闭资源
		JDBCUtils.close(connection, ps);
	}

	/*
	 * 修改一条件数
	 */
	@Test
	public void update() throws Exception {

		Connection connection = null;
		PreparedStatement ps = null;
		try {
			connection = JDBCUtils.getConnection();

			String sql = "UPDATE customers SET NAME = ? WHERE id = ?";
			ps = connection.prepareStatement(sql);

			ps.setString(1, "成哥");
			ps.setInt(2, 18);

			int executeUpdate = ps.executeUpdate();
			System.out.println(executeUpdate + "条数据受到影响");
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			JDBCUtils.close(connection, ps);
		}
	}
}

4、通用增删查改

创建类 User.java

public class User {

	private int id;
	private String name;
	private String address;
	private String phone;
	

4.1 创建通用连接方案 - 通用增删改

	/*
	 * 通用的增删改
	 */
	public static int UID(String sql,Object ...objects) {
		// 1. 获取数据库的连接
		Connection connection = null;
		// 2. 预编译
		PreparedStatement ps = null;
		// 4.执行sql语句
		int executeUpdate = -1;
		try {
			connection = JDBCUtils.getConnection();
			ps = connection.prepareStatement(sql);
			// 3.填充数据
			for (int i = 0; i < objects.length; i++) { // i 表示索引
				ps.setObject(i + 1, objects[i]); // i + 1表示的是第几个占位符
			}
			executeUpdate = ps.executeUpdate();
		} catch (Exception e) {
			e.printStackTrace();
		}finally{
			// 5.关闭资源
			JDBCUtils.close(connection, ps);
		}
		return executeUpdate;
	}

测试代码:

        /*
	 * 测试通用的增删改
	 */
	@Test
	public void insert(){
		String sql = "INSERT INTO customers(NAME,email,birth) VALUES(?,?,?)";
		Date date = new java.sql.Date(new java.util.Date().getTime());
		int uid = JDBCUtils.UID(sql, "强哥","[email protected]",date);
		System.out.println(uid + "条数据受到影响");
	}

4.2 通用查找(一条或多条)

只查一条数据:

/*
	 * 通用的查找 (只有一条数据)
	 * 
	 * Class<T> clazz : 运行时类的对象
	 */
	public static <T> T getObject(Class<T> clazz,String sql,Object ...objects) throws Exception{
		
		//1.获取数据库的连接
		Connection connection = JDBCUtils.getConnection();
		
		//2.预编译
		PreparedStatement ps = connection.prepareStatement(sql);
		
		//3.填充数据
		for (int i = 0; i < objects.length; i++) {
			ps.setObject(i + 1, objects[i]);
		}
		
		//4.执行sql语句
		ResultSet rs = ps.executeQuery();
		
		ResultSetMetaData md = rs.getMetaData(); //获取元数据。
		//获取列的数量
		int columnCount = md.getColumnCount();
		//创建对象
		T t = clazz.newInstance();
		while(rs.next()){ //遍历每一条数据
			/*
			 * 第一个问题 : 如何获取到表的列数
			 * 第二个问题 : 需要知道类中的属性
			 * 第三个问题 : 对象中属性的名字怎么来
			 */
			for (int i = 0; i < columnCount; i++) {
				//获取某列的列名(如果有别名获取的是别名)
				String columnLabel = md.getColumnLabel(i + 1); 
				 //通过列名获取列中的数据。
				Object value = rs.getObject(columnLabel);

				//封装
				//通过反射 - 给对象中的属性进行赋值
				//将表中的列名当作类中的属性名。如果列名和属性名不一样,可以通过别名的方式(别名 = 属性名)
				//通过列名就获取到了类中的对应的属性
				Field declaredField = clazz.getDeclaredField(columnLabel);
				declaredField.setAccessible(true);
				/*
				 * 第一个参数 : 是给哪个对象进行赋值
				 * 第二个参数 : 属性值
				 */
				declaredField.set(t, value);
			}
		}
		//关闭资源
		JDBCUtils.close(connection, ps, rs);
		
		return t;
	}

查多条数据:

/*
	 * 通用的查找 (返回多条数据)
	 * 
	 */
	public static <T> List<T> getObjects(Class<T> clazz,String sql,Object ...objects) throws Exception{
		
		//1.获取数据库的连接
		Connection connection = JDBCUtils.getConnection();
		
		//2.预编译
		PreparedStatement ps = connection.prepareStatement(sql);
		
		//3.填充数据
		for (int i = 0; i < objects.length; i++) {
			ps.setObject(i + 1, objects[i]);
		}
		
		//4.执行sql语句
		ResultSet rs = ps.executeQuery();
		
		ResultSetMetaData md = rs.getMetaData(); //获取元数据。
		//获取列的数量
		int columnCount = md.getColumnCount();
		//创建一个集合
		List<T> list = new ArrayList<>();
		
		while(rs.next()){ //遍历每一条数据
			//创建对象
			T t = clazz.newInstance();
			/*
			 * 第一个问题 : 如何获取到表的列数
			 * 第二个问题 : 需要知道类中的属性
			 * 第三个问题 : 对象中属性的名字怎么来
			 */
			for (int i = 0; i < columnCount; i++) {
				//获取某列的列名(如果有别名获取的是别名)
				String columnLabel = md.getColumnLabel(i + 1); 
				 //通过列名获取列中的数据。
				Object value = rs.getObject(columnLabel);

				//封装
				//通过反射 - 给对象中的属性进行赋值
				//将表中的列名当作类中的属性名。如果列名和属性名不一样,可以通过别名的方式(别名 = 属性名)
				//通过列名就获取到了类中的对应的属性
				Field declaredField = clazz.getDeclaredField(columnLabel);
				declaredField.setAccessible(true);
				/*
				 * 第一个参数 : 是给哪个对象进行赋值
				 * 第二个参数 : 属性值
				 */
				declaredField.set(t, value);
			}
			
			//将对象放入到集合中
			list.add(t);
		}
		//关闭资源
		JDBCUtils.close(connection, ps, rs);
		
		return list;
	}

测试代码:

                String sql = "select id,name,email,birth from customers where id = ?";
		Customer customer = getObject(Customer.class, sql, 5);
		System.out.println(customer);
		
		
		sql = "select * from users where id = ?";
		User object = getObject(User.class, sql, 1);
		System.out.println(object);
		
		sql = "select * from users";
		List<User> users = getObjects(User.class, sql);
		for (User user : users) {
			System.out.println(user);
		}
发布了222 篇原创文章 · 获赞 60 · 访问量 4万+

猜你喜欢

转载自blog.csdn.net/weixin_42405670/article/details/103927276
今日推荐