import java.sql.CallableStatement; import java.sql.Connection; import java.sql.Driver; import java.sql.DriverManager; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import java.sql.Types; import java.util.Properties; import java.util.UUID; /** * * jdbc复习: * SQL语言共分为四大类: * dcl控制 ddl定义 dql查询 dml执行(insert update delete) * * JDBC API是java app用来连接数据库的API接口,可以用来连接、操作语句、接收操作结果. * JDBC客户端分为:thin瘦客户端;OCI Oracle call interface 需要安装Oracle客户端,调用客户端dll访问数据库 * Oracle 一般装了数据库就有jar包 在jdbc/lib下面 * Mysql Connector/J https://dev.mysql.com/downloads/connector/j/ * 这个5.7 和 8版本不一样 jar包还不一样. * JDBC调用流程: * java app --> jdbc api --> driveManager --> driver --> database * * 打开一个数据库连接的方式: * * 第一种: * Connection con = DriverManager.getConnection("jdbc:oracle:thin:@192.168.1.66:1521/orclwq","smwb_framework","salis"); 第二种、单 URL getConnection模式: Connection con = DriverManager.getConnection("jdbc:oracle:thin:smwb_framework/salis@192.168.1.66:1521/orclwq"); 第三种: Properties info = new Properties(); info.put("user", "smwb_framework"); info.put("password", "salis"); Connection con = DriverManager.getConnection("jdbc:oracle:thin:@192.168.1.66:1521/orclwq", info); * * 操作数据库三个接口: * CallAbleStatement extends PreparedStatement extends Statement * * 编写过程: * 1、注册驱动 * 2、加载驱动类 * Class.forName("") 类里有静态代码可以直接 registerDriver() * 而且可以RTTI,运行时动态可配 * 3、创建连接 * 4、执行 * 5、获取结果 * * * ResultSet:DQL返回的结果集. * 方法说明: * 1、移动型: * next()、previous()、first()、last()、absolute() -->每一行都有一个编号, 1 2 3 -1 -2 -3 负的为从尾开始、 * relative() 相对于当前位置,不是从头、尾开始、 * getRow() 返回当前行的行号 * * 每移动一次,指针指向下一列 absolute(11)超过num移动到0处(标识哪个指针也不指向) * 2、获取型: * get 每一个类型都有俩方法,一个是字段名称 一个是字段index * * 如果是基础类型数据,如果数据库是NULL,会附一个值,可以用 wasNull判断上一个取值的字段是否原始是NULL * 同时基础类型数据也不允许赋值时候给NULL 报错! * * 列名无效直接报错!!! * * 3、更新型: * updateString() 类型 每一个update对应两种,一个针对字段update 一个针对 index update * 更新到集合列表里 * updateRow() 更新一行 更新到数据库 提交! * deleteRow() 直接删除数据 提交! * refreshRow 去数据库取最新数据替换当前的ResultRow(rowId匹配上),从数据库获取更新,必须有指针才开始 * 没有调用updateRow的话,refreshRow会替换掉之前的update * 多次调用refreshRow 效率会下降 * cancelRowUpdates -->取消对当前行的更新操作,必须在updateRow 之前操作 * * 光标没有next时候很多方法都不能使用!!! * * 4、判断函数 * isFirst()... * 5、插入语句 * insertRow插入数据之前 需要先调用moveToInsertRow方法,移动到插入位置处. * 其实相当于moveToInsertRow之后创建一个空对象,然后对这个空对象进行 * 赋值等更新操作,然后才可以插入 * 6、如果resultSet获取列的时候getString("xxx", "") 所指定的列不存在 则报错~~ 没有好办法 * * * StateMent初始化与 集合操作类型: * 1、第一个参数决定 移动类型 * TYPE_FORWARD_ONLY * TYPE_SCROLL_INSENSITIVE * TYPE_SCROLL_SENSITIVE * 是否双向滚动、是否及时更新数据库操作同步 * * 1、只能向前浏览数据 * 2、可以回退、向前,但是对数据更新不敏感 * 3、可以回退、向前,更新数据敏感 * 意思是获取到一个ResultSet结果集,并不是缓存结果集,而是获取rowId,每次取数据都是再次执行sql根据rowId去获取一次最新数据; * 对于新插入的数据,因为没有rowId所以获取不到 * 对于delete的数据,因为rowId还在,所以还是可以找到(因为数据库删除记录只是记录上做一个标记不再被检索,但原来被缓存的ROWID还在, * 根据它还可以通过数据库自己的底层操作正确地把数据提取出来) * * 不起动作: * 1、数据库不支持这个 操作 * 2、setFetchSize 机制 -->不去数据库 去缓存取 避免内存开销,一次拉一批回来放进缓存 * * 2、第二个参数决定 读取类型 * CONCUR_READ_ONLY 只读 * CONCUR_UPDATABLE 可写入 * * 初始化导致问题: * 对只转发结果集的无效操作: first 默认是只能单向next操作的列表集合 * 对只读结果集的无效操作: updateString * 1、这个一个是驱动版本问题 class12.jar不支持写 * 2、select * 操作,driver直接禁止写操作 -->改成 select UserName 具体字段 * * PreparedStateMent: * PreparedStateMent继承自StateMent,所以很多方法类似; * 区别在于PreparedStateMent执行的SQL是数据库里预编译的,编译好之后缓存在数据库里,之后替换参数执行执行. * 批量执行时候效率比StateMent好,但是初次预编译时候效率较低. * * 优点: * 1、可读性更高 * 2、批量效率高 * 3、安全性相对高 --->注入问题 * 因为SQL语句在程序运行前已经进行了预编译,在程序运行时第一次操作数据库之前, * SQL语句已经被数据库分析,编译和优化,对应的执行计划也会缓存下来并允许数据库已参数化的形式进行查询, * 当运行时动态地把参数传给PreprareStatement时,即使参数里有敏感字符如 or '1=1' * 也数据库会作为一个参数一个字段的属性值来处理而不会作为一个SQL指令,如此,就起到了SQL注入的作用了! * * * * CallableStatement --->调用存储过程 * 存储过程参数有三类,IN OUT INOUT,后面两个使用时候需要注册声明一下 * * String xx = { call getEmpName (?, ?)} * call = con.prepareCall(xx) * call.setXX(); * * inout 或者 out的都需要使用registerOutParameter方法 * call.registerOutParameter(index, type); * call.execute(); * call.getXX(index); * * 特别注意 ? 替换的索引从 1 开始的!!! * * * 特别异常: * SQLException * * Close关闭顺序: * resultSet.close() --> statement.close() --> con.close() * * 事物: * ACID * 事物是一个逻辑单元,一次有始有终的活动, 可以包括一个或多个 DML,一个DDL、一个DCL 或多个DQL * 1、减少请求次数 * 2、保障数据安全、有效 * ACID --->I 隔离 * 见网易云 事物隔离: * 脏读:一个事物做了修改,没提交,另一个事物读取了没提交的事物 --->脏读 * 不可重复读:两次查询结果不一样 -->更新 删除 * 幻读:汇总之后 出现新增数据 * * 第一次丢失更新: * 第二类丢失更新: * * 解决隔离问题加锁 --->数据库为我们提供了事务隔离机制,发生响应事物时候自动加锁限制 * 隔离级别越高,并发越差. * 串行 -->最高级别 读/写都加表级锁 * 读可以一起读 但是还是之前版本 * 写的时候只有我操作 * 读已提交的 --->Oracle默认 可能A B差不多一起发生,比如买票 看见还3张,先后下单。数据有延缓性 * 开始读时候才加锁,读完释放锁 * (不可重复读问题) * 重复读 --->读取时候 加共享锁,可以一起读(就算你提交了,也是读那一瞬间的表现 版本) 但是不能改 * --->更新时候 加排他锁,不能一起操作一行 * 锁一直在 * (幻读问题) * 读未提交 --->只有提交瞬间加锁 其他时间可以读到未提交的. --->各种问题 * 读时候不限制 不加锁,可以读未提交 * * (读取)操作创建的锁,其他用户可以并发读取数据,但任何事务都不能获取数据上的排他锁,直到已释放所有共享锁。 共享锁(S锁)又称为读锁,若事务T对数据对象A加上S锁,则事务T只能读A; 其他事务只能再对A加S锁,而不能加X锁,直到T释放A上的S锁 这就保证了其他事务可以读A,但在T释放A上的S锁之前不能对A做任何修改 * * 排他锁(X锁):如果事务T对数据A加上排他锁后,则其他事务不能再对A加任任何类型的封锁 * 获准排他锁的事务既能读数据,又能修改数据。 * * * * @author *** * @date *** */ public class jdbcTest { public static void main(String[] args) throws ClassNotFoundException, SQLException { //加载类 Class.forName("oracle.jdbc.driver.OracleDriver"); //打开连接 Connection con = DriverManager.getConnection("jdbc:oracle:thin:@192.168.1.66:1521/orcl", "smwb_framework", "salis"); Connection con2 = DriverManager.getConnection("jdbc:oracle:thin:smwb_framework/salis@192.168.1.66:1521/orcl"); Properties property = new Properties(); property.put("user", "smwb_framework"); property.put("password", "salis"); Connection con3 = DriverManager.getConnection("jdbc:oracle:thin:@192.168.1.66:1521/orcl", property); //执行statement Statement statement = con.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE); statement.setFetchSize(1); //DDL true/false //statement.execute(""); //DML int 影响行数 //statement.executeUpdate("") //DQL 列表 ResultSet resultSet = statement.executeQuery("select ID,AREA from MESSAGE"); while(resultSet.next()) { System.out.println(resultSet.getString("AREA")); //resultSet.relative("") System.out.println(resultSet.getRow()); //resultSet.refreshRow(); } // absolute(11) 超出的话移动到0位置,0位置为未指定 resultSet.absolute(11); System.out.println("absolute_Row " + resultSet.getRow()); resultSet.moveToInsertRow(); System.out.println("Row " + resultSet.getRow()); String uuid = UUID.randomUUID().toString().replace("-", ""); resultSet.updateInt("ID", (int)Math.random()); //resultSet.insertRow(); System.out.println("Row " + resultSet.getRow()); //以下为updateString refreshRow(强制获取数据库最新数据) //对只转发结果集的无效操作: first 默认是只能单向next操作的列表集合 //resultSet.first(); resultSet.absolute(1); System.out.println(resultSet.getString("AREA")); System.out.println(resultSet.getString("AREA")); System.out.println(resultSet.getString("AREA")); System.out.println(resultSet.getString("AREA")); System.out.println(resultSet.getString("AREA")); resultSet.updateString("AREA", "XYL"); System.out.println(resultSet.getString("AREA")); resultSet.cancelRowUpdates(); System.out.println("cancelRowUpdates " + resultSet.getString("AREA")); resultSet.refreshRow(); System.out.println("refreshRow " + resultSet.getString("AREA")); resultSet.updateString("AREA", "XYL"); resultSet.updateRow(); System.out.println("updateRow " + resultSet.getString("AREA")); resultSet.refreshRow(); System.out.println("refreshRow " + resultSet.getString("AREA")); resultSet.deleteRow(); //预编译 PreparedStatement preState = con2.prepareStatement("select ID,AREA from MESSAGE where ID = ?"); preState.setInt(1, 10086); ResultSet set2 = preState.executeQuery(); //callabled String callAble = "{call BDC_WORKFLOW.PROC_GETMAXNum(?, ?, ?, ?, ?)}"; CallableStatement call = con3.prepareCall(callAble); call.setInt(1, 20190306); call.setString(2, "330110"); call.setString(3, "0"); call.setString(4, "SSSS"); call.registerOutParameter(5, Types.VARCHAR); call.execute(); System.out.println(call.getString(5)); //close resultSet.close(); statement.close(); call.close(); con.close(); con2.close(); con3.close(); } }