Java-jdbc Love The Way You Lie 2022-04-23 05:30 172阅读 0赞 ## 1、JDBC ## JDBC的全称是Java DataBase Connection,也就是Java数据库连接,我们可以用它来操作**关系型数据库**。JDBC接口及相关类在java.sql包和javax.sql包里。我们可以用它来连接数据库,执行SQL查询,存储过程,并处理返回的结果。 > JDBC接口让Java程序和JDBC驱动实现了松耦合,使得切换不同的数据库变得更加简单。 ### 1.1、简述JDBC连接数据库的步骤。 ### **加载驱动 创建连接 执行sql并返回执行结果 处理结果 关闭资源** //1.加载驱动 Class.forName("com.mysql.jdbc.Driver"); //2.创建连接 Connection conn = DriverManager.getConnection(url, user, password); //3.执行sql语句并返回执行结果 //准备sql String sql = "SELECT * FROM student"; //创建Statement,用PreparedStatement更好 //PreparedStatement stmt = conn.prepareStatement(sql); Statement stmt = conn.createStatement(); //执行sql ResultSet rs = stmt.executeQuery(sql); //4.处理结果 while(rs.next()){ int id = rs.getInt("id"); String name = rs.getString("name"); String gender = rs.getString("gender"); System.out.println(id+","+name+","+gender); } //5.关闭资源 stmt.close(); conn.close(); ### **1.2、ResultSet** ### ResultSet是结果集,用来存放执行sql后返回的执行结果,对应于数据库中的数据表。 ### 1.3、JDBC是如何实现Java程序和JDBC驱动的松耦合的? ### JDBC API使用Java的反射机制来实现Java程序和JDBC驱动的松耦合。随便看一个简单的JDBC示例,你会发现所有操作都是通过JDBC接口完成的,而驱动只有在通过Class.forName反射机制来加载的时候才会出现。 我觉得这是Java核心库里反射机制的最佳实践之一,它使得应用程序和驱动程序之间进行了隔离,让迁移数据库的工作变得更简单。在[这里][Link 1]可以看到更多JDBC的使用示例。 ### 1.4、什么是JDBC连接,在Java中如何创建一个JDBC连接? ### JDBC连接是和数据库服务器建立的一个会话。你可以想像成是一个和数据库的Socket连接。 创建JDBC连接很简单,只需要两步: A. 注册并加载驱动:使用Class.forName(),驱动类就会注册到DriverManager里面并加载到内存里。 B. 用DriverManager获取连接对象:调用DriverManager.getConnnection()方法并传入数据库连接的URL,用户名及密码,就能获取到连接对象。 JDBC的DriverManager是一个工厂类,我们通过它来创建数据库连接。当JDBC的Driver类被加载进来时,它会自己注册到DriverManager类里面 ### 1.4、在Java程序中,如何获取数据库服务器的相关信息? ### 使用DatabaseMetaData可以获取到服务器的信息。当和数据库的连接成功建立了之后,可以通过调用getMetaData()方法来获取数据库的元信息。DatabaseMetaData里面有很多方法,通过它们可以获取到数据库的产品名称,版本号,配置信息等。 > DatabaseMetaData metaData = con.getMetaData(); > String dbProduct = metaData.getDatabaseProductName(); ### 1.5、JDBC的Statement是什么? ### Statement是JDBC中用来执行数据库SQL查询语句的接口。通过调用连接对象的getStatement()方法我们可以生成一个Statement对象。我们可以通过调用它的execute(),executeQuery(),executeUpdate()方法来执行静态SQL查询。 由于SQL语句是程序中传入的,如果没有对用户输入进行校验的话可能会引起SQL注入的问题,如果想了解更多关于SQL注入的,可以看下[这里][Link 2]。 默认情况下,一个Statement同时只能打开一个ResultSet。如果想操作多个ResultSet对象的话,需要创建多个Statement。Statement接口的所有execute方法开始执行时都默认会关闭当前打开的ResultSet。 ### 1.6、execute,executeQuery,executeUpdate的区别是什么? ### Statement的execute(String query)方法用来执行任意的SQL查询,如果查询的结果是一个ResultSet,这个方法就返回true。如果结果不是ResultSet,比如insert或者update查询,它就会返回false。我们可以通过它的getResultSet方法来获取ResultSet,或者通过getUpdateCount()方法来获取更新的记录条数。 Statement的executeQuery(String query)接口用来执行select查询,并且返回ResultSet。即使查询不到记录返回的ResultSet也不会为null。我们通常使用executeQuery来执行查询语句,这样的话如果传进来的是insert或者update语句的话,它会抛出错误信息为 “executeQuery method can not be used for update”的java.util.SQLException。 Statement的executeUpdate(String query)方法用来执行insert或者update/delete(DML)语句,或者 什么也不返回DDL语句。返回值是int类型,如果是DML语句的话,它就是更新的条数,如果是DDL的话,就返回0。 只有当你不确定是什么语句的时候才应该使用execute()方法,否则应该使用executeQuery或者executeUpdate方法。 ### 1.7、JDBC的PreparedStatement是什么? ### PreparedStatement对象代表的是一个预编译的SQL语句。用它提供的setter方法可以传入查询的变量。**其使用了缓存池,将编译的上去了语句由DBMS放入缓存池,如果再次接受到对该数据库的操作时,先判断。。。** 由于PreparedStatement是预编译的,通过它可以将对应的SQL语句高效的执行多次。由于PreparedStatement自动对特殊字符转义,避免了SQL注入攻击,因此应当尽量的使用它。 ### 1.8、PreparedStatement中如何注入NULL值? ### 可以使用它的setNull方法来把null值绑定到指定的变量上。setNull方法需要传入参数的索引以及SQL字段的类型,像这样: ps.setNull(10, java.sql.Types.INTEGER);. ### 1.9、Statement中的getGeneratedKeys方法有什么用? ### 有的时候表会生成主键,这时候就可以用Statement的getGeneratedKeys()方法来获取这个自动生成的主键的值了。 ### 1.10、相对于Statement,PreparedStatement的优点是什么? ### 它和Statement相比优点在于: * PreparedStatement有助于防止SQL注入,因为它会自动对特殊字符转义。 * PreparedStatement可以用来进行动态查询。而Statment只能使用静态的sql * PreparedStatement可以使用sql缓存区,效率比Statment高。尤其当你重用它或者使用它的拼量查询接口执行多条语句时。 * 使用PreparedStatement的setter方法更容易写出面向对象的代码,而Statement的话,我们得拼接字符串来生成查询语句。如果参数太多了,字符串拼接看起来会非常丑陋并且容易出错。 ### 1.11、PreparedStatement的缺点是什么,怎么解决这个问题? ### PreparedStatement的一个缺点是,我们不能直接用它来执行in条件语句;需要执行IN条件语句的话,下面有一些解决方案: * 分别进行单条查询——这样做性能很差,不推荐。 * 使用存储过程——这取决于数据库的实现,不是所有数据库都支持。 * 动态生成PreparedStatement——这是个好办法,但是不能享受PreparedStatement的缓存带来的好处了。 * 在PreparedStatement查询中使用NULL值——如果你知道输入变量的最大个数的话,这是个不错的办法,扩展一下还可以支持无限参数。 ### 1.12、DDL和DML语句分别代表什么? ### DDL(数据定义语言,Data Definition Language)语句用来定义数据库模式。Create,Alter, Drop, Truncate, Rename都属于DDL语句,一般来说,它们是不返回结果的。 DML(数据操作语言,Data Manipulation Language)语句用来操作数据库中的数据。select, insert, update, delete, call等,都属于DML语句。 ### **1.13、JDBC的DataSource是什么,有什么好处?** ### DataSource即数据源,它是定义在javax.sql中的一个接口,跟DriverManager相比,它的功能要更强大。我们可以用它来创建数据库连接,当然驱动的实现类会实际去完成这个工作。除了能创建连接外,它还提供了如下的特性: * 缓存PreparedStatement以便更快的执行 * 可以设置连接超时时间 * 提供日志记录的功能 * ResultSet大小的最大阈值设置 * 通过JNDI的支持,可以为servlet容器提供连接池的功能 ### 1.14、JDBC的RowSet是什么,有哪些不同的RowSet? ### ### ### RowSet用于存储查询的数据结果,和ResultSet相比,它更具灵活性。RowSet继承自ResultSet,因此ResultSet能干的,它们也能,而ResultSet做不到的,它们还是可以。RowSet接口定义在javax.sql包里。 RowSet提供的额外的特性有: * 提供了Java Bean的功能,可以通过settter和getter方法来设置和获取属性。RowSet使用了JavaBean的事件驱动模型,它可以给注册的组件发送事件通知,比如游标的移动,行的增删改,以及RowSet内容的修改等。 * RowSet对象默认是可滚动,可更新的,因此如果数据库系统不支持ResultSet实现类似的功能,可以使用RowSet来实现。 RowSet分为两大类: A. 连接型RowSet——这类对象与数据库进行连接,和ResultSet很类似。JDBC接口只提供了一种连接型RowSet,javax.sql.rowset.JdbcRowSet,它的标准实现是com.sun.rowset.JdbcRowSetImpl。 B. 离线型RowSet——这类对象不需要和数据库进行连接,因此它们更轻量级,更容易序列化。它们适用于在网络间传递数据。有四种不同的离线型RowSet的实现。 * CachedRowSet——可以通过他们获取连接,执行查询并读取ResultSet的数据到RowSet里。我们可以在离线时对数据进行维护和更新,然后重新连接到数据库里,并回写改动的数据。 * WebRowSet继承自CachedRowSet——他可以读写XML文档。 * JoinRowSet继承自WebRowSet——它不用连接数据库就可以执行SQL的join操作。 * FilteredRowSet继承自WebRowSet——我们可以用它来设置过滤规则,这样只有选中的数据才可见。 ### 1.15、RowSet和ResultSet的区别是什么? ### RowSet继承自ResultSet,因此它有ResultSet的全部功能,同时它自己添加了些额外的特性。RowSet一个最大的好处是它可以是离线的,这样使得它更轻量级,同时便于在网络间进行传输。 具体使用哪个取决于你的需求,不过如果你操作ResultSet对象的时间较长的话,最好选择一个离线的RowSet,这样可以释放数据库连接。 ## 2、数据库的隔离级别 ## **数据库开启事务命令** •start transaction 开启事务 •Rollback 回滚事务 •Commit 提交事务 **JDBC控制事务语句** •Connection.setAutoCommit(false); //start transaction •Connection.rollback(); //rollback •Connection.commit(); //commit **设置事务回滚点** •Savepoint sp = conn.setSavepoint(); •Conn.rollback(sp); •Conn.commit(); //回滚后必须要提交 **2. 事务的特性(ACID)** 原子性(Atomicity):原子性是指事务是一个不可分割的工作单位,事务中的操作要么都发生,要么都不发生。 一致性(Consistency):事务必须使数据库从一个一致性状态变换到另外一个一致性状态。 隔离性(Isolation):事务的隔离性是多个用户并发访问数据库时,数据库为每一个用户开启的事务,不能被其他事务的操作数据所干扰,多个并发事务之间要相互隔离。 持久性(Durability):持久性是指一个事务一旦被提交,它对数据库中数据的改变就是永久性的,接下来即使数据库发生故障也不应该对其有任何影响。 **3. 事务的隔离级别** 脏读:•指一个事务读取了另外一个**事务未提交**的数据。 不可重复读:•在一个事务内读取表中的某一行数据,多次读取结果不同(另一个**事务已经提交**)。 虚读(幻读):•是指在一个事务内读取到了**别的事务插入的数据**,导致前后读取不一致。 数据库共定义了**五种隔离级别**: <table> <tbody> <tr> <td style="vertical-align:top;"> <p><strong>隔离行为</strong></p> </td> <td style="vertical-align:top;"> <p><strong>更新遗失</strong></p> </td> <td style="vertical-align:top;"> <p><strong>脏读</strong></p> </td> <td style="vertical-align:top;"> <p><strong>无法重复读取</strong></p> </td> <td style="vertical-align:top;"> <p><strong>幻读</strong></p> </td> </tr> <tr> <td style="vertical-align:top;"> <p><strong>读未提交Read Uncommitted</strong></p> </td> <td style="vertical-align:top;"> <p><strong>预防</strong></p> </td> <td style="vertical-align:top;"> <p><strong> </strong></p> </td> <td style="vertical-align:top;"> <p><strong> </strong></p> </td> <td style="vertical-align:top;"> <p><strong> </strong></p> </td> </tr> <tr> <td style="vertical-align:top;"> <p><strong>读已提交</strong></p> <p><strong>Read Uncommitted</strong></p> </td> <td style="vertical-align:top;"> <p><strong>预防</strong></p> </td> <td style="vertical-align:top;"> <p><strong>预防</strong></p> </td> <td style="vertical-align:top;"> <p><strong> </strong></p> </td> <td style="vertical-align:top;"> <p><strong> </strong></p> </td> </tr> <tr> <td style="vertical-align:top;"> <p><strong>可重复读取</strong></p> <p><strong>Repeatable read</strong></p> </td> <td style="vertical-align:top;"> <p><strong>预防</strong></p> </td> <td style="vertical-align:top;"> <p><strong>预防</strong></p> </td> <td style="vertical-align:top;"> <p><strong>预防</strong></p> </td> <td style="vertical-align:top;"> <p><strong> </strong></p> </td> </tr> <tr> <td style="vertical-align:top;"> <p><strong>可串行化</strong></p> <p><strong>Serializable</strong></p> </td> <td style="vertical-align:top;"> <p><strong>预防</strong></p> </td> <td style="vertical-align:top;"> <p><strong>预防</strong></p> </td> <td style="vertical-align:top;"> <p><strong>预防</strong></p> </td> <td style="vertical-align:top;"> <p><strong>预防</strong></p> </td> </tr> </tbody> </table> 还有一种是隔离级别最低的为TRANSACTION\_NONE不支持事务。。MySQL不支持该级别隔离机制。。 > set transaction isolation level 设置事务隔离级别 > > select @@tx\_isolation 查询当前事务隔离级别 当我们为了数据的一致性使用事务时,数据库系统用锁来防止别人访问事务中用到的数据。数据库通过锁来防止脏读,不可重复读(Non-Repeatable Reads)及幻读(Phantom-Read)的问题。 数据库使用JDBC设置的隔离级别来决定它使用何种锁机制,我们可以通过Connection的getTransactionIsolation和setTransactionIsolation方法来获取和设置数据库的隔离级别。 **2.1、JDBC的脏读是什么?哪种数据库隔离级别能防止脏读?** 当我们使用事务时,有可能会出现这样的情况,有一行数据刚更新,与此同时另一个查询读到了这个刚更新的值。这样就导致了脏读,因为更新的数据还没有进行持久化,更新这行数据的业务可能会进行回滚,这样这个数据就是无效的。 数据库的TRANSACTIONREADCOMMITTED,TRANSACTIONREPEATABLEREAD,和TRANSACTION\_SERIALIZABLE隔离级别可以防止脏读。 ### 2.2、什么是幻读,哪种隔离级别可以防止幻读? ### 幻读是指一个事务多次执行一条查询返回的却是不同的值。假设一个事务正根据某个条件进行数据查询,然后另一个事务插入了一行满足这个查询条件的数据。之后这个事务再次执行了这条查询,返回的结果集中会包含刚插入的那条新数据。这行新数据被称为幻行,而这种现象就叫做幻读。 只有TRANSACTION\_SERIALIZABLE隔离级别才能防止产生幻读。 ## 3、事务处理 ## 一个事务是由一条或多条对数据库操作的SQL语句所组成的一个不可分割的工作单元,只有当事务中的所有操作都正常执行了,整个事务才会被提交给数据库。在JDBC中,一般是通过commit()方法或rollback()方法来结束事务的操作。其中commit()方法表示完成对事务的提交,rollback()方法表示完成事务回滚,多用于在处理事务的过程中出现了异常的情况,这两种方法都位于java.sql.Connection类中。一般而言,事务默认操作是自动提交,即操作成功后,系统将自动调用commit()方法,否则将调用rollback()方法。 当然,在JDBC中,也可以通过调用setAutoCommit(false)方法来禁止自动提交,然后就可以把多个数据库操作的表达式作为一个事务,在操作完成后调用commit()方法实现整体提交,如果其中一个表达式操作失败,就会抛出异常而不会调用commit()方法。在这种情况下,就可以在异常捕获的代码块中调用rollback()进行事务回滚。通过此种方法可以保持对数据库的多次操作后,数据仍然保持一致性。 ### 3.1、JDBC的事务管理是什么,为什么需要它? ### 默认情况下,我们创建的数据库连接,是工作在自动提交的模式下的。这意味着只要我们执行完一条查询语句,就会自动进行提交。因此我们的每条查询,实际上都是一个事务,如果我们执行的是DML或者DDL,每条语句完成的时候,数据库就已经完成修改了。 有的时候我们希望由一组SQL查询组成一个事务,如果它们都执行OK我们再进行提交,如果中途出现异常了,我们可以进行回滚。 JDBC接口提供了一个setAutoCommit(boolean flag)方法,我们可以用它来关闭连接自动提交的特性。我们应该在需要手动提交时才关闭这个特性,不然的话事务不会自动提交,每次都得手动提交。数据库通过表锁来管理事务,这个操作非常消耗资源。因此我们应当完成操作后尽快的提交事务。在[这里][Link 3]有更多关于事务的示例程序。 ### 3.2、如何回滚事务? ### 通过Connection对象的rollback方法可以回滚事务。它会回滚这次事务中的所有修改操作,并释放当前连接所持有的数据库锁。 JDBC的保存点(Savepoint)是什么,如何使用? 有时候事务包含了一组语句,而我们希望回滚到这个事务的某个特定的点。JDBC的保存点可以用来生成事务的一个检查点,使得事务可以回滚到这个检查点。 一旦事务提交或者回滚了,它生成的任何保存点都会自动释放并失效。回滚事务到某个特定的保存点后,这个保存点后所有其它的保存点会自动释放并且失效。可以读下[这个][Link 3]了解更多关于JDBC Savepoint的信息。 ### 4、JDBC的批处理 ### 有时候类似的查询我们需要执行很多遍,比如从CSV文件中加载数据到关系型数据库的表里。我们也知道,执行查询可以用Statement或者PreparedStatement。除此之外,JDBC还提供了批处理的特性,有了它,我们可以在一次数据库调用中执行多条查询语句。 JDBC通过Statement和PreparedStatement中的addBatch和executeBatch方法来支持批处理。 批处理比一条条语句执行的速度要快得多,因为它需要很少的数据库调用,想进一步了解请点[这里][Link 4]。 ### 5、连接池 ### 答:实际开发中“获得连接”或“释放资源”是非常消耗系统资源的两个过程,为了解决此类性能问题,通常情况我们采用连接池技术,来共享连接Connection。 用池来管理Connection,这样可以重复使用Connection。有了池,所以我们就不用自己来创建Connection,而是通过池来获取Connection对象。当使用完Connection后,调用Connection的close()方法也不会真的关闭Connection,而是把Connection“归还”给池。池就可以再利用这个Connection对象了。 常见的连接池有C3P0、DBCP。 C3P0开源免费的连接池!目前使用它的开源项目有:Spring、Hibernate等。使用第三方工具需要导入jar包,c3p0使用时还需要添加配置文件c3p0-config.xml。 DBCP也是一个开源的连接池,是Apache Common成员之一,在企业开发中也比较常见,tomcat内置的连接池。 JdbcUtils代码 JdbcUtils.java public class JdbcUtils { private static final String dbconfig = "dbconfig.properties"; private static Properties prop = new Properties(); static { try { InputStream in = Thread.currentThread().getContextClassLoader().getResourceAsStream(dbconfig); prop.load(in); Class.forName(prop.getProperty("driverClassName")); } catch(IOException e) { throw new RuntimeException(e); } } public static Connection getConnection() { try { return DriverManager.getConnection(prop.getProperty("url"), prop.getProperty("username"), prop.getProperty("password")); } catch (Exception e) { throw new RuntimeException(e); } } } dbconfig.properties > driverClassName=com.mysql.jdbc.Driver > > url=jdbc:mysql://localhost:3306/mydb1?useUnicode=true&characterEncoding=UTF8 > > username=root > > password=123 参考:[https://blog.csdn.net/crow\_feiyu/article/details/51305826][https_blog.csdn.net_crow_feiyu_article_details_51305826] [https://blog.csdn.net/qq523786283/article/details/73176573][https_blog.csdn.net_qq523786283_article_details_73176573] [Link 1]: http://www.journaldev.com/2471/jdbc-example-tutorial-drivers-connection-statement-resultset [Link 2]: http://www.journaldev.com/2489/jdbc-statement-vs-preparedstatement-sql-injection-example [Link 3]: http://www.journaldev.com/2483/jdbc-transaction-management-and-savepoint-example-tutorial [Link 4]: http://www.journaldev.com/2494/jdbc-batch-processing-example-tutorial-with-insert-statements [https_blog.csdn.net_crow_feiyu_article_details_51305826]: https://blog.csdn.net/crow_feiyu/article/details/51305826 [https_blog.csdn.net_qq523786283_article_details_73176573]: https://blog.csdn.net/qq523786283/article/details/73176573
相关 Java学习24:JavaJDBC下 ava学习24: JavaJDBC下: 链接:[https://pan.baidu.com/s/1IcilS0VNn9jcVNgjCeycRg][https_pan.... 桃扇骨/ 2024年04月17日 05:54/ 0 赞/ 76 阅读
相关 Java学习23:JavaJDBC上 ava学习23: JavaJDBC上: 链接:[https://pan.baidu.com/s/1cQzVvjzflbDy1qBml41FEQ][https_pan.... £神魔★判官ぃ/ 2024年04月17日 05:37/ 0 赞/ 89 阅读
相关 JavaJDBC:连接池 > 本篇内容包括:数据库连接池概述、JDBC 连接池原理、JDBC 连接池 Demo(addBatch demo、获取主键 demo、查看数据库的元数据 demo等)以及其他类 秒速五厘米/ 2023年09月24日 13:41/ 0 赞/ 119 阅读
相关 JavaJDBC:详解 > 本篇内容包括:JDBC 概述、JDBC 的执行流程(包括注册驱动、获取连接对象、创建 SQL 执行对象、执行SQL语句、遍历结果集、关闭资源(处理异常))以及 JDBC 的 喜欢ヅ旅行/ 2023年09月24日 13:36/ 0 赞/ 196 阅读
相关 java中事务代码大全,java JDBC使用事务示例,javajdbc事务示例,下面代码演示如何使用JD... java JDBC使用事务示例,javajdbc事务示例,下面代码演示如何使用JD 下面代码演示如何使用JDBC的事务。JDBC事务操作需要在执行操作之前调用Connecti ゞ 浴缸里的玫瑰/ 2023年01月13日 09:21/ 0 赞/ 147 阅读
相关 JavaJDBC编程中PreparedStatement的用法 PreparedStatement的用法 一个PreparedStatement是从java.sql.connection对象和所提供的sql字符串得到的,sql字符串中 快来打我*/ 2022年04月04日 06:52/ 0 赞/ 253 阅读
相关 基于javaJDBC技术的账务管理系统(思路+代码) 技术要点: 1.JDBC工具类的搭建 public class JDBCutils { private static BasicDataSource 喜欢ヅ旅行/ 2022年03月16日 06:36/ 0 赞/ 300 阅读
还没有评论,来说两句吧...