0%

java-JDBC应用和原理

[toc]

Connection.setAutoCommit(boolean)#

用于事务提交。
setAutoCommit(true), 则执行的所有sql执行都会作为单个事务直接提交并运行
setAutoCommit(false), 则必须等调用conn.commit()才会提交运行

Q: setAutoCommit默认是true还是false#

A: 默认是true。

Q: setAutoCommit(true)的缺点是什么?
A: 如果一次性执行多个sql语句, 中间sql出错时,就会造成脏数据。

Q: setAutoCommit(false)后,如果出了错却没有在catch中进行Connection的rollBack操作,会发生什么?#

A; 操作的表就会被锁住,造成数据库死锁

fetchSize#

fetchSize 是设定JDBC的Statement读取数据的时候每次从数据库中取出的记录条数#

  • fetchSize越 , 客户端内存占用越 ,读取数据库次数越 ,速度越

Q: Oracle和Mysql中的fetchSize有什么区别?#

A: Oracle会每次网络传输fetchSize条数据到客户端, MYSQL则会一次性全部传送到客户端,因此Mysql中的fetchSize是一种模拟游标。

PreparedStatement#

Q:使用PreparedStatement相比Statement的好处?#

A:

  1. PreparedStatement是预编译的,比Statement速度快,执行效率高,因此即使sql中不带参数也最好使用PreparedStatement
  2. 代码的可读性和可维护性更好(相比于sql拼接)
  3. PreparedStatement可以防止SQL注入攻击,而Statement却不能

Q:prepareStatement是statement接口的实现吗?#

A:
prepareStatement不是实现,而是继承的接口
0a20a5f49a20cca0aa7b8e2b3bf04a98ad855e40

CallableStatement#

  • CallableStatement继承自PreparedStatement
  • CallableStatement接口添加了 调用存储过程 核函数以及处理输出参数(INOUT)的方法。
  • 即存储过程就用CallableStatement

Connection Pool#

连接池优点:

  1. 减少连接创建次数
  2. 更快的系统整体响应速度
  3. 统一连接管理,减少失误性的连接未关闭。

ResultSet#

作用: 缓存数据结果集

1
2
Statement st = conn. createStatement (int resultSetType, int resultSetConcurrency)
ResultSet rs = st.executeQuery(sqlStr)

滚动,就是指调用.next()或者.previous()或者移动到对应行


resultSetType 是设置 ResultSet 对象的类型可滚动,或者是不可滚动。取值如下(见单词知意):

  • ResultSet.TYPE_FORWARD_ONLY 只能向前滚动
  • ResultSet.TYPE_SCROLL_INSENSITIVE, 支持前后滚动,对修改不敏感
  • ResultSet.TYPE_SCROLL_SENSITIVE 支持前后滚动,对修改敏感

resultSetConcurency 是设置 ResultSet 对象能够修改的,取值如下:
  • ResultSet.CONCUR_READ_ONLY 设置为只读类型的参数。
  • ResultSet.CONCUR_UPDATABLE 设置为可修改类型的参数。

Q:Connection、statement、ResultSet的关闭顺序是?#

A:
先ResultSet、再Statement、最后再connection。
因为这种操作很麻烦,最好使用jdbc连接池,或者try-with-resource


相关原理#

Q: JDBC 的spi机制有了解吗?#

A:
SPI机制(Service Provider Interface)其实源自服务提供者框架(Service Provider Framework,参考【EffectiveJava】page6),是一种将服务接口与服务实现分离以达到解耦、大大提升了程序可扩展性的机制。引入服务提供者就是引入了spi接口的实现者,通过本地的注册发现获取到具体的实现类,轻松可插拔

关键在于serviceLoader去加载某路径下相关的driver包,获取出一个列表。

  1. 通过SPI方式,读取 META-INF/services 下文件中的类名,使用TCCL加载;
  2. 通过System.getProperty(“jdbc.drivers”)获取设置,然后通过系统类加载器加载。
    00f3c667eed44817134e87e4bb8ec08084a89177
    dd2a60f8ad344663f1502b1604b1db5519659a73
    后面根据url去查找匹配的driver,每个deriver都有一个match方法

深入理解java SPI机制


Q: jdbc driver是用什么classloader加载的? 和线程上下文又有什么关系?#

A:

  • Class.forName(DriverName, false, loader)代码所在的类在java.util.ServiceLoader类中,而ServiceLoader.class又加载在BootrapLoader中,因此传给 forName 的 loader 必然不能是BootrapLoader,复习双亲委派加载机制请看:java类加载器不完整分析 。这时候只能使用TCCL了,也就是说把自己加载不了的类加载到TCCL中(通过Thread.currentThread()获取,简直作弊啊!)。上面那篇文章末尾也讲到了TCCL默认使用当前执行的是代码所在应用的系统类加载器AppClassLoader。

  • ContextClassLoader默认存放了AppClassLoader的引用,由于它是在运行时被放在了线程中,所以不管当前程序处于何处(BootstrapClassLoader或是ExtClassLoader等),在任何需要的时候都可以用Thread.currentThread().getContextClassLoader()取出应用程序类加载器来完成需要的操作。

  • 我(JDK)提供了一种帮你(第三方实现者)加载服务(如数据库驱动、日志库)的便捷方式,只要你遵循约定(把类名写在/META-INF里),那当我启动时我会去扫描所有jar包里符合约定的类名,再调用forName加载,但我的ClassLoader是没法加载的,那就把它加载到当前执行线程的TCCL里,后续你想怎么操作(驱动实现类的static代码块)就是你的事了。
    真正理解线程上下文类加载器(多案例分析)