本次内容总结使用jdbc访问并操作数据库的流程。
一:获取数据库的连接(四个基本要素 )
要素1:具体数据库厂商的Driver接口实现类,加载与注册JDBC驱动
要素2:数据库URL
要素3:用户名
要素4:密码
代码:获取数据库连接的五种方式,依次迭代优化。
1 /** 2 * @author only 3 */ 4 public class testConnection { 5 6 //连接数据库方式一 7 @Test 8 public void test1() throws SQLException { 9 Driver driver = new com.mysql.jdbc.Driver(); 10 String url = "jdbc:mysql://localhost:3306/bjpowernode"; 11 Properties info = new Properties(); 12 info.setProperty("user","root"); 13 info.setProperty("password","111"); 14 15 Connection conn = driver.connect(url,info); 16 System.out.println(conn); 17 } 18 19 //连接数据库方式二:对方式一的迭代:在如下的程序中不出现第三方的API,使得程序具有更好的可移植性 20 @Test 21 public void test2() throws Exception { 22 //1.使用反射获取Driver实现类对象 23 Class clazz = Class.forName("com.mysql.jdbc.Driver"); 24 Driver driver = (Driver) clazz.newInstance(); 25 //2.提供要连接的数据库 26 String url = "jdbc:mysql://localhost:3306/bjpowernode"; 27 //3.将用户名和密码封装在properties中,使用properties对象提供需要的用户名和密码 28 Properties info = new Properties(); 29 info.setProperty("user","root"); 30 info.setProperty("password","111"); 31 //4.获取连接 32 Connection conn = driver.connect(url,info); 33 System.out.println(conn); 34 } 35 36 //连接数据库方式三:使用DriverManager替换Driver 37 @Test 38 public void test3() throws Exception { 39 //1.使用反射获取Driver实现类对象 40 Class clazz = Class.forName("com.mysql.jdbc.Driver"); 41 Driver driver = (Driver) clazz.newInstance(); 42 //2.提供另外三个连接的基本信息: 43 String url = "jdbc:mysql://localhost:3306/bjpowernode"; 44 String user = "root"; 45 String password = "111"; 46 47 //3.注册驱动 48 DriverManager.registerDriver(driver); 49 //4.获取连接(也可以传入properties对象) 50 Connection conn = DriverManager.getConnection(url, user, password); 51 System.out.println(conn); 52 } 53 54 //连接数据库方式四:可以仅加载驱动,而不用显示的注册驱动 55 @Test 56 public void test4() throws Exception { 57 //1.提供另外三个连接的基本信息: 58 String url = "jdbc:mysql://localhost:3306/bjpowernode"; 59 String user = "root"; 60 String password = "111"; 61 62 //2.可以仅使用反射加载驱动类,获取Driver实现类对象。无需再注册是因为,Driver的实现类中进行了注册操作。 63 Class.forName("com.mysql.jdbc.Driver"); 64 //Driver driver = (Driver) clazz.newInstance(); 65 //注册驱动 66 //DriverManager.registerDriver(driver); 67 68 //3.获取连接(也可以传入properties对象) 69 Connection conn = DriverManager.getConnection(url, user, password); 70 System.out.println(conn); 71 } 72 73 74 /**连接数据库方式五(最终版):将数据库连接的4个基本信息(第4个为数据库的驱动类名)声明在配置文件中,通过读取配置文件的方式,获取连接 75 * 这种方式的好处? 76 * 1.实现了数据与代码的分离(实现了数据和代码的解耦合)。防止硬编码(代码写死)。当需要更改时仅改配置文件即可。 77 * 2.如果需要修改配置文件信息,可以避免程序重新打包。 78 * */ 79 @Test 80 public void test5() throws Exception { 81 //1.读取配置文件中的4个基本信息 82 //类的加载器下的主要方法getResourceAsStream()获取类路径下的指定文件的输入流 83 InputStream is = testConnection.class.getClassLoader().getResourceAsStream("jdbc.properties"); 84 Properties pros = new Properties(); 85 pros.load(is); 86 String user = pros.getProperty("user"); 87 String password = pros.getProperty("password"); 88 String url = pros.getProperty("url"); 89 String driverClass = pros.getProperty("driverClass"); 90 91 //2.可以仅使用反射加载驱动类,获取Driver实现类对象。无需再注册是因为,Driver的实现类中进行了注册操作。 92 Class.forName("com.mysql.jdbc.Driver"); 93 //Driver driver = (Driver) clazz.newInstance(); 94 //注册驱动 95 //DriverManager.registerDriver(driver); 96 97 //3.获取连接(也可以传入properties对象) 98 Connection conn = DriverManager.getConnection(url, user, password); 99 System.out.println(conn); 100 } 101 }
获取连接后,就可以通过创建PreparedStatement(预编译sql statement)对象操作数据库。
二:使用PreparedStatement对象实现CRUD操作
(1)操作和访问数据库:
其中PreparedStatement 是 Statement的子接口。但由于Statement有很多弊端(1.需要拼写字符串。 2.存在SQL注入的问题:即指web应用程序对用户输入数据的合法性没有判断或过滤不严,攻击者可以在web应用程序中事先定义好的查询语句的结尾上添加额外的SQL语句,在管理员不知情的情况下实现非法操作,以此来实现欺骗数据库服务器执行非授权的任意查询,从而进一步得到相应的数据信息 。例如输入的用户名或密码不正确时仍然能查到数据 ),因此后续就直接使用PreparedStatement接口执行SQL语句,而不用Statement。CallableStatement接口用于执行SQL存储过程。
对于java而言,只要用PreparedStatement取代Statement就可以解决注入问题了!!
(2)使用PreparedStatement实现通用的 增删改 操作:
1 //使用PreparedStatement实现通用的增删改操作,将重复步骤封装起来 2 @Test 3 public void updateTest() throws Exception { 4 //删除 5 String sql = "delete from customers where id = ?"; 6 update(sql,3); 7 8 //改,由于表名‘order’与 sql语句中的关键字order重复,需要加着重符‘‘; 9 String sql = "update ‘order‘ set name= ?,where id = ?"; 10 update(sql,"牛顿",5); //由于sql语句中有两个占位符,所以第二个可变形参args需要传两个参数 11 12 } 13 14 //通用的增删改操作,用到可变形参。(可以传任意多个形参) 15 public void update(String sql,Object ...args){ //sql中的占位符个数要与args长度一致,args是一个数组类型 16 Connection conn = null; 17 PreparedStatement ps = null; 18 try { 19 //1.获取数据库连接 20 conn = getConnection(); 21 //2.预编译sql语句,返回PreparedStatement实例 22 ps = conn.prepareStatement(sql); 23 //3.填充占位符 24 for (int i=0; i<args.length; i++){ 25 ps.setObject(i+1,args[i]); //注意setObject从 1 开始填充 26 } 27 //4.执行 28 ps.execute(); 29 } catch (Exception e) { 30 e.printStackTrace(); 31 } 32 //5.资源的关闭 33 closeResource(conn,ps); 34 } 35 36 //获取数据库连接 37 public Connection getConnection() throws Exception{ 38 //1.读取配置文件中的4个基本信息 39 InputStream is = ClassLoader.getSystemClassLoader().getResourceAsStream("jdbc.properties"); 40 Properties pros = new Properties(); 41 pros.load(is); 42 String user = pros.getProperty("user"); 43 String password = pros.getProperty("password"); 44 String url = pros.getProperty("url"); 45 String driverClass = pros.getProperty("driverClass"); 46 47 //2.可以仅使用反射加载驱动类,获取Driver实现类对象。无需再注册是因为,Driver的实现类中进行了注册操作。 48 Class.forName("com.mysql.jdbc.Driver"); 49 50 //3.获取连接(也可以传入properties对象) 51 Connection conn = DriverManager.getConnection(url, user, password); 52 return conn; 53 } 54 55 //关闭连接和Statement操作 56 public void closeResource(Connection conn, PreparedStatement ps){ 57 try { 58 if(conn!=null) 59 conn.close(); 60 }catch (Exception e){ 61 e.printStackTrace(); 62 } 63 try { 64 if(ps!=null) 65 ps.close(); 66 }catch (Exception e){ 67 e.printStackTrace(); 68 } 69 }
(3)使用PreparedStatement实现通用的 查 操作:(查操作需要返回一个结果集)
Java与SQL对应数据类型转换表
查询操作的流程示意图 (理解ORM编程思想!以及表字段名和类属性名不同时的处理!)
使用PreparedStatement执行查询操作:
1 //测试查询操作 2 @Test 3 public void queryTest(){ 4 String sql = "select id cid, name cname, email cemail from customer where id < ?"; 5 List<Customer> customer = query(Customer.class, sql, 10); 6 System.out.println(customer); 7 } 8 9 //实现通用的查询操作,返回查找到数据的对象 10 @Test 11 public <T> List<T> query(Class<T> clazz, String sql, Object ...args){ 12 Connection conn = null; 13 PreparedStatement ps = null; 14 ResultSet rs = null; 15 try { 16 //1.获取数据库连接 17 conn = getConnection(); 18 //2.预编译sql语句,返回PreparedStatement实例 19 ps = conn.prepareStatement(sql); 20 //3.填充占位符 21 for (int i=0; i<args.length; i++){ 22 ps.setObject(i+1,args[i]); //注意setObject从 1 开始填充 23 } 24 //4.执行查询(注意这里不是execute了)并得到结果 25 rs = ps.executeQuery(); 26 //5.获取结果的元数据,用于得到字段名,和列数 27 ResultSetMetaData rsmd = rs.getMetaData(); 28 int colnum = rsmd.getColumnCount(); 29 List<T> ret = new ArrayList<>(); 30 while(rs.next()){ 31 T t = clazz.newInstance(); 32 //处理结果集一行数据中的每一列 33 for(int i=0; i<colnum; i++){ 34 //获取列值 35 Object colvalue = rs.getObject(i+1); 36 //获取列名(有别名则获取到别名) 37 String collabel = rsmd.getColumnLabel(i+1); 38 //通过反射,给t对象的coluname属性赋值为colvalue 39 Field field = clazz.getDeclaredField(collabel); 40 field.setAccessible(true); 41 field.set(t,colvalue); 42 } 43 ret.add(t); 44 } 45 return ret; 46 } catch (Exception e) { 47 e.printStackTrace(); 48 } finally { 49 //5.资源的关闭 50 closeResource(conn, ps, rs); 51 } 52 return null; 53 }
参考 :https://www.bilibili.com/video/BV1eJ411c7rf?