网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
https://www.journaldev.com/2509/java-datasource-jdbc-datasource-example
默认情况下,一个Statement同时只能打开一个ResultSet。如果想操作多个ResultSet对象的话,需要创建多个Statement。Statement接口的所有execute方法开始执行时都默认会关闭当前打开的ResultSet。
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方法。
JDBC的PreparedStatement是什么?
PreparedStatement对象代表的是一个预编译的SQL语句。用它提供的setter方法可以传入查询的变量。
由于PreparedStatement是预编译的,通过它可以将对应的SQL语句高效的执行多次。由于PreparedStatement自动对特殊字符转义,避免了SQL注入攻击,因此应当尽量的使用它。
PreparedStatement中如何注入NULL值?
可以使用它的setNull方法来把null值绑定到指定的变量上。setNull方法需要传入参数的索引以及SQL字段的类型,像这样:
ps.setNull(10, java.sql.Types.INTEGER);.
Statement中的getGeneratedKeys方法有什么用?
有的时候表会生成主键,这时候就可以用Statement的getGeneratedKeys()方法来获取这个自动生成的主键的值了。
相对于Statement,PreparedStatement的优点是什么?
它和Statement相比优点在于:
- PreparedStatement有助于防止SQL注入,因为它会自动对特殊字符转义。
- PreparedStatement可以用来进行动态查询。
- PreparedStatement执行更快。尤其当你重用它或者使用它的拼量查询接口执行多条语句时。
- 使用PreparedStatement的setter方法更容易写出面向对象的代码,而Statement的话,我们得拼接字符串来生成查询语句。如果参数太多了,字符串拼接看起来会非常丑陋并且容易出错。
PreparedStatement的缺点是什么,怎么解决这个问题?
PreparedStatement的一个缺点是,我们不能直接用它来执行in条件语句;需要执行IN条件语句的话,下面有一些解决方案:
- 分别进行单条查询——这样做性能很差,不推荐。
- 使用存储过程——这取决于数据库的实现,不是所有数据库都支持。
- 动态生成PreparedStatement——这是个好办法,但是不能享受PreparedStatement的缓存带来的好处了。
- 在PreparedStatement查询中使用NULL值——如果你知道输入变量的最大个数的话,这是个不错的办法,扩展一下还可以支持无限参数。
关于这个问题更详细的分析可以看下
https://www.journaldev.com/2521/java-preparedstatement-in-clause-alternatives
JDBC的ResultSet是什么?
在查询数据库后会返回一个ResultSet,它就像是查询结果集的一张数据表。
ResultSet对象维护了一个游标,指向当前的数据行。开始的时候这个游标指向的是第一行。如果调用了ResultSet的next()方法游标会下移一行,如果没有更多的数据了,next()方法会返回false。可以在for循环中用它来遍历数据集。
默认的ResultSet是不能更新的,游标也只能往下移。也就是说你只能从第一行到最后一行遍历一遍。不过也可以创建可以回滚或者可更新的ResultSet,像下面这样。
Statement stmt = con.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_UPDATABLE);
当生成ResultSet的Statement对象要关闭或者重新执行或是获取下一个ResultSet的时候,ResultSet对象也会自动关闭。
可以通过ResultSet的getter方法,传入列名或者从1开始的序号来获取列数据。
有哪些不同的ResultSet?
根据创建Statement时输入参数的不同,会对应不同类型的ResultSet。如果你看下Connection的方法,你会发现createStatement和prepareStatement方法重载了,以支持不同的ResultSet和并发类型。
一共有三种ResultSet对象。
- ResultSet.TYPE_FORWARD_ONLY:这是默认的类型,它的游标只能往下移。
- ResultSet.TYPE_SCROLL_INSENSITIVE:游标可以上下移动,一旦它创建后,数据库里的数据再发生修改,对它来说是透明的。
- ResultSet.TYPE_SCROLL_SENSITIVE:游标可以上下移动,如果生成后数据库还发生了修改操作,它是能够感知到的。
ResultSet有两种并发类型。
- ResultSet.CONCUR_READ_ONLY:ResultSet是只读的,这是默认类型。
- ResultSet.CONCUR_UPDATABLE:我们可以使用ResultSet的更新方法来更新里面的数据。
Statement中的setFetchSize和setMaxRows方法有什么用处?
setMaxRows可以用来限制返回的数据集的行数。当然通过SQL语句也可以实现这个功能。比如在MySQL中我们可以用LIMIT条件来设置返回结果的最大行数。
setFetchSize理解起来就有点费劲了,因为你得知道Statement和ResultSet是怎么工作的。当数据库在执行一条查询语句时,查询到的数据是在数据库的缓存中维护的。ResultSet其实引用的是数据库中缓存的结果。
假设我们有一条查询返回了100行数据,我们把fetchSize设置成了10,那么数据库驱动每次只会取10条数据,也就是说得取10次。当每条数据需要处理的时间比较长的时候并且返回数据又非常多的时候,这个可选的参数就变得非常有用了。
我们可以通过Statement来设置fetchSize参数,不过它会被ResultSet对象设置进来的值所覆盖掉。
如何使用JDBC接口来调用存储过程?
存储过程就是数据库编译好的一组SQL语句,可以通过JDBC接口来进行调用。我们可以通过JDBC的CallableStatement接口来在数据库中执行存储过程。初始化CallableStatement的语法是这样的:
CallableStatement stmt = con.prepareCall(“{call insertEmployee(?,?,?,?,?,?)}”);
stmt.setInt(1, id);
stmt.setString(2, name);
stmt.setString(3, role);
stmt.setString(4, city);
stmt.setString(5, country);
//register the OUT parameter before calling the stored procedure
stmt.registerOutParameter(6, java.sql.Types.VARCHAR);
stmt.executeUpdate();
我们得在执行CallableStatement之前注册OUT参数。关于这个更详细的资料可以看
https://www.journaldev.com/2502/callablestatement-in-java-example
JDBC的批处理是什么,有什么好处?
有时候类似的查询我们需要执行很多遍,比如从CSV文件中加载数据到关系型数据库的表里。我们也知道,执行查询可以用Statement或者PreparedStatement。除此之外,JDBC还提供了批处理的特性,有了它,我们可以在一次数据库调用中执行多条查询语句。
JDBC通过Statement和PreparedStatement中的addBatch和executeBatch方法来支持批处理。
批处理比一条条语句执行的速度要快得多,因为它需要很少的数据库调用,想进一步了解请点
http://www.journaldev.com/2494/jdbc-batch-processing-example-tutorial-with-insert-statements
JDBC的事务管理是什么,为什么需要它?
默认情况下,我们创建的数据库连接,是工作在自动提交的模式下的。这意味着只要我们执行完一条查询语句,就会自动进行提交。因此我们的每条查询,实际上都是一个事务,如果我们执行的是DML或者DDL,每条语句完成的时候,数据库就已经完成修改了。
有的时候我们希望由一组SQL查询组成一个事务,如果它们都执行OK我们再进行提交,如果中途出现异常了,我们可以进行回滚。
JDBC接口提供了一个setAutoCommit(boolean flag)方法,我们可以用它来关闭连接自动提交的特性。我们应该在需要手动提交时才关闭这个特性,不然的话事务不会自动提交,每次都得手动提交。数据库通过表锁来管理事务,这个操作非常消耗资源。因此我们应当完成操作后尽快的提交事务。在
http://www.journaldev.com/2483/jdbc-transaction-management-and-savepoint-example-tutorial
有更多关于事务的示例程序。
如何回滚事务?
通过Connection对象的rollback方法可以回滚事务。它会回滚这次事务中的所有修改操作,并释放当前连接所持有的数据库锁。
JDBC的保存点(Savepoint)是什么,如何使用?
有时候事务包含了一组语句,而我们希望回滚到这个事务的某个特定的点。JDBC的保存点可以用来生成事务的一个检查点,使得事务可以回滚到这个检查点。
一旦事务提交或者回滚了,它生成的任何保存点都会自动释放并失效。回滚事务到某个特定的保存点后,这个保存点后所有其它的保存点会自动释放并且失效。可以读下
https://www.journaldev.com/2483/java-jdbc-transaction-management-savepoint
了解更多关于JDBC Savepoint的信息。
JDBC的DataSource是什么,有什么好处?
DataSource即数据源,它是定义在javax.sql中的一个接口,跟DriverManager相比,它的功能要更强大。我们可以用它来创建数据库连接,当然驱动的实现类会实际去完成这个工作。除了能创建连接外,它还提供了如下的特性:
- 缓存PreparedStatement以便更快的执行
- 可以设置连接超时时间
- 提供日志记录的功能
- ResultSet大小的最大阈值设置
- 通过JNDI的支持,可以为servlet容器提供连接池的功能
关于JDBC数据源的示例请看下
https://www.journaldev.com/2509/java-datasource-jdbc-datasource-example
如何通过JDBC的DataSource和Apache Tomcat的JNDI来创建连接池?
对部署在servlet容器中的WEB程序而言,创建数据库连接池非常简单,仅需要以下几步。
- 在容器的配置文件中创建JDBC的JNDI资源,通常在server.xml或者context.xml里面。像这样:
- <Resource name=“jdbc/MyDB”
- global=“jdbc/MyDB”
- auth=“Container”
- type=“javax.sql.DataSource”
- driverClassName=“com.mysql.jdbc.Driver”
- url=“jdbc:mysql://localhost:3306/UserDB”
- username=“pankaj”
- password=“pankaj123”
- maxActive=“100”
- maxIdle=“20”
- minIdle=“5”
- maxWait=“10000”/>
- <ResourceLink name=“jdbc/MyLocalDB”
- global=“jdbc/MyDB”
- auth=“Container”
- type=“javax.sql.DataSource” />
在WEB应用程序中,先用InitialContext来查找JNDI资源,然后获取连接。
Context ctx = new InitialContext();
DataSource ds = (DataSource) ctx.lookup("java:/comp/env/jdbc/MyLocalDB");
完整的示例请看
https://www.journaldev.com/2513/tomcat-datasource-jndi-example-java
Apache的DBCP是什么?
如果用DataSource来获取连接的话,通常获取连接的代码和驱动特定的DataSource是紧耦合的。另外,除了选择DataSource的实现类,剩下的代码基本都是一样的。
Apache的DBCP就是用来解决这些问题的,它提供的DataSource实现成为了应用程序和不同JDBC驱动间的一个抽象层。Apache的DBCP库依赖commons-pool库,所以要确保它们都在部署路径下。
完整的使用示例请看
https://www.journaldev.com/2509/java-datasource-jdbc-datasource-example
什么是数据库的隔离级别?
当我们为了数据的一致性使用事务时,数据库系统用锁来防止别人访问事务中用到的数据。数据库通过锁来防止脏读,不可重复读(Non-Repeatable Reads)及幻读(Phantom-Read)的问题。
数据库使用JDBC设置的隔离级别来决定它使用何种锁机制,我们可以通过Connection的getTransactionIsolation和setTransactionIsolation方法来获取和设置数据库的隔离级别。
隔离级别 | 事务 | 脏读 | 不可重复读 | 幻读 |
---|---|---|---|---|
TRANSACTION_NONE | 不支持 | 不可用 | 不可用 | 不可用 |
TRANSACTION_READ_COMMITTED | 支持 | 阻止 | 允许 | 允许 |
TRANSACTION_READ_UNCOMMITTED | 支持 | 允许 | 允许 | 允许 |
TRANSACTION_REPEATABLE_READ | 支持 | 阻止 | 阻止 | 允许 |
TRANSACTION_SERIALIZABLE | 支持 | 阻止 | 阻止 | 阻止 |
JDBC的RowSet是什么,有哪些不同的RowSet?
RowSet用于存储查询的数据结果,和ResultSet相比,它更具灵活性。RowSet继承自ResultSet,因此ResultSet能干的,它们也能,而ResultSet做不到的,它们还是可以。RowSet接口定义在javax.sql包里。
RowSet提供的额外的特性有:
- 提供了Java Bean的功能,可以通过settter和getter方法来设置和获取属性。RowSet使用了JavaBean的事件驱动模型,它可以给注册的组件发送事件通知,比如游标的移动,行的增删改,以及RowSet内容的修改等。
- RowSet对象默认是可滚动,可更新的,因此如果数据库系统不支持ResultSet实现类似的功能,可以使用RowSet来实现。
RowSet分为两大类:
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上大数据知识点,真正体系化!
由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新
[外链图片转存中…(img-tDP5XDgs-1715298661098)]
[外链图片转存中…(img-Prn0fviH-1715298661099)]
[外链图片转存中…(img-QvH7LkkM-1715298661099)]
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上大数据知识点,真正体系化!
由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新