工作原理图:
PreparedStatement是Statement的一个子接口
条件:
使用的驱动--->使用xml或者其他配置文件进行管理
URL--->连接的ip和端口号和数据库
用户名
密码
封装获取连接的过程:
getConnection()
/*获取数据库连接*/ /** * 获取数据库连接 * @return */ public static Connection getConnection() throws SQLException { /*从配置文件当中去读数据库连接所需要的数据--->通过获取系统类加载器来读取配置文件*/ InputStream is = ClassLoader.getSystemClassLoader().getResourceAsStream("Localhost.properties"); //创建Properties引用 Properties prop = new Properties(); try { //读取流文件 prop.load(is); }catch (IOException e){ System.out.println("读取流文件的时候抛出的异常!"); e.printStackTrace(); } //获取文件当中的属性 String driverClass = prop.getProperty("DRIVER"); String url = prop.getProperty("URL"); String username = prop.getProperty("USERNAME"); String password = prop.getProperty("PASSWORD"); try { //反射获取mysql驱动 Class.forName(driverClass); }catch (ClassNotFoundException e){ System.out.println("获取数据库驱动抛出的异常!"); e.printStackTrace(); } /*获取Connection对象引用*/ Connection conn = DriverManager.getConnection(url, username, password); return conn; }
closeResource()
/*关闭资源类*/ public static void closeResource(Connection conn, PreparedStatement ps){ /*后打开的先关闭*/ try { if (ps!=null){ ps.close(); } }catch (SQLException e){ System.out.println("关闭ps抛出的异常!"); e.printStackTrace(); } try { if (conn!=null){ conn.close(); } }catch (SQLException e){ System.out.println("关闭连接抛出的异常!"); e.printStackTrace(); } }
将不确定的信息参数化:
使用可变形参将需要参数化的地方参数化:
/*统一的修改方法*/ //由于通配符不知道有多少所以设置成可变形参 public void upload(String sql, Object ...args){ //定义属性 Connection conn = null; PreparedStatement ps = null; //获取数据库连接 try { conn = JDBCUtils.getConnection(); //创建PreparedStatement对象携带sql去进行操作 ps = conn.prepareStatement(sql); //填充占位符 /* sql当中占位符的个数应该与可变形参的长度一致 可变形参当成数组 */ for (int i = 1; i < args.length; i++){ ps.setObject(i, args[i]); //当心参数声明错误 } //执行语句 ps.execute(); }catch (Exception e){ e.printStackTrace(); } //关闭资源 JDBCUtils.closeResource(conn, ps); }
测试:
@Test public void testCommonUpdate(){ // String sql = "delete from customers where `id` = 1;"; // upload(sql, 3); String sql = "update `users` set `name` = ? where `id` = ?;"; upload(sql, "Jun", 2); }
Java
与SQL
对应数据类型转换表:
Java类型 | SQL类型 |
---|---|
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 |
针对不同的表进行查询操作:
@Test public void testQueryNo1 () throws SQLException { Connection conn = null; PreparedStatement ps = null; ResultSet rs = null; try { //获取连接,生成连接对象 conn = JDBCUtils.getConnection(); //sql String sql = "select `id`,`name`,`email`,`photo` from customers where `id` = ?;"; //预编译 ps = conn.prepareStatement(sql); ps.setObject(1, 1); //执行sql语句并返回结果集 rs = ps.executeQuery(); //处理结果集--->类似迭代器--->调用has/next方法(相当于有一个指针指向了结果集,要判断是否有内容)迭代器中的has和next方法返回一个布尔类型的值,表示下一个位置有没有值 /* 结果集当中有相关的方法获取到具体的字段的值 */ if (rs.next()){ //判断结果集的next方法返回的布尔类型进而判断是否取出值 int id = rs.getInt(1); String name = rs.getString("Jun"); String email = rs.getString("JunkingBoy@163.com"); Date birth = rs.getDate(19990909); //直接显示 System.out.println("id:" + id + "name:" + name + "email:" + email + "birthday:" + birth); //封装到一个数组当中进行输出 Object[] resp = new Object[]{id, name, email, birth}; //封装到一个集合类的对象当中--->相当于专门的定义一个结构体用于输出 /*将数据封装成一个对象*/ Customer cus = new Customer(id, name, email, birth); /*打印--->调用toString方法*/ System.out.println(cus); } }catch (Exception e){ e.printStackTrace(); }finally { //关闭资源--->rs也需要关闭 JDBCUtils.closeResource(conn, ps, rs); } }
ORM
思想:一个Java
类对应一个表格
:
package JDBCStatementCRUD; import java.util.Date; /** * 相当于一个结构体,用于存放某一个表当中的记录 * ORM的编程思想: * 1、一个数据表对应一个Java类 * 2、表中的一条记录对应一个Java类的一个对象 * 3、表中的一个字段对应Java类的一个属性 * @since JDk 1.8 * @date 2021/09/24 * @author Lucifer */ public class Customer { /*属性字段私有化*/ private int id; private String name; private String email; private Date birth; /*生成构造器*/ public Customer() { } public Customer(int id, String name, String email, Date birth) { this.id = id; this.name = name; this.email = email; this.birth = birth; } public int getId() { return id; } public String getName() { return name; } public String getEmail() { return email; } public Date getBirth() { return birth; } public void setId(int id) { this.id = id; } public void setName(String name) { this.name = name; } public void setEmail(String email) { this.email = email; } public void setBirth(Date birth) { this.birth = birth; } }
指定查询的字段进行查询:
@Test public void testQueryNo1 () { Connection conn = null; PreparedStatement ps = null; ResultSet rs = null; try { //获取连接,生成连接对象 conn = JDBCUtils.getConnection(); //sql String sql = "select `id`,`name`,`email`,`photo` from customers where `id` = ?;"; //预编译 ps = conn.prepareStatement(sql); ps.setObject(1, 1); //执行sql语句并返回结果集 rs = ps.executeQuery(); //处理结果集--->类似迭代器--->调用has/next方法(相当于有一个指针指向了结果集,要判断是否有内容)迭代器中的has和next方法返回一个布尔类型的值,表示下一个位置有没有值 /* 结果集当中有相关的方法获取到具体的字段的值 */ if (rs.next()){ //判断结果集的next方法返回的布尔类型进而判断是否取出值 int id = rs.getInt(1); String name = rs.getString("Jun"); String email = rs.getString("JunkingBoy@163.com"); Date birth = rs.getDate(19990909); //直接显示 System.out.println("id:" + id + "name:" + name + "email:" + email + "birthday:" + birth); //封装到一个数组当中进行输出 Object[] resp = new Object[]{id, name, email, birth}; //封装到一个集合类的对象当中--->相当于专门的定义一个结构体用于输出 /*将数据封装成一个对象*/ Customer cus = new Customer(id, name, email, birth); /*打印--->调用toString方法*/ System.out.println(cus); } }catch (Exception e){ e.printStackTrace(); }finally { //关闭资源--->rs也需要关闭 JDBCUtils.closeResource(conn, ps, rs); } }
通用的查询方式:
关键点:
根据传入的sql
语句判断需要查询的字段进行查询
因为ORM
的思想,一个表对应一个JavaBean
对象。所以需要根据传入的sql
要获取传入的列
通过元数据获取列数,循环获取查询的列名,在通过反射运行时类的属性与列名进行比较,相同的赋值--->这不是判断而是同时获取
没有查询到的属性为null
/** * 针对Customers表的通用的查询操作--->查询的字段数量不一样 * 1、获取列数--->结果集元数据 * 2、获取列名--->结果集元数据 * 3、获取需要查询多少个字段--->反射的方式来获取--->动态获取对象当中的属性--->运行时类加载器 */ @Test public Customer queryForCustomers(String sql, Object ...args) { Connection conn = null; PreparedStatement ps = null; ResultSet rs = null; try { //获取连接 conn = JDBCUtils.getConnection(); //预编译sql语句--->因为不知道查询多少个字段所以定义成参数 ps = conn.prepareStatement(sql); //填充占位符 for (int i=0; i<args.length; i++) { ps.setObject(i+1, args[i]); } //执行查询 rs = ps.executeQuery(); //处理结果集--->希望返回一个对象,所以返回表对象 //因为传入的sql查询的字段决定了查询的结果集,所以要想办法拿到结果集当中的列--->在result接口中将列封装在结果集的元数据当中 ResultSetMetaData rsmd = rs.getMetaData(); //--->获取结果集的元数据(修饰结果集的元数据)--->类比元注解,修饰现有数据的一个数据--->通过结果集的元数据获取结果集中的列数 int columnCount = rsmd.getColumnCount(); //--->获取列数 //查询一条数据,用if,多条用while if (rs.next()) { //new对象 Customer cust = new Customer(); //--->查询到结果了造对象,所以写到if里面 //循环获取列--->类似操作excel的方法 //这个是处理一行结果集,处理一行数据中的每一个列 for (int i=0; i<columnCount; i++) { Object value = rs.getObject(i+1); //--->获取到该字段的值了(列值) //--->JDBC当中最困难的一块(拿到数据了以后封装到一个对象当中,按照拿到的属性赋值)--->构造器或者set方法 /* 1、用空参的构造器new一个对象 2、看查询什么,查询的对象就set方法放进去 3、给cust对象指定的某个属性赋值为value--->找到并且判断是哪个属性--->用结果集当中的属性对应到对象当中的属性 获取结果集当中的列名 */ //获取每个列的列名(结果集的元数据去拿)--->动态获取--->通过反射的方法获取 String columnName = rsmd.getColumnName(i+1); //--->列名 //给cust对象当中的columnName的属性赋值为columValue--->通过反射去赋值 /* 1、在Customer类当中找columnName的属性 2、把属性对应对象的成员赋值给成员 调用运行时类的指定属性--->反射 */ Field field = Customer.class.getDeclaredField(columnName); //属性可能是私有的属性 field.setAccessible(true); //--->设置私有的属性能访问 field.set(cust, value); /*上述是最困难也是最重要的点--->将customer对象叫columnName名的属性赋值给列值*/ /*通过反射的方式去动态实现,因为不知道具体要查多少个值*/ } return cust; } }catch (Exception e) { e.printStackTrace(); }finally { //关闭资源 JDBCUtils.closeResource(conn, ps, rs); } return null; }