JDBC详解(三):使用PreparedStatement实现CRUD操作(超详解)

news2024/11/27 12:51:38

JDBC详解(三):使用PreparedStatement实现CRUD操作(超详解)

  • 前言
  • 一、操作和访问数据库
  • 二、使用Statement操作数据表的弊端
  • 三、PreparedStatement的使用
    • 1、PreparedStatement介绍
    • 2、PreparedStatement vs Statement
    • 3、Java与SQL对应数据类型转换表
    • 4、使用PreparedStatement实现增、删、改操作
      • 4.1 针对customers表实现增加(insert)操作
      • 4.2 针对customers表实现修改(update)操作
      • 4.3 针对通用表的通用增删改的操作
      • 4.4 使用PreparedStatement实现查询操作
        • (1)ResultSet
        • (2)ResultSetMetaData
        • (3)代码演示
  • 四、资源的释放
  • 五、JDBC API小结
    • 练习题1
    • 练习题2



前言

本博主将用CSDN记录软件开发求学之路上亲身所得与所学的心得与知识,有兴趣的小伙伴可以关注博主!也许一个人独行,可以走的很快,但是一群人结伴而行,才能走的更远!

一、操作和访问数据库

⭕ 数据库连接被用于向数据库服务器发送命令和 SQL 语句,并接受数据库服务器返回的结果。其实一个数据库连接就是一个Socket连接。

⭕ 在 java.sql 包中有 3 个接口分别定义了对数据库的调用的不同方式:

  • Statement:用于执行静态 SQL 语句并返回它所生成结果的对象。
  • PrepatedStatementSQL 语句被预编译并存储在此对象中,可以使用此对象多次高效地执行该语句。
  • CallableStatement:用于执行 SQL 存储过程

在这里插入图片描述

二、使用Statement操作数据表的弊端

⭕ 通过调用 Connection 对象的 createStatement() 方法创建该对象。该对象用于执行静态的 SQL语句,并且返回执行结果。

Statement 接口中定义了下列方法用于执行 SQL 语句:

方法作用
int excuteUpdate(String sql)执行更新操作INSERTUPDATEDELETE
ResultSet executeQuery(String sql)执行查询操作SELECT

⭕ 但是使用Statement操作数据表存在弊端:

  • 问题一:存在拼串操作,繁琐

  • 问题二:存在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 就可以了。

  • 代码演示:
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;
	}
}

⭕ 综上:

在这里插入图片描述

三、PreparedStatement的使用

1、PreparedStatement介绍

⭕ 可以通过调用 Connection 对象的 preparedStatement(String sql)方法获取 PreparedStatement 对象

PreparedStatement 接口是 Statement 的子接口,它表示一条预编译过的 SQL 语句

PreparedStatement 对象所代表的 SQL 语句中的参数用问号(?)来表示,调用 PreparedStatement对象的 setXxx() 方法来设置这些参数。
setXxx() 方法有两个参数,第一个参数是要设置的 SQL 语句中的参数的索引(从 1 开始),第二个是设置的 SQL 语句中的参数的值

2、PreparedStatement vs Statement

⭕ 代码的可读性和可维护性。

PreparedStatement 能最大可能提高性能:

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

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

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

  • PreparedStatement 可以防止 SQL 注入

3、Java与SQL对应数据类型转换表

Java类型SQL类型
booleanBIT
byteTINYINT
shortSMALLINT
intINTEGER
longBIGINT
StringCHAR,VARCHAR,LONGVARCHAR
byte arrayBINARY , VAR BINARY
java.sql.DateDATE
java.sql.TimeTIME
java.sql.TimestampTIMESTAMP

4、使用PreparedStatement实现增、删、改操作

4.1 针对customers表实现增加(insert)操作

@Test
	public void testInsert() {
		Connection conn = null;
		PreparedStatement ps = null;
		try {
			// 1.读取配置文件中的4个基本信息
			InputStream is = ClassLoader.getSystemClassLoader().getResourceAsStream("jdbc.properties");

			Properties pros = new Properties();
			pros.load(is);

			String user = pros.getProperty("user");
			String password = pros.getProperty("password");
			String url = pros.getProperty("url");
			String driverClass = pros.getProperty("driverClass");

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

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

//		System.out.println(conn);

			//4.预编译sql语句,返回PreparedStatement的实例
			String sql = "insert into customers(name,email,birth)values(?,?,?)";//?:占位符
			ps = conn.prepareStatement(sql);
			//5.填充占位符
			ps.setString(1, "小老师ir");
			ps.setString(2, "xiaolaoshiir@gmail.com");
			SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
			java.util.Date date = sdf.parse("1000-01-01");
			ps.setDate(3, new Date(date.getTime()));

			//6.执行操作
			ps.execute();
		} catch (Exception e) {
			e.printStackTrace();
		}finally{
			//7.资源的关闭
			try {
				if(ps != null)
					ps.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}
			try {
				if(conn != null)
					conn.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}

		}

	}

4.2 针对customers表实现修改(update)操作

//修改customers表的一条记录(标准)
	@Test
	public void testUpdate(){
		Connection conn = null;
		PreparedStatement ps = null;
		try {
			//1.获取数据库的连接
			conn = JDBCUtils.getConnection();
			//2.预编译sql语句,返回PreparedStatement的实例
			String sql = "update customers set name = ? where id = ?";
			ps = conn.prepareStatement(sql);
			//3.填充占位符
			ps.setObject(1,"莫扎特");
			ps.setObject(2, 18);
			//4.执行
			ps.execute();
		} catch (Exception e) {
			e.printStackTrace();
		}finally{
			//5.资源的关闭
			JDBCUtils.closeResource(conn, ps);

		}
	}

4.3 针对通用表的通用增删改的操作

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

注意:当数据库中的表名为关键字字符时,需用着重号``括起来,如:

在这里插入图片描述

4.4 使用PreparedStatement实现查询操作

(1)ResultSet

⭕ 查询需要调用PreparedStatementexecuteQuery() 方法,查询结果是一个ResultSet 对象

ResultSet 对象以逻辑表格的形式封装了执行数据库操作的结果集,ResultSet 接口由数据库厂商提供实现

ResultSet 返回的实际上就是一张数据表。有一个指针指向数据表的第一条记录的前面。

ResultSet 对象维护了一个指向当前数据行的游标,初始的时候,游标在第一行之前,可以通过 ResultSet 对象的 next() 方法移动到下一行。调用 next()方法检测下一行是否有效。若有效,该方法返回true,且指针下移。相当于Iterator对象的 hasNext()next() 方法的结合体。

⭕ 当指针指向一行时, 可以通过调用 getXxx(int index)getXxx(int columnName) 获取每一列的值。

  • 例如: getInt(1), getString("name")

  • 注意:Java与数据库交互涉及到的相关Java API中的索引都从1开始。

⭕ ResultSet 接口的常用方法:

  • boolean next()
  • getString()

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jQnvwS3y-1649036189281)(尚硅谷_宋红康_JDBC.assets/1555580152530.png)]

(2)ResultSetMetaData

⭕ 可用于获取关于 ResultSet 对象中列的类型和属性信息的对象

ResultSetMetaData meta = rs.getMetaData();

ResultSetMetaData类中的方法

方法描述
getColumnName(int column)获取指定列的名称
getColumnLabel(int column)获取指定列的别名
getColumnCount()返回当前 ResultSet 对象中的列数
getColumnTypeName(int column)检索指定列的数据库特定的类型名称
getColumnDisplaySize(int column)指示指定列的最大标准宽度,以字符为单位
isNullable(int column)指示指定列中的值是否可以为 null
isAutoIncrement(int column)指示是否自动为指定列进行编号,这样这些列仍然是只读的

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SaSiC053-1649036189282)(尚硅谷_宋红康_JDBC.assets/1555579494691.png)]

问题1:得到结果集后, 如何知道该结果集中有哪些列 ? 列名是什么?

​ 需要使用一个描述 ResultSet 的对象, 即 ResultSetMetaData

问题2:关于ResultSetMetaData

如何获取 ResultSetMetaData: 调用 ResultSetgetMetaData() 方法即可
获取ResultSet 中有多少列:调用 ResultSetMetaDatagetColumnCount() 方法
获取ResultSet 每一列的列的别名是什么:调用 ResultSetMetaDatagetColumnLabel() 方法

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KWNRniOf-1649036189282)(尚硅谷_宋红康_JDBC.assets/1555579816884.png)]

(3)代码演示

  • version 1.0:针对于customer表的单一查询操作
	@Test
	public void testQuery1() {
		Connection conn = null;
		PreparedStatement ps = null;
		ResultSet resultSet = null;
		try {
			conn = JDBCUtils.getConnection();
			String sql = "select id,name,email,birth from customers where id = ?";
			ps = conn.prepareStatement(sql);
			ps.setObject(1, 1);
			
			//执行,并返回结果集
			resultSet = ps.executeQuery();
			//处理结果集
			if(resultSet.next()){//next():判断结果集的下一条是否有数据,如果有数据返回true,并指针下移;如果返回false,指针不会下移。
				
				//获取当前这条数据的各个字段值
				int id = resultSet.getInt(1);
				String name = resultSet.getString(2);
				String email = resultSet.getString(3);
				Date birth = resultSet.getDate(4);
				
			//方式一:
//			System.out.println("id = " + id + ",name = " + name + ",email = " + email + ",birth = " + birth);
				
			//方式二:
//			Object[] data = new Object[]{id,name,email,birth};
            //方式三:将数据封装为一个对象(推荐)
				Customer customer = new Customer(id, name, email, birth);
				System.out.println(customer);
				
			}
		} catch (Exception e) {
			e.printStackTrace();
		}finally{
			//关闭资源
			JDBCUtils.closeResource(conn, ps, resultSet);
			
		}
		
	}

  • version 2.0:针对于customer表的通用查询操作
@Test
	public void testQueryForCustomers(){
		String sql = "select id,name,birth,email from customers where id = ?";
		Customer customer = queryForCustomers(sql, 13);
		System.out.println(customer);
		
		sql = "select name,email from customers where name = ?";
		Customer customer1 = queryForCustomers(sql,"周杰伦");
		System.out.println(customer1);
	}
public Customer queryForCustomers(String sql,Object...args){
		Connection conn = null;
		PreparedStatement ps = null;
		ResultSet rs = null;
		try {
			conn = JDBCUtils.getConnection();
			
			ps = conn.prepareStatement(sql);
			for(int i = 0;i < args.length;i++){
				ps.setObject(i + 1, args[i]);
			}
			
			rs = ps.executeQuery();
			//获取结果集的元数据 :ResultSetMetaData
			ResultSetMetaData rsmd = rs.getMetaData();
			//通过ResultSetMetaData获取结果集中的列数
			int columnCount = rsmd.getColumnCount();
			
			if(rs.next()){
				Customer cust = new Customer();
				//处理结果集一行数据中的每一个列
				for(int i = 0;i <columnCount;i++){
					//获取列值
					Object columValue = rs.getObject(i + 1);
					
					//获取每个列的列名
//					String columnName = rsmd.getColumnName(i + 1);
					String columnLabel = rsmd.getColumnLabel(i + 1);
					
					//给cust对象指定的columnName属性,赋值为columValue:通过反射
					Field field = Customer.class.getDeclaredField(columnLabel);
					field.setAccessible(true);
					field.set(cust, columValue);
				}
				return cust;
			}
		} catch (Exception e) {
			e.printStackTrace();
		}finally{
			JDBCUtils.closeResource(conn, ps, rs);
			
		}
		
		return null;
		
		
	}
	
  • version 3.0:针对于order表的通用查询操作(使用查询别名)
/*
	 * 针对于表的字段名与类的属性名不相同的情况:
	 * 1. 必须声明sql时,使用类的属性名来命名字段的别名
	 * 2. 使用ResultSetMetaData时,需要使用getColumnLabel()来替换getColumnName(),
	 *    获取列的别名。
	 *  说明:如果sql中没有给字段其别名,getColumnLabel()获取的就是列名
	 * 
	 * 
	 */
	@Test
	public void testOrderForQuery(){
		String sql = "select order_id orderId,order_name orderName,order_date orderDate from `order` where order_id = ?";
		Order order = orderForQuery(sql,1);
		System.out.println(order);
	}
public Order orderForQuery(String sql,Object...args){

		Connection conn = null;
		PreparedStatement ps = null;
		ResultSet rs = null;
		try {
			conn = JDBCUtils.getConnection();
			ps = conn.prepareStatement(sql);
			for(int i = 0;i < args.length;i++){
				ps.setObject(i + 1, args[i]);
			}

			//执行,获取结果集
			rs = ps.executeQuery();
			//获取结果集的元数据
			ResultSetMetaData rsmd = rs.getMetaData();
			//获取列数
			int columnCount = rsmd.getColumnCount();
			if(rs.next()){
				Order order = new Order();
				for(int i = 0;i < columnCount;i++){
					//获取每个列的列值:通过ResultSet
					Object columnValue = rs.getObject(i + 1);
					//通过ResultSetMetaData
					//获取列的列名:getColumnName() --不推荐使用
					//获取列的别名:getColumnLabel()
//					String columnName = rsmd.getColumnName(i + 1);
					String columnLabel = rsmd.getColumnLabel(i + 1);

					//通过反射,将对象指定名columnName的属性赋值为指定的值columnValue
					Field field = Order.class.getDeclaredField(columnLabel);
					field.setAccessible(true);
					field.set(order, columnValue);
				}

				return order;
			}
		} catch (Exception e) {
			e.printStackTrace();
		}finally{

			JDBCUtils.closeResource(conn, ps, rs);
		}


		return null;

	}


  • version 4.0:针对于通用表的通用查询操作(使用查询别名)
@Test
	public void testGetInstance(){
		String sql = "select id,name,email from customers where id = ?";
		Customer customer = getInstance(Customer.class,sql,12);
		System.out.println(customer);
		
		String sql1 = "select order_id orderId,order_name orderName from `order` where order_id = ?";
		Order order = getInstance(Order.class, sql1, 1);
		System.out.println(order);
	}
public <T> T getInstance(Class<T> clazz,String sql, Object... args) {
		Connection conn = null;
		PreparedStatement ps = null;
		ResultSet rs = null;
		try {
			conn = JDBCUtils.getConnection();

			ps = conn.prepareStatement(sql);
			for (int i = 0; i < args.length; i++) {
				ps.setObject(i + 1, args[i]);
			}

			rs = ps.executeQuery();
			// 获取结果集的元数据 :ResultSetMetaData
			ResultSetMetaData rsmd = rs.getMetaData();
			// 通过ResultSetMetaData获取结果集中的列数
			int columnCount = rsmd.getColumnCount();

			if (rs.next()) {
				T t = clazz.newInstance();
				// 处理结果集一行数据中的每一个列
				for (int i = 0; i < columnCount; i++) {
					// 获取列值
					Object columValue = rs.getObject(i + 1);

					// 获取每个列的列名
					// String columnName = rsmd.getColumnName(i + 1);
					String columnLabel = rsmd.getColumnLabel(i + 1);

					// 给t对象指定的columnName属性,赋值为columValue:通过反射
					Field field = clazz.getDeclaredField(columnLabel);
					field.setAccessible(true);
					field.set(t, columValue);
				}
				return t;
			}
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			JDBCUtils.closeResource(conn, ps, rs);

		}

		return null;
	}


  • version 5.0:针对于通用表的通用查询操作(使用查询别名)
@Test
	public void testGetForList(){
		
		String sql = "select id,name,email from customers where id < ?";
		List<Customer> list = getForList(Customer.class,sql,12);
		list.forEach(System.out::println);
		
		String sql1 = "select order_id orderId,order_name orderName from `order`";
		List<Order> orderList = getForList(Order.class, sql1);
		orderList.forEach(System.out::println);
	}
public <T> List<T> getForList(Class<T> clazz,String sql, Object... args){
		Connection conn = null;
		PreparedStatement ps = null;
		ResultSet rs = null;
		try {
			conn = JDBCUtils.getConnection();

			ps = conn.prepareStatement(sql);
			for (int i = 0; i < args.length; i++) {
				ps.setObject(i + 1, args[i]);
			}

			rs = ps.executeQuery();
			// 获取结果集的元数据 :ResultSetMetaData
			ResultSetMetaData rsmd = rs.getMetaData();
			// 通过ResultSetMetaData获取结果集中的列数
			int columnCount = rsmd.getColumnCount();
			//创建集合对象
			ArrayList<T> list = new ArrayList<T>();
			while (rs.next()) {
				T t = clazz.newInstance();
				// 处理结果集一行数据中的每一个列:给t对象指定的属性赋值
				for (int i = 0; i < columnCount; i++) {
					// 获取列值
					Object columValue = rs.getObject(i + 1);

					// 获取每个列的列名
					// String columnName = rsmd.getColumnName(i + 1);
					String columnLabel = rsmd.getColumnLabel(i + 1);

					// 给t对象指定的columnName属性,赋值为columValue:通过反射
					Field field = clazz.getDeclaredField(columnLabel);
					field.setAccessible(true);
					field.set(t, columValue);
				}
				list.add(t);
			}
			
			return list;
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			JDBCUtils.closeResource(conn, ps, rs);

		}

		return null;
	}
	

说明:使用PreparedStatement实现的查询操作可以替换Statement实现的查询操作,解决Statement拼串和SQL注入问题。

四、资源的释放

⭕ 释放ResultSet, Statement,Connection

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

⭕ 可以在finally中关闭,保证及时其他代码出现异常,资源也一定能被关闭。

五、JDBC API小结

⭕ 两种思想

  • 面向接口编程的思想

  • ORM思想(object relational mapping)

    • 一个数据表对应一个java
    • 表中的一条记录对应java类的一个对象
    • 表中的一个字段对应java类的一个属性

sql是需要结合列名和表的属性名来写。注意起别名。

⭕ 两种技术

  • JDBC结果集的元数据:ResultSetMetaData
    • 获取列数:getColumnCount()
    • 获取列的别名:getColumnLabel()
  • 通过反射,创建指定类的对象,获取指定的属性并赋值

练习题1

从控制台向数据库的表customers中插入一条数据,表结构如下:

在这里插入图片描述
答案:

public class Exer1Test {
	
	@Test
	public void testInsert(){
		Scanner scanner = new Scanner(System.in);
		System.out.print("请输入用户名:");
		String name = scanner.next();
		System.out.print("请输入邮箱:");
		String email = scanner.next();
		System.out.print("请输入生日:");
		String birthday = scanner.next();//'1992-09-08'
		
		String sql = "insert into customers(name,email,birth)values(?,?,?)";
		int insertCount = update(sql,name,email,birthday);
		if(insertCount > 0){
			System.out.println("添加成功");
			
		}else{
			System.out.println("添加失败");
		}
		
	}
	
	
	// 通用的增删改操作
	public int update(String sql, Object... args) {// sql中占位符的个数与可变形参的长度相同!
		Connection conn = null;
		PreparedStatement ps = 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.执行
			/*
			 * ps.execute():
			 * 如果执行的是查询操作,有返回结果,则此方法返回true;
			 * 如果执行的是增、删、改操作,没有返回结果,则此方法返回false.
			 */
			//方式一:
//			return ps.execute();
			//方式二:
			return ps.executeUpdate();
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			// 5.资源的关闭
			JDBCUtils.closeResource(conn, ps);

		}
		return 0;
	}

}

练习题2

创立数据库表 examstudent,表结构如下:
在这里插入图片描述
向数据表中添加如下数据:
在这里插入图片描述
代码实现1:插入一个新的student 信息

请输入考生的详细信息

Type: IDCard: ExamCard: StudentName: Location: Grade:

信息录入成功!
答案:

@Test
	public void testInsert(){
		Scanner scanner = new Scanner(System.in);
		System.out.print("四级/六级:");
		int type = scanner.nextInt();
		System.out.print("身份证号:");
		String IDCard = scanner.next();
		System.out.print("准考证号:");
		String examCard = scanner.next();
		System.out.print("学生姓名:");
		String studentName = scanner.next();
		System.out.print("所在城市:");
		String location = scanner.next();
		System.out.print("考试成绩:");
		int grade = scanner.nextInt();
		
		String sql = "insert into examstudent(type,IDCard,examCard,studentName,location,grade)values(?,?,?,?,?,?)";
		int insertCount = update(sql,type,IDCard,examCard,studentName,location,grade);
		if(insertCount > 0){
			System.out.println("添加成功");
		}else{
			System.out.println("添加失败");
		}
		
		
		
	}
	// 通用的增删改操作
	public int update(String sql, Object... args) {// sql中占位符的个数与可变形参的长度相同!
		Connection conn = null;
		PreparedStatement ps = 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.执行
			/*
			 * ps.execute(): 
			 * 如果执行的是查询操作,有返回结果,则此方法返回true;
			 * 如果执行的是增、删、改操作,没有返回结果,则此方法返回false.
			 */
			// 方式一:
			// return ps.execute();
			// 方式二:
			return ps.executeUpdate();
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			// 5.资源的关闭
			JDBCUtils.closeResource(conn, ps);

		}
		return 0;
	}
	

代码实现2:在 eclipse中建立 java 程序:输入身份证号或准考证号可以查询到学生的基本信息。结果如下:
在这里插入图片描述

@Test
	public void queryWithIDCardOrExamCard(){
		System.out.println("请选择您要输入的类型:");
		System.out.println("a.准考证号");
		System.out.println("b.身份证号");
		Scanner scanner = new Scanner(System.in);
		String selection = scanner.next();
		if("a".equalsIgnoreCase(selection)){//if(selection.equalsIgnoreCase("a")){
			System.out.println("请输入准考证号:");
			String examCard = scanner.next();
			String sql = "select FlowID flowID,Type type,IDCard,ExamCard examCard,StudentName name,Location location,Grade grade from examstudent where examCard = ?";
			
			Student student = getInstance(Student.class,sql,examCard);
			if(student != null){
				System.out.println(student);
				
			}else{
				System.out.println("输入的准考证号有误!");
			}
			
		}else if("b".equalsIgnoreCase(selection)){
			System.out.println("请输入身份证号:");
			String IDCard = scanner.next();
			String sql = "select FlowID flowID,Type type,IDCard,ExamCard examCard,StudentName name,Location location,Grade grade from examstudent where IDCard = ?";
			
			Student student = getInstance(Student.class,sql,IDCard);
			if(student != null){
				System.out.println(student);
				
			}else{
				System.out.println("输入的身份证号有误!");
			}
		}else{
			System.out.println("您的输入有误,请重新进入程序。");
		}
		
	}
	
	public <T> T getInstance(Class<T> clazz,String sql, Object... args) {
		Connection conn = null;
		PreparedStatement ps = null;
		ResultSet rs = null;
		try {
			conn = JDBCUtils.getConnection();

			ps = conn.prepareStatement(sql);
			for (int i = 0; i < args.length; i++) {
				ps.setObject(i + 1, args[i]);
			}

			rs = ps.executeQuery();
			// 获取结果集的元数据 :ResultSetMetaData
			ResultSetMetaData rsmd = rs.getMetaData();
			// 通过ResultSetMetaData获取结果集中的列数
			int columnCount = rsmd.getColumnCount();

			if (rs.next()) {
				T t = clazz.newInstance();
				// 处理结果集一行数据中的每一个列
				for (int i = 0; i < columnCount; i++) {
					// 获取列值
					Object columValue = rs.getObject(i + 1);

					// 获取每个列的列名
					// String columnName = rsmd.getColumnName(i + 1);
					String columnLabel = rsmd.getColumnLabel(i + 1);

					// 给t对象指定的columnName属性,赋值为columValue:通过反射
					Field field = clazz.getDeclaredField(columnLabel);
					field.setAccessible(true);
					field.set(t, columValue);
				}
				return t;
			}
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			JDBCUtils.closeResource(conn, ps, rs);

		}

		return null;
	}
	

代码实现3:完成学生信息的删除功能
在这里插入图片描述

@Test
	public void testDeleteByExamCard(){
		System.out.println("请输入学生的考号:");
		Scanner scanner = new Scanner(System.in);
		String examCard = scanner.next();
		//查询指定准考证号的学生
		String sql = "select FlowID flowID,Type type,IDCard,ExamCard examCard,StudentName name,Location location,Grade grade from examstudent where examCard = ?";
		Student student = getInstance(Student.class,sql,examCard);
		if(student == null){
			System.out.println("查无此人,请重新输入");
		}else{
			String sql1 = "delete from examstudent where examCard = ?";
			int deleteCount = update(sql1, examCard);
			if(deleteCount > 0){
				System.out.println("删除成功");
			}
		}
	}
	//优化以后的操作:
	@Test
	public void testDeleteByExamCard1(){
		System.out.println("请输入学生的考号:");
		Scanner scanner = new Scanner(System.in);
		String examCard = scanner.next();
		String sql = "delete from examstudent where examCard = ?";
		int deleteCount = update(sql, examCard);
		if(deleteCount > 0){
			System.out.println("删除成功");
		}else{
			System.out.println("查无此人,请重新输入");
		}
		
	}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/474946.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

连接分析工具箱 | 利用CATO进行结构和功能连接重建

导读 本研究描述了一个连接分析工具箱(CATO)&#xff0c;用于基于扩散加权成像(DWI)和静息态功能磁共振成像(rs-fMRI)数据来重建大脑结构和功能连接。CATO是一个多模态软件包&#xff0c;使研究人员能够运行从MRI数据到结构和功能连接组图的端到端重建&#xff0c;定制其分析并…

牛郎织女

我写的十二星座十二人大多是奇女子&#xff0c;如双子的刘若英《若》、天秤的叶倩文《AB天秤座&#xff0c;Sally》、射手的桂纶镁《半人马座&#xff0c;桂纶镁》、水瓶的杨千嬅《可惜我是水瓶座》、双鱼的安妮伊能静《十二星座十二人之&#xff1a;双鱼&#xff0c;伊能&…

使用cube studio开发机器学习建模的pipeline

&#xff08;作者&#xff1a;陈玓玏&#xff09; Cube Studio目前包含了传统机器学习模板&#xff0c;400AI模型&#xff0c;欢迎私信了解哇&#xff01; 在使用cube studio进行模型训练或推理的过程中&#xff0c;我们有时会发现没有符合自己要求的模板&#xff0c;此时我们…

Unity 后处理(Post-Processing) -- (1)概览

在Unity中&#xff0c;后处理&#xff08;Post-Processing&#xff09;是在相机所捕捉的图像上应用一些特殊效果的过程&#xff0c;后处理会让图像视觉效果更好&#xff08;前提是做的好&#xff09;。 这些效果的范围有非常细微的颜色调整&#xff0c;也包括整体的美术风格的大…

graalvm spring 打包成exe

graalvm jdk下载https://www.graalvm.org/downloads/ 把graalvm加入环境变量和就是JAVA_HOME 安装native-image gu.cmd install native-image 问题: Error: Default native-compiler executable cl.exe not found via environment variable PATH Error: To prevent native-tool…

Redis基础——Redis常用命令

Redis基础 1.1 Redis通用命令 通用指令是部分数据类型的&#xff0c;都可以使用的指令&#xff0c;常见的有&#xff1a; KEYS&#xff1a;查看符合模板的所有keyDEL&#xff1a;删除一个指定的keyEXISTS&#xff1a;判断key是否存在EXPIRE&#xff1a;给一个key设置有效期&…

C++动态规划模板汇总大全

前言 如果你不太了解dp&#xff08;动态规划&#xff09;是个什么东西&#xff0c;请回到上次dp。 链接&#xff1a;动态规划算法详解 数字三角形模型 问题 A: 【一本通基础DP基础模型】【例9.2】数字金字塔 【题目描述】 观察下面的数字金字塔。写一个程序查找从最高点到…

【计算机图形学】三维图形投影和消隐(三视图构造)

模块4-1 三维图形投影和消隐 一 实验目的 编写三维图形各种变换的投影算法 二 实验内容 1&#xff1a;自行选择三维物体&#xff08;不能选长方体&#xff09;&#xff0c;建立坐标系&#xff0c;给定点的三维坐标值&#xff0c;建立边表结构。完成三视图。 实验结果如下图所…

如何解决服务器认证失败

服务器认证失败是指在连接服务器时&#xff0c;由于身份认证失败而无法访问服务器。其实这是一种非常常见的问题&#xff0c;这种问题的原因很多&#xff0c;多方面导致的&#xff0c;但是我们又该如何解决这种问题呢&#xff1f;接下来就让小编为大家介绍服务器认证失败的原因…

41.Java单列集合LinkedList

单列集合LinkedList 1.LinkedList集合2.源码3. ArrayList和LinkedList的区别 1.LinkedList集合 在许多情况下&#xff0c;ArrayList效率更高&#xff0c;因为通常需要访问列表中的某一个元素&#xff0c;但是LinkedList提供了几种方法来更有效地执行某些操作。 2.源码 3. Arr…

【Java笔试强训 4】

&#x1f389;&#x1f389;&#x1f389;点进来你就是我的人了博主主页&#xff1a;&#x1f648;&#x1f648;&#x1f648;戳一戳,欢迎大佬指点! 欢迎志同道合的朋友一起加油喔&#x1f93a;&#x1f93a;&#x1f93a; 目录 一、选择题 二、编程题 &#x1f525;计算糖…

Dubbo 的引入(1)

目录 认识RPC Dubbo 认识RPC RPC是解决不同JVM之间数据调用的一个思想&#xff0c;比如说现在有2台不同的机器&#xff0c;业务代码需要在这2台机器间调用后台业务代码&#xff0c;RPC就可以解决这2台机器业务代码调用的问题&#xff1a; 而RPC实现流程是什么样的呢&#xff…

无脑005----在mmyolo框架下复现RTMDet-R

背景 win11系统wsl虚拟机Ubuntu下GTX1060显卡 cuda 10.1 pytorch 1.7.1 python 3.8 耗时 两个小时 参考github链接&#xff1a;https://github.com/open-mmlab/mmyolo 1.安装环境 首先安装pytorch conda install pytorch1.7.1 torchvision0.8.2 torchaudio0.7.2 cudatoolkit…

最新发布!面向开发者的 ChatGPT Prompt Engineering 免费教程,吴恩达与OpenAI合作打造!...

就在几小时之前&#xff0c;吴恩达在Twitter上宣布了与OpenAI合作的最新免费教程&#xff1a;《ChatGPT Prompt Engineering for Developers》 我们可以通过下面的链接&#xff0c;直达官网网站&#xff1a; https://www.deeplearning.ai/short-courses/chatgpt-prompt-enginee…

当下常见的企业文件管理工具都有哪些?

随着数字化的发展&#xff0c;企业在日常办公中会产出大量文件&#xff0c;高效管理办公文件十分重要。当下常见文件管理工具都有哪些呢&#xff1f; 1.Zoho WorkDrive 高性价比&#xff0c;多设备支持&#xff0c;安全性能高。Zoho Workdive 是一个团队协作多人内容编辑平台&…

“递归三要素”寻踪(浅析递归“经典”框架,领略递归优雅秀气;看到有“递归算法优化”的操作,余试剖之)

浅析递归“经典”框架&#xff0c;领略递归优雅秀气。看到有“递归算法优化”的操作&#xff0c;余试剖之。 【学习的细节是欢悦的历程】 Python 官网&#xff1a;https://www.python.org/ Free&#xff1a;大咖免费“圣经”教程《 python 完全自学教程》&#xff0c;不仅仅是…

mulesoft MCIA 破釜沉舟备考 2023.04.29.27 (易错题)

mulesoft MCIA 破釜沉舟备考 2023.04.29.27 (易错题) 1. An insurance company has an existing API which is currently used by customers.2. An organization has deployed both Mule and non-Mule API implementations to integrate its customer and order management s…

《2023中国各地区科创之星势力图1.0版》重磅发布

数据猿出品 本次“数据猿2023年度三大媒体策划活动——《2023中国各地区科创之星势力图1.版》”的发布&#xff0c;是数据猿在2022年3.0版本的基础上&#xff0c;迭代升级的2023开年的第一个版本。本年度下一次版本迭代将于2023年8月底发布2023年2.0版&#xff0c;敬请期待&…

51 - 自写操作系统

简易OS 1> 版本1&#xff1a;任务建立与切换2> 版本2&#xff1a;定时器切换2.1> main.c2.2> task.c2.3> sleep.c 3> 版本3&#xff1a;加时间片轮转 &#x1f517; //--------- 参考视频链接 ------// 通过实验&#xff0c; 学习理解操作系统的大概&#xf…

【MySQL入门指南】外键约束使用详解

一、为什么需要外键&#xff1f; MySQL是一种关系型数据库&#xff0c;现实中的业务往往是相互关联的&#xff0c;这也就决定了数据库中的表也是存在相互关系的。而表与表之间的相互关系就是通过外键来维护的。给大家举一个现实的例子来帮助理解&#xff1a; 现在我们需要在数据…