关于jdbc:聊聊如何获取PreparedStatement的参数

序本文次要钻研一下如何获取PreparedStatement的参数 PreparedStatementjava/sql/PreparedStatement.java public interface PreparedStatement extends Statement { void setNull(int parameterIndex, int sqlType) throws SQLException; void setBoolean(int parameterIndex, boolean x) throws SQLException; void setInt(int parameterIndex, int x) throws SQLException; void setLong(int parameterIndex, long x) throws SQLException; //...... default void setObject(int parameterIndex, Object x, SQLType targetSqlType, int scaleOrLength) throws SQLException { throw new SQLFeatureNotSupportedException("setObject not implemented"); } default void setObject(int parameterIndex, Object x, SQLType targetSqlType) throws SQLException { throw new SQLFeatureNotSupportedException("setObject not implemented"); } /** * Retrieves the number, types and properties of this * <code>PreparedStatement</code> object's parameters. * * @return a <code>ParameterMetaData</code> object that contains information * about the number, types and properties for each * parameter marker of this <code>PreparedStatement</code> object * @exception SQLException if a database access error occurs or * this method is called on a closed <code>PreparedStatement</code> * @see ParameterMetaData * @since 1.4 */ ParameterMetaData getParameterMetaData() throws SQLException;}PreparedStatement继承了Statement接口,它次要是多定义了一系列的set办法,然而没有定义get办法,只是定义了getParameterMetaData办法返回ParameterMetaDataParameterMetaDatajava/sql/ParameterMetaData.java ...

September 6, 2023 · 7 min · jiezi

关于jdbc:JDBC写入速度比较慢应该怎么解决

云数据库的话,思考网络环境、并发状况、数据规模。 残缺内容请点击下方链接查看: https://developer.aliyun.com/ask/427635?utm_content=g_1000371542 版权申明:本文内容由阿里云实名注册用户自发奉献,版权归原作者所有,阿里云开发者社区不领有其著作权,亦不承当相应法律责任。具体规定请查看《阿里云开发者社区用户服务协定》和《阿里云开发者社区知识产权爱护指引》。如果您发现本社区中有涉嫌剽窃的内容,填写侵权投诉表单进行举报,一经查实,本社区将立即删除涉嫌侵权内容。

May 11, 2023 · 1 min · jiezi

关于jdbc:JDBC多个数据库之间联表查询

须要对不同数据库中的表进行联表查问,这种问题在单机器中很难见到,个别不会把零碎分成多个库,顶多是分表,微服务或分布式中比拟常见,但此次的确遇到,提供一个简略的实现思路,框架为springboot。 在application.peoperties中配置好两个数据源。两个库别离为db1和db2 spring.datasource.cardbag.jdbcUrl=jdbc:mysql://${MYSQL_HOST:192.168.199.131}:3306/db1spring.datasource.cardbag.username=rootspring.datasource.cardbag.password=123456spring.datasource.cardbag.driver-class-name=com.mysql.cj.jdbc.Driverspring.datasource.zhisonguser.jdbcUrl=jdbc:mysql://${MYSQL_HOST:192.168.199.131}:3306/db2spring.datasource.zhisonguser.username=rootspring.datasource.zhisonguser.password=123456spring.datasource.zhisonguser.driver-class-name=com.mysql.cj.jdbc.Driver新建一个Config类,将数据源配置进去。 @Configurationpublic class DataSourceConfig { /*db1的源*/ @Bean(name="db1DataSource") @Qualifier("db1DataSource") @ConfigurationProperties(prefix="spring.datasource.db1") public DataSource db1DataSource(){ return DataSourceBuilder.create().build(); } /*db2的源*/ @Bean(name="db2DataSource") @Qualifier("db2DataSource") @ConfigurationProperties(prefix="spring.datasource.db2") public DataSource db2DataSource(){ return DataSourceBuilder.create().build(); } @Bean(name="db1Template") public JdbcTemplate db1JdcbTemplate(@Qualifier("db1DataSource")DataSource db1DataSource){ return new JdbcTemplate(db1DataSource); } @Bean(name="db2Template") public JdbcTemplate db2JdcbTemplate(@Qualifier("db2DataSource")DataSource db2DataSource){ return new JdbcTemplate(db2DataSource); }}在数据库中创立一个视图View,通过该View实现多库联查,能够抉择在db1或db2表上新建查问,而后创立一个view,过程大略如下: CREATE VIEW USER_VIEW ASSELECT `db1`.User.userid AS `Userid`, `db1`.User.name AS `Name`, `db2`.User.age AS `Age`,FROM `db1`.UserINNER JOIN `db2`.UserON `db1`.User.userid = `db2`.User.useridWHERE `db1`.User.name is not null;生成视图时要留神在其中一个库中生成,前面操作也是在该源上操作的。 此时能够在代码中对生成的视图进行操作了 ...

October 8, 2022 · 1 min · jiezi

关于jdbc:JDBC-在性能测试中的应用

什么是 JDBCJDBC(Java DataBase Connectivity,Java 数据库连贯)是一种用于执行 SQL 语句的 Java API,能够为多种关系数据库提供对立拜访,它由一组用 Java 语言编写的类和接口组成。JDBC 提供了一种基准,据此能够构建更高级的工具和接口,使数据库开发人员可能编写数据库应用程序。简略地说,JDBC 可做三件事:与数据库建设连贯、发送操作数据库的语句并处理结果。JDBC 的设计原理整体架构 JDBC 制订了一套和数据库进行交互的规范,数据库厂商提供这套规范的实现,这样就能够通过对立的 JDBC 接口来连贯各种不同的数据库。能够说 JDBC 的作用是屏蔽了底层数据库的差别,使得用户依照 JDBC 写的代码能够在各种不同的数据库上进行执行。那么这是如何实现的呢?如下图所示: JDBC 定义了 Driver 接口,这个接口就是数据库的驱动程序, 所有跟数据库打交道的操作最初都会归结到这里 ,数据库厂商必须实现该接口,通过这个接口来实现下层利用的调用者和底层具体的数据库进行交互。Driver 是通过 JDBC 提供的 DriverManager 进行注册的,注册的代码写在了 Driver 的动态块中,如 MySQL 的注册代码如下所示:static { try { java.sql.DriverManager.registerDriver(new Driver()); } catch (SQLException E) { throw new RuntimeException("Can't register driver!"); }}作为驱动定义的标准 Driver,它的次要目标就是和数据库建设连贯,所以其接口也很简略,如下所示:public interface Driver { //建设连贯 Connection connect(String url, java.util.Properties info) throws SQLException;boolean acceptsURL(String url) throws SQLException;DriverPropertyInfo[] getPropertyInfo(String url, java.util.Properties info) throws SQLException;int getMajorVersion();int getMinorVersion();boolean jdbcCompliant();public Logger getParentLogger() throws SQLFeatureNotSupportedException;} ...

July 1, 2022 · 2 min · jiezi

关于jdbc:JDBC-的两种开源的数据库连接池

JDBC 的数据库连接池应用 javax.sql.DataSource 来示意,DataSource 只是一个接口,该接口通常由服务器(Weblogic, WebSphere, Tomcat)提供实现,也有一些开源组织提供实现: 1、DBCP 数据库连接池 2、C3P0 数据库连接池 DataSource 通常被称为数据源,它蕴含连接池和连接池治理两个局部,习惯上也常常把 DataSource 称为连接池 1、DBCP 数据源DBCP 是 Apache 软件基金组织下的开源连接池实现,该连接池依赖该组织下的另一个开源零碎:Common-pool. 如需应用该连接池实现,应在零碎中减少如下两个 jar 文件: 1)Commons-dbcp.jar:连接池的实现 2)Commons-pool.jar:连接池实现的依赖库 Tomcat 的连接池正是采纳该连接池来实现的。该数据库连接池既能够与应用服务器整合应用,也可由应用程序独立应用。 留神: 数据源和数据库连贯不同,数据源无需创立多个,它是产生数据库连贯的工厂,因而整个利用只须要一个数据源即可。 当数据库拜访完结后,程序还是像以前一样敞开数据库连贯:conn.close(); 但 conn.close()并没有敞开数据库的物理连贯,它仅仅把数据库连贯开释,归还给了数据库连接池。 形式一:示例代码: 步骤: 1、退出两个 jar DBCP 数据库连接池的的 jar:Commons-dbcp.jar 连接池实现的依赖库:Commons-pool.jar,如果不加这个,运行报如下异样 Exception in thread "main" java.lang.NoClassDefFoundError: org/apache/commons/pool/KeyedObjectPoolFactoryat com.jdbc.datasource.TestDBCP.main(TestDBCP.java:14)Caused by: java.lang.ClassNotFoundException: org.apache.commons.pool.KeyedObjectPoolFactoryat java.net.URLClassLoader.findClass(Unknown Source)at java.lang.ClassLoader.loadClass(Unknown Source)at sun.misc.Launcher$AppClassLoader.loadClass(Unknown Source)at java.lang.ClassLoader.loadClass(Unknown Source)... 1 more2、编写代码 package com.jdbc.datasource; import java.sql.Connection;import java.sql.SQLException; import org.apache.commons.dbcp.BasicDataSource; public class TestDBCP { ...

April 15, 2022 · 3 min · jiezi

关于jdbc:最通俗易懂的JDBC数据库教程jdbc学习笔记

本文是依据B站能源节点老杜的视频教程做的笔记,教程中解说了Java语言如何连贯数据库,对数据库中的数据进行增删改查操作,适宜于曾经学习过Java编程根底以及数据库的同学。Java教程中论述了接口在开发中的真正作用,JDBC标准制订的背景,JDBC编程六部曲,JDBC事务,JDBC批处理,SQL注入,行级锁等。 1、JDBC是什么?Java DataBase Connectivity(Java语言连贯数据库) 2、JDBC的实质是什么?JDBC是SUN公司制订的一套接口(interface) java.sql.*; (这个软件包下有很多接口。) 接口都有调用者和实现者。 面向接口调用、面向接口写实现类,这都属于面向接口编程。 为什么要面向接口编程? 解耦合:升高程序的耦合度,进步程序的扩大力。 多态机制就是十分典型的:面向形象编程。(不要面向具体编程) 倡议:Animal a = new Cat();Animal a = new Dog();// 喂养的办法public void feed(Animal a){ // 面向父类型编程。 } 不倡议:Dog d = new Dog();Cat c = new Cat(); 思考:为什么SUN制订一套JDBC接口呢? 因为每一个数据库的底层实现原理都不一样。Oracle数据库有本人的原理。MySQL数据库也有本人的原理。MS SqlServer数据库也有本人的原理。....每一个数据库产品都有本人独特的实现原理。 JDBC的实质到底是什么?一套接口。 3、JDBC开发前的筹备工作,先从官网下载对应的驱动jar包,而后将其配置到环境变量classpath当中。classpath=.;D:\course\06-JDBC\resources\MySql Connector Java 5.1.23\mysql-connector-java-5.1.23-bin.jar 以上的配置是针对于文本编辑器的形式开发,应用IDEA工具的时候,不须要配置以上的环境变量。IDEA有本人的配置形式。 4、JDBC编程六步(须要背会)第一步:注册驱动(作用:通知Java程序,行将要连贯的是哪个品牌的数据库)第二步:获取连贯(示意JVM的过程和数据库过程之间的通道关上了,这属于过程之间的通信,重量级的,应用完之后肯定要敞开通道。)第三步:获取数据库操作对象(专门执行sql语句的对象)第四步:执行SQL语句(DQL DML....)第五步:解决查问后果集(只有当第四步执行的是select语句的时候,才有这第五步解决查问后果集。)第六步:开释资源(应用完资源之后肯定要敞开资源。Java和数据库属于过程间的通信,开启之后肯定要敞开。)注册驱动形式两种URL解析5.遍历后果集 idea导入.jar包 视频教程整起:https://www.bilibili.com/video/BV1Bt41137iB字符串中拼变量(sql)单引号中 加双引号 加两个加号++ 加号外面加变量 PreparedStatement预编译查(SELECT)第四步用 rs = ps.executeQuery(“字段”); 增删改(INSERT DELETE UPDATE)第四步用 rs= ps.executeUpdate(“字段”); 框架(select) public static void main(String[] args){ Connection conn = null; PreparedStatement ps = null;//加d是名词 ResultSet rs = null; try { //1.注册驱动 Class.forName("com.mysql.cj.jdbc.Driver"); //2.获取连贯 conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/codeyuaiiao?serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=utf-8","root","3615yuhaijiao"); //3.获取预编译的数据库操作对象 String sql = "select * from student"; ps = conn.prepareStatement(sql);//动词 //4.执行sql语句 rs = ps.executeQuery();//查问select //rs = ps.executeUpdate();//增删改都是用Update(INSERT DELETE UPDATE) //5.解决查问后果集 } catch (Exception e) { e.printStackTrace(); }finally { //6.开释资源 if (rs != null) { try { rs.close(); } catch (SQLException e) { e.printStackTrace(); } } if (ps != null) { try { ps.close(); } catch (SQLException e) { e.printStackTrace(); } } if (conn != null) { try { rs.close(); } catch (SQLException e) { e.printStackTrace(); } } } }事务三件套 (开启 提交 回滚)conn.setAutoCommit(false); 敞开主动提交 开启事务conn.commit(); 手动提交conn.rollback(); catch中写 回滚if(conn != null){ try{ conn.rollback(); ...

February 11, 2022 · 1 min · jiezi

关于jdbc:如何使用JDBC-API操作数据库

1、应用JDBC API操作数据库的根本步骤剖析应用JDBC API操作数据库,进行CRUD根本步骤都类似。加载和注册驱动获取数据库连贯筹备操作执行SQL的Statement对象执行SQL调用Statement对象的executeUpdate(String sql)执行SQL语句进行插入、批改、删除操作调用Statement对象的executeQuery(String sql)执行SQL语句进行查问操作解决执行后果CUD操作,依据返回的int值判断后果查问操作,依据返回ResultSet后果集,大数据培训获取查问数据开释资源总结:加载和注册驱动,整个我的项目做一次即可获取数据库连贯能够封装到一个办法中开释资源能够封装到一个办法中2、编写工具类JDBCUtilspackage com.atguigu.utils;import java.io.IOException;import java.sql.Connection;import java.sql.DriverManager;import java.sql.ResultSet;import java.sql.SQLException;import java.sql.Statement;import java.util.Properties;/* 工具类: 1、注册驱动:只有运行一次 2、获取连贯 3、敞开资源 */ public class JDBCUtils {private static String drivername;private static String url;private static String user;private static String password;private static Properties pro = new Properties();static{try {//加载,读取jdbc.properties配置的信息//pro.load的作用是把jdbc.properties文件中配置的信息,一一put到pro这个map中pro.load(ClassLoader.getSystemClassLoader().getResourceAsStream(“jdbc.properties”));// drivername = pro.getProperty(“key”)drivername = pro.getProperty(“drivername”);url = pro.getProperty(“url”);user = pro.getProperty(“user”);password = pro.getProperty(“password”);//注册驱动,加载驱动Class.forName(drivername);} catch (ClassNotFoundException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();}}public static Connection getConnection()throws SQLException{Connection conn = DriverManager.getConnection(url, user, password);return conn;}public static void closeQuietly(Connection conn){try {if(conn!=null){conn.close();}} catch (SQLException e) {e.printStackTrace();}}public static void closeQuietly(Statement st){try {if(st!=null){st.close();}} catch (SQLException e) {e.printStackTrace();}}public static void closeQuietly(ResultSet rs){try {if(rs!=null){rs.close();}} catch (SQLException e) {e.printStackTrace();}}public static void closeQuietly(Statement st,Connection conn){closeQuietly(st);closeQuietly(conn);}public static void closeQuietly(ResultSet rs,Statement st,Connection conn){closeQuietly(rs);closeQuietly(st);closeQuietly(conn);}}

January 6, 2022 · 1 min · jiezi

关于jdbc:如何解决JDBC死链接导致NIFI线程假死

景象ExcuteSql不工作不调度,stop也进行不了,active的工作个数也不升高。 场景复现1、模拟现场结构数据 2、Tc命令模仿现场网络环境模拟丢包30% tc qdisc del dev enp4s0f0 root netem loss 30% 问题排查netstat -pan | grep $NIFI_PID 能够看到和数据库开启了5个tcp连贯,也能够看到有五个socket处于连贯状态 间接看线程jstack -l $NIFI_PID | grep 'java.net.SocketInputStream.socketRead0(Native Method)' -C 10 察看了一段时间,socket依然没有敞开,能够根本必定现场环境应该曾经复现。 问题剖析一次残缺的数据库申请包含三个阶段: 1、建设连贯 2、数据传输 3、断开连接connect timeout:如果与服务器(这里指数据库)申请建设连贯的工夫超过ConnectionTimeOut,就会抛 ConnectionTimeOutException,即服务器连贯超时,没有在规定的工夫内建设连贯。 socket timeout:如果与服务器连贯胜利,就开始数据传输了。如果服务器解决数据用时过长,超过了SocketTimeOut,就会抛出SocketTimeOutExceptin,即服务器响应超时,服务器没有在规定的工夫内返回给客户端数据。 在未设置超时工夫场景下,nifi通过jdbc获取数据库数据,实质是操作系统在调用Socket.read(),如果网络问题被阻塞住,那么操作系统会很少在调用Socket,java培训因为底层没有数据交互,那么其无奈晓得对端是否宕机,所以会始终期待。 难道零碎没有默认的socketTimeout? 有的,上述场景下内核会在一个socket两个小时都没有数据交互状况下(可设置)启动keepalive定时器来探测对端的socket。 也就是说呈现假死会在7875秒后复原,大略两个多小时吧。 这就是nifi端线程假死的起因; 论断JDBC的socket未设置超时,呈现了死连贯。 JDBC 驱动是用 Socket 形式与数据库连贯的,应用程序和数据库之间的连贯超时并不是由数据库解决的。 当数据库忽然宕掉或产生网络谬误(设施故障等)时,JDBC 驱动的 Socket 超时的值是必须的。因为 TCP/IP 的构造,Socket 没有方法检测到网络谬误,因而利用不能检测到与数据库到连贯断开了。如果没有设置 Socket 超时,应用程序会始终期待数据库返回后果。(这个连贯也被叫做“死连贯”) 为了防止死连贯,Socket 必须要设置超时工夫。Socket 超时能够通过 JDBC 驱动程序配置。通过设置 Socket 超时,能够防止出现网络谬误时始终期待的状况并缩短故障工夫。在timeout为0的时候,走默认的零碎调用不设置超时工夫的逻辑。在timeout>0时,将socket设置为非阻塞,而后用select零碎调用去模仿超时,而没有走linux自身的超时逻辑。 解决措施设置socket和Connection的超时工夫。 避免因为网络问题呈现死连贯,给数据库的拜访url加socket timeout和Connection timeout。

January 5, 2022 · 1 min · jiezi

关于jdbc:JDBC基础入门教程轻松掌握jdbc基础核心技术超全面

JDBC是什么?JDBC是Sun提供的一套数据库编程接口API函数,由Java语言编写的类、界面组成。 JDBC API 的设计初衷就是为了让简略的事件更简略,这意味着JDBC使得执行所有数据库工作都更容易. 用JDBC写的程序可能主动地将SQL语句传送给相应的数据库管理系统。不但如此,应用Java编写的应用程序能够在任何反对Java的平台上运行,不用在不同的平台上编写不同的利用。 Java和JDBC的联合能够让开发人员在开发数据库应用程序时真正实现 “WriteOnce,RunEverywhere!” JDBC的用处是什么?简略地说,JDBC 可做三件事: 与数据库建设连贯,发送 SQL 语句,处理结果。JDBC学习教程能源节点老杜解说的jdbc教程,能够说是十分全面了,想学习jdbc的小伙伴们不能错过!! JDBC学习视频教程内容涵盖: 解说Java语言如何连贯数据库,对数据库中的数据进行增删改查操作解说接口在开发中的真正作用JDBC标准制订的背景JDBC编程六部曲JDBC事务JDBC批处理SQL注入行级锁等适宜人群适宜于曾经学习过Java编程根底以及数据库的小伙伴们 JDBC视频教程:https://www.bilibili.com/vide... JDBC材料下载:http://www.bjpowernode.com/?s... 学习目录•001. JDBC课程的目录构造介绍 •002. 对JDBC实质的了解 •003. 编写程序模仿JDBC实质 •004. 将驱动jar配置到环境变量classpath中 •005. JDBC编程六步的概述 •006. 注册驱动与获取连贯 •007. 执行sql与开释资源 •008. JDBC执行删除与更新 •009. 类加载的形式注册驱动 •010. 从属性资源文件中读取连贯数据库信息 •011. 解决查问后果集 •012. 应用IDEA开发JDBC代码配置驱动 •013. 回顾JDBC •014. 用户登录业务介绍 •015. PowerDesigner工具的装置 •016. 应用PowerDesigner工具进行物理建模 •017. 用户登录性能界面的初始化 •018. 登录办法的实现 •019. 演示SQL注入景象 •020. 解决SQL注入问题 •021. Statement和PreparedStatement比照 •022. 演示Statement的用处 •023. PreparedStatement实现增删改 •024. JDBC的事务主动提交机制的演示 •025. 账户转账演示事务 •026. JDBC工具类的封装 •027. JDBC实现含糊查问 ...

December 15, 2021 · 1 min · jiezi

关于jdbc:JavaWeb中jdbc增删查改

jdbc增删查改前言: Java数据库连贯(Java Database Connectivity,J 简称JDBC),简略来说就是 应用Java执行sql语句的,面向关系型数据库的java_API 创立 Directory包 > 取名为 lib后导入 MySQL-connector-java-5.1.37 文件创立数据库(不然连贯什么) // 根据实体类创立数据库字段和类型 创立实体类(BankModel.java)package com.kjzz;public class Teacher{ // 成员变量 private int SID; // id private String SName; // name private String birthday; // birthday 生日 // 无参结构 / 有参结构 public Teacher(){ } public Teacher(int SID, String SName,String birthday) { this.SID = SID; this.SName = SName; this.birthday = birthday; } // get / set 办法 public int getSID() { return SID; } public void setSID(int SID) { this.SID = SID; } public String getSName() { return SName; } public void setSName(String SName) { this.SName = SName; } public String getBirthday(){ return birthday; } public void setBirthday(String birthday){ this.birthday = birthday; }}5.创立数据库驱动类(DBUtill.java) ...

November 1, 2021 · 4 min · jiezi

关于jdbc:JDBC快速开发工具包fastjdbc使用介绍

介绍一个开源的jdbc操作工具类fastjdbc,一个基于SpringBoot框架开发的jdbc疾速开发工具包它实际上是对spring框架提供的NamedJdbcTemplate类的二次封装,提供一种基于注解形式的API配置形式去操作sql,而不是调用NamedJdbcTemplate的办法去传递sql语句。 装置 <dependency> <groupId>com.github.paganini2008.springdessert</groupId> <artifactId>fastjdbc-spring-boot-starter</artifactId> <version>2.0.3</version> </dependency>(以最新版为准) 疾速开始上面举几个例子,看看fastjdbc-spring-boot-starter是怎么应用的 @Daopublic interface UserDao { @Insert("insert into tb_user(username,password,age) values (:username,:password,:age)") int saveUser(@Example User user); @Update("update tb_user set username=:username, password=:password where id=:id") int updateUser(@Example User user); @Update("delete from tb_user where id=:id") int deleteUser(@Arg("id") int id); @Batch("insert into tb_user(username,password,age) values (:username,:password,:age)") int batchSaveUser(@Args List<User> userList); @Get("select * from tb_user where id=:id") User getById(@Arg int id); @Query("select * from tb_user order by create_time desc") List<Map<String, Object>> queryUser(); @Query("select * from tb_user where 1=1 @sql order by create_time desc") List<Map<String, Object>> queryUserByCondition(@Sql String whereCondition, @Example Map<String,Object> queryExample); @Select("select * from tb_user order by create_time desc") ResultSetSlice<User> selectUser();}API很简略,但须要的注意事项: ...

August 15, 2021 · 1 min · jiezi

关于jdbc:JDBC-42-Specifications-中文翻译-第十二章-分布式事务

到目前为止,对于事务的探讨基本上都聚焦在本地事务上,本地事务只会波及到一个繁多的数据源。本章开始介绍分布式事务,分布式事务会在单个事务内波及多个数据源。以下内容次要包含: 散布识事务基础设施事务管理器和资源管理器XADataSource,XAConnection 和 XAResource 接口两阶段提交JDBC 的事务管理 API 与 JTA 标准是兼容的。 基础设施分布式事务的基础设施包含以下几个局部: 事务管理器,用来管制事务的边界和治理两阶段提交协定。它也应该是 JTA 的一个经典实现。实现了XADataSource,XAConnection 和 XAResource 接口的 JDBC 驱动。一个对于应用层齐全可见的 DataSource 实现,利用它来操作 XADataSource ,并与事务管理器交互。通常这个实现由应用服务器提供。用来治理底层数据的资源管理器。在 JDBC 的语境中,资源管理器指的是数据库服务器。“资源管理器” 这个术语实际上来自于 JTA,在这里应用这个术语是为了强调 JDBC 中的分布式事务是遵循 JTA 标准的架构来解决的。通常会以“三层架构”来实现这个基础设施,包含: 客户端一个蕴含应用程序、EJB 服务器、JDBC 驱动汇合的中间层多个资源管理器分布式事务也能够实现为“两层架构”。在两层架构中,应用层自身就会表演事务管理器的角色,并且间接操作 XADataSource API。下图论述了分布式事务的基础设施: 后续的内容将会对基础设施的各个局部进行具体的阐明 XADataSource 和 XAConnectionXADataSource 和 XAConnection 接口,定义在 javax.sql 包中。反对分布式事务的数据库驱动须要实现这两个接口。XAConnection 继承了 PooledConnection 接口,增加了一个 getXAResource 办法,这个办法会生成一个 XAResource 对象,事务管理器能够利用这个对象来实现分布式事务。以下是 XAConnection 接口的定义: public interface XAConnection extends PooledConnection {javax.transaction.xa.XAResource getXAResource() throws SQLException;}因为继承了 PooledConnection 接口,所以所有的 XAConnection 对象也反对 PooledConnection 中定义的办法。这些对象代表着与底层数据源的一条可重用的物理连贯,应用层也能够通过这个对象操作这条连贯。 ...

June 15, 2021 · 3 min · jiezi

关于jdbc:故障分析-Java-连接-MySQL-80-排错案例

作者:胡呈清爱可生 DBA 团队成员,善于故障剖析、性能优化,集体博客:https://www.jianshu.com/u/a95...,欢送探讨。 本文起源:原创投稿 *爱可生开源社区出品,原创内容未经受权不得随便应用,转载请分割小编并注明起源。 在客户那边遇见过几次这样的问题,Java 连贯 MySQL8.0 偶然会报错:java.sql.SQLNonTransientConnectionException: Public Key Retrieval is not allowed。网上一搜这个报错,早有人踩过这个坑: 如果用户应用了 sha256_password 认证,明码在传输过程中必须应用 TLS 协定爱护,然而如果 RSA 公钥不可用,能够应用服务器提供的公钥;能够在连贯中通过 ServerRSAPublicKeyFile 指定服务器的 RSA 公钥,或者 AllowPublicKeyRetrieval=True 参数以容许客户端从服务器获取公钥;然而须要留神的是 AllowPublicKeyRetrieval=True 可能会导致歹意的代理通过中间人攻打(MITM)获取到明文明码,所以默认是敞开的,必须显式开启。原始文档如下: JDBC 增加参数 AllowPublicKeyRetrieval=True 后的确解决了问题,然而留神前边说的“偶然”两字,我遇见了大略三种状况,在这之前一点问题都没有,但一产生就会报错: 1.MySQL Server 重启了; 2.MySQL MGR 产生切换了; 3.DBA 对 MySQL Server 做了某些变更后。 那后面的解释就显得有点不够了,尤其是第 3 点,开发肯定会感觉这是 MySQL 层面的问题或者 DBA 的问题,而不是他们对 MySQL 驱动理解不够。上面就为大家揭晓答案。 caching_sha2_passwordMySQL8.0 默认应用明码加密插件为:caching_sha2_password,应用 sha256 算法对明码加密。而且在应用此插件时,MySQL Server 会在内存中缓存用户的认证信息,使已连贯的用户的身份验证速度更快,文档形容: 这个缓存的说法就很暧昧了,当然因为文档上没有更多形容,咱们只能先做假如:Java 程序通过驱动连贯到 MySQL 时,如果 MySQl Server 有用户的验证缓存,则不须要额定配置 RSA 公钥即可连贯胜利;如果没有缓存也没有指定 RSA 公钥,则连贯报错:Public Key Retrieval is not allowed。 ...

March 9, 2021 · 1 min · jiezi

关于jdbc:面试被问JDBC底层是如何连接数据库的

关注“Java后端技术全栈” 回复“面试”获取全套面试材料 背景前两天一个小伙伴面试的时候,被问JDBC底层是如何连贯数据库的? 他登时一脸懵逼,因为大部分人只晓得JDBC的几个步骤,至于底层到底是怎么连贯数据库的,还真不知道。 因为小伙伴是面试高级开发,问这种问题倒也不能说面试官过分,如果是高级或者中级,那问着问题就的确有些过分了。 然而如果你在高级或者中级的阶段,就晓得了答案,岂不是爽歪歪么? 预计大部分人都不晓得这个问题该怎么答复,略微发散一下思维,倒是能够猜想一下,明天咱们就来搞清楚JDBC底层到底是如何连贯数据库的。往后别再猜了。 反过来,如果面试官问你JDBC的时候,你能晓得底层是怎么连贯数据库的,预计,很多绝对较水的面试官也会一脸懵逼。 何为 JDBC ?JDBC(Java DataBase Connectivity)是Java和数据库之间的一个桥梁,是一个「标准」而不是一个实现,可能执行SQL语句。JDBC由一组用Java语言编写的类和接口组成。各种不同类型的数据库都有相应的实现,留神:本文中的代码都是针对MySQL数据库实现的。 JDBC 架构分为双层架构和三层架构。 双层作用:此架构中,Java Applet 或利用间接拜访数据源。 条件:要求 Driver 能与拜访的数据库交互。 机制:用户命令传给数据库或其余数据源,随之后果被返回。 部署:数据源能够在另一台机器上,用户通过网络连接,称为 C/S配置(能够是内联网或互联网)。 三层侧架构非凡之处在于,引入中间层服务。 流程:命令和构造都会通过该层。 吸引:能够减少企业数据的访问控制,以及多种类型的更新;另外,也可简化利用的部署,并在少数状况下有性能劣势。 历史趋势:以往,因性能问题,中间层都用 C 或 C++ 编写,随着优化编译器(将 Java 字节码 转为 高效的 特定机器码)和技术的倒退,如EJB,Java 开始用于中间层的开发这也让 Java 的劣势突显呈现进去,应用 Java 作为服务器代码语言,JDBC随之被器重。 入门案例上面给出一个JDBC入门级案例: public class JdbcDemo {    public static final String URL = "jdbc:mysql://localhost:3306/mblog";    public static final String USER = "root";    public static final String PASSWORD = "123456";    public static void main(String[] args) throws Exception {         Class.forName("com.mysql.jdbc.Driver");         Connection conn = DriverManager.getConnection(URL, USER, PASSWORD);         Statement stmt = conn.createStatement();         ResultSet rs = stmt.executeQuery("SELECT id, name, age FROM m_user where id =1");         while(rs.next()){            System.out.println("name: "+rs.getString("name")+" 年龄:"+rs.getInt("age"));        }    }}JDBC 步骤数据库驱动: Class.forName("com.mysql.jdbc.Driver"); 获取连贯: Connection conn = DriverManager.getConnection(URL, USER, PASSWORD); 创立Statement或者PreparedStatement对象: Statement stmt = conn.createStatement(); 执行sql数据库查问: ResultSet rs = stmt.executeQuery("SELECT id, name, age FROM m_user where id =1"); 解析后果集: System.out.println("name: "+rs.getString("name")+" 年龄:"+rs.getInt("age"));最初就是各种资源的敞开。 数据库驱动加载MySql的驱动类 : Class.forName("com.mysql.jdbc.Driver"); 咱们装置好数据库之后,咱们的应用程序也是不能间接应用数据库的,必须要通过相应的数据库驱动程序,通过驱动程序去和数据库打交道。其实也就是数据库厂商的JDBC接口实现,即对Connection等接口的实现类的jar文件。 Driver接口java.sql.Driver此接口是提供给数据库厂商实现的。比如说MySQL的,须要依赖对应的jar包。 <dependency>    <groupId>mysql</groupId>    <artifactId>mysql-connector-java</artifactId>    <version>8.0.16</version></dependency>MySQL数据库对应的实现驱动实现类: package com.mysql.cj.jdbc;import java.sql.SQLException; public class Driver extends NonRegisteringDriver implements java.sql.Driver {    static {        try {            //注册驱动            java.sql.DriverManager.registerDriver(new Driver());        } catch (SQLException E) {            throw new RuntimeException("Can't register driver!");        }    }     public Driver() throws SQLException {     }}DriverManager是rt.jar包下的类,(rt=runtime),把咱们须要驱动类注册进去。 //DriverManager类中的办法public static synchronized void registerDriver(java.sql.Driver driver, DriverAction da)    throws SQLException {     /* Register the driver if it has not already been added to our list */     if(driver != null) {          registeredDrivers.addIfAbsent(new DriverInfo(driver, da));      } else {          // This is for compatibility with the original DriverManager          throw new NullPointerException();      }      println("registerDriver: " + driver);}相应装载Oracle驱动: ...

December 26, 2020 · 1 min · jiezi

关于jdbc:JDBC4-jdbc预编译与拼接sql对比

在jdbc中,有三种形式执行sql,别离是应用Statement(sql拼接),PreparedStatement(预编译),还有一种CallableStatement(存储过程),在这里我就不介绍CallableStatement了,咱们来看看Statement与PreparedStatement的区别。1. 创立数据库,数据表数据库名字是test,数据表的名字是student,外面有四个字段,一个是id,也就是主键(主动递增),还有名字,年龄,问题。最初先应用sql语句插入六个测试记录。 CREATE DATABASE `test` DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci;CREATE TABLE `student` ( `id` INT NOT NULL AUTO_INCREMENT , `name` VARCHAR(20) NOT NULL , `age` INT NOT NULL , `score` DOUBLE NOT NULL , PRIMARY KEY (`id`)) ENGINE = MyISAM; INSERT INTO `student` VALUES (1, '小红', 26, 83);INSERT INTO `student` VALUES (2, '小白', 23, 93);INSERT INTO `student` VALUES (3, '小明', 34, 45);INSERT INTO `student` VALUES (4, '张三', 12, 78);INSERT INTO `student` VALUES (5, '李四', 33, 96);INSERT INTO `student` VALUES (6, '魏红', 23, 46);建设对应的学生类: ...

December 5, 2020 · 3 min · jiezi

关于jdbc:JDBC通用CRUD解决方案DBUtils

一, 先相熟DBUtils的API:简介:DbUtils是一个为简化JDBC操作的小类库。以下应用的是最新版的commons-dbutils-1.4,先给个简介,以便迅速把握API的应用。整个dbutils总共才3个包: 1、包org.apache.commons.dbutils 接口摘要 ResultSetHandler 将ResultSet转换为别的对象的工具。RowProcessor       将ResultSet行转换为别的对象的工具。 类摘要 BasicRowProcessor   RowProcessor接口的根本实现类。 BeanProcessor       BeanProcessor匹配列明到Bean属性名,并转换后果集列到Bean对象的属性中。DbUtils             一个JDBC辅助工具汇合。 ProxyFactory        产生JDBC接口的代理实现。 QueryLoader         属性文件加载器,次要用于加载属性文件中的 SQL 到内存中。 QueryRunner         应用可插拔的策略执行SQL查问并处理结果集。ResultSetIterator   包装后果集为一个迭代器。 2、包org.apache.commons.dbutils.handlersResultSetHandler接口的实现类 类摘要 AbstractListHandler 将ResultSet转为List的抽象类ArrayHandler 将ResultSet转为一个Object[]的ResultSetHandler实现类ArrayListHandler 将ResultSet转换为List<Object[]>的ResultSetHandler实现类BeanHandler 将ResultSet行转换为一个JavaBean的ResultSetHandler实现类BeanListHandler 将ResultSet转换为List<JavaBean>的ResultSetHandler实现类ColumnListHandler 将ResultSet的一个列转换为List<Object>的ResultSetHandler实现类KeyedHandler 将ResultSet转换为Map<Map>的ResultSetHandler实现类MapHandler 将ResultSet的首行转换为一个Map的ResultSetHandler实现类MapListHandler 将ResultSet转换为List<Map>的ResultSetHandler实现类ScalarHandler 将ResultSet的一个列到一个对象。3、包org.apache.commons.dbutils.wrappers增加java.sql类中性能包装类。 类摘要 SqlNullCheckedResultSet  在每个getXXX办法上查看SQL NULL值的ResultSet包装类。StringTrimmedResultSet    取出后果集中字符串左右空格的ResultSet包装类。二,应用DBUtils其实只是应用的话,只看两个类(DbUtils 和QueryRunner)和一个接口(ResultSethandler)就能够了。 1,DbUtilsDbUtils是一个为做一些诸如敞开连贯、装载JDBC驱动程序之类的惯例工作提供有用办法的类,它外面所有的办法都是动态的。 这个类里的重要办法有: close(): DbUtils类提供了三个重载的敞开办法。这些办法查看所提供的参数是不是NULL,如果不是的话,它们就敞开连贯、申明和后果集(ResultSet)。 CloseQuietly:CloseQuietly这一办法不仅能在连贯、申明或者后果集(ResultSet)为NULL状况下防止敞开,还能暗藏一些在程序中抛出的SQLEeception。如果你不想捕获这些异样的话,这对你是十分有用的。在重载CloseQuietly办法时,特地有用的一个办法是closeQuietly(Connection conn,Statement stmt,ResultSet rs),这是因为在大多数状况下,连贯、申明和后果集(ResultSet)是你要用的三样货色,而且在最初的块你必须敞开它们。应用这一办法,你最初的块就能够只须要调用这一办法即可。 CommitAndCloseQuietly(Connection conn): 这一办法用来提交连贯,而后敞开连贯,并且在敞开连贯时不向上抛出在敞开时产生的一些SQL异样。LoadDriver(String driveClassName):这一办法装载并注册JDBC驱动程序,如果胜利就返回TRUE。应用这种办法,你不须要去捕获这个异样ClassNotFoundException。应用loadDrive办法,编码就变得更容易了解,你也就失去了一个很好的Boolean返回值,这个返回值会通知你驱动类是不是曾经加载胜利了。 2,ResultSetHandler 这一接口执行解决一个jaca.sql.ResultSet,将数据转变并解决为任何一种模式,这样有益于其利用而且应用起来更容易。这一组件提供了ArrayHandler, ArrayListHandler, BeanHandler, BeanListHandler, MapHandler, MapListHandler, and ScalarHandler等执行程序。 ...

December 5, 2020 · 3 min · jiezi

关于jdbc:JDBC3-SPI技术以及数据库连接中的使用

[TOC] 1.SPI是什么?SPI,即是Service Provider Interface,是一种服务提供(接口实现)发现机制,能够通过ClassPath门路下的META-INF/Service文件查找文件,加载外面定义的类。个别能够用来启用框架拓展和替换组件,比方在最常见的数据库连贯JDBC中,java.sql.Driver,不同的数据库产商能够对接口做不一样的实现,然而JDK怎么晓得他人有哪些实现呢?这就须要SPI,能够查找到接口的实现,对其进行操作。用两个字解释:解耦。 2.如何应用SPI来提供自定义服务?咱们来写一个简略的例子:整个我的项目构造: SPI-Project:maven我的项目 DBInterface:maven我的项目,parent是SPI-Project,定义了一个接口com.aphysia.sqlserver.DBConnectionService,本人不做实现。MysqlConnection:prarent是SPI-Project,实现了接口DBConnectionService,也就是MysqlConnectionServiceImplSqlServerConnection:prarent 也是SPI-Project,实现了DBConnectionService,也就是SqlServerConnectionServiceImplWebProject:测试项目,模仿web我的项目外面应用数据库驱动。不论是MySqlConnection还是SqlServerConnection两个module中,都是去实现了DBInterface的接口,并且在resource/META-INF/services下都须要申明所实现的类,文件名就是实现的接口全限定名com.aphysia.sql.DBConnectionService,文件外面就是具体的实现类的全限定名,比方:com.aphysia.mysql.MysqlConnectionServiceImpl SPI-Project的pom文件: <?xml version="1.0" encoding="UTF-8"?><project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.aphysia</groupId> <artifactId>SPI-Project</artifactId> <packaging>pom</packaging> <version>1.0-SNAPSHOT</version> <modules> <module>DbInterface</module> <module>MySqlConection</module> <module>SqlServerConnection</module> <module>WebProject</module> </modules></project>2.1 DBInterface定义接口DBInterface是SPIProject的一个module,次要是定义一个标准(接口),不做任何实现。pom文件如下: <?xml version="1.0" encoding="UTF-8"?><project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <parent> <artifactId>SPI-Project</artifactId> <groupId>com.aphysia</groupId> <version>1.0-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>DbInterface</artifactId></project>定义的接口(模仿了java提供的数据库驱动的情景,定义了驱动标准):DBConnectionService.java package com.aphysia.sql;public interface DBConnectionService { void connect();}2.2 模仿Mysql实现驱动接口的第一种实现,相当于模仿第三方Mysql对接口做了本人的拓展:pom文件: <?xml version="1.0" encoding="UTF-8"?><project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <parent> <artifactId>SPI-Project</artifactId> <groupId>com.aphysia</groupId> <version>1.0-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>MySqlConection</artifactId> <dependencies> <dependency> <groupId>com.aphysia</groupId> <artifactId>DbInterface</artifactId> <version>1.0-SNAPSHOT</version> </dependency> </dependencies></project>实现了后面定义的接口:MysqlConnectionServiceImpl package com.aphysia.mysql;import com.aphysia.sqlserver.DBConnectionService;public class MysqlConnectionServiceImpl implements DBConnectionService { public void connect() { System.out.println("mysql 正在连接..."); }}申明实现,在resource/META-INF.services/下定义一个文件,名为com.aphysia.sql.DBConnection,外面内容是: ...

November 28, 2020 · 4 min · jiezi

关于jdbc:JDBC2-JDBC工作原理以及简单封装

[TOC] 1. 工作原理个别咱们次要的JDBC解决流程如下: graph TDA[注册一个Driver] -->B(建设数据库连贯)B --> C(创立一个Statement)C-->D(执行SQL语句,获取后果)D-->F(敞开JDBC对象)1.1 加载驱动首先申明:这个阶段在1.6之后就不须要手动执行了,也就是这个代码不须要了!!!剖析它有利于了解流程。 Class.forName("com.mysql.jdbc.Driver")下面代码产生在注册Driver阶段,指的是让JVN将com.mysql.jdbc.Driver这个类加载入内存中,最重要的是将mysql驱动注册到DriverManager中去。 此处加载Driver的时候,加载的是java.mysql.jdbc包下的,这其实是一种向后兼容的做法,实际上代码都是写在了com.mysql.cj.jdbc下,所以,mysql的连贯包应用了继承的形式,com.mysql.jdbc.Driver只是对外的一个兼容类,其父类是com.mysql.cj.jdbc.Driver,真正的的mysql Driver驱动。 加载Driver的目标就是加载它的父类: public class Driver extends com.mysql.cj.jdbc.Driver { public Driver() throws SQLException { super(); }}咱们关上com.mysql.cj.jdbc.Driver,能够发现,外面有一个结构空办法,这也是调用Class.forName().newInstance() 所须要的,这个类继承了NonRegisteringDriver,实现了java.mysql.Driver。外面有一个空无参构造方法,为反射调用newInstance()筹备的,另外就是动态代码块,动态代码块次要的性能是通过DriverManager注册本人(Driver,也就是驱动),这里很重要的一点,就是Driver是java.sql.Driver(这是jdk的包!!!)的实现。 咱们引入的驱动实质上是JDK中的Driver的实现类,为啥?这就是规范,束缚,不这样干,不合规矩。public class Driver extends NonRegisteringDriver implements java.sql.Driver { static { try { // 调用DriverManager 注册本人(Driver) java.sql.DriverManager.registerDriver(new Driver()); } catch (SQLException E) { throw new RuntimeException("Can't register driver!"); } } public Driver() throws SQLException { // Class.forName().newInstance() 所须要的 }}DriverManager外面根本全是static办法,也就是专门治理各种驱动的,registerDriver()办法如同其名字,就是为了注册驱动,注册到哪里呢?看上面的代码,能够晓得,driverInfo(驱动相干信息)会被增加到registeredDrivers外面去。registeredDrivers是DriverManager的static属性,外面寄存着注册的驱动信息。如果这个驱动曾经被注册,那么就不会再注册。 public static synchronized void registerDriver(java.sql.Driver driver) throws SQLException { registerDriver(driver, null); } public static synchronized void registerDriver(java.sql.Driver driver, DriverAction da) throws SQLException { /* Register the driver if it has not already been added to our list */ if(driver != null) { // 将驱动相干信息加到registeredDrivers registeredDrivers.addIfAbsent(new DriverInfo(driver, da)); } else { // This is for compatibility with the original DriverManager throw new NullPointerException(); } println("registerDriver: " + driver); }到这里其实Class.forName(“com.mysql.jdbc.Driver”)这句代码剖析就完结了。 ...

November 28, 2020 · 3 min · jiezi

关于jdbc:JDBC1初级增删改查

[TOC] 1.jdbc是什么JDBC(Java DataBase Connectivity,java数据库连贯)是一种用于执行SQL语句的Java API,能够为多种关系数据库提供对立拜访,它由一组用Java语言编写的类和接口组成。JDBC提供了一种基准,据此能够构建更高级的工具和接口,使数据库开发人员可能编写数据库应用程序。(百度百科)jdbc常常用来连贯数据库,创立sql或者mysql语句,应用相干的api去执行sql语句,从而操作数据库,达到查看或者批改数据库的目标。学习jbbc要求对java编程有肯定理解,并理解一种数据库系统以及sql语句。环境要求:1.本地装好jdk,并且装好mysql数据库,我是间接装过wamp带有mysql数据库/docker中装置的mysql。2.应用IDEA开发 2.应用IDEA开发2.1 创立数据库,数据表我的mysql是应用docker创立的,如果是windows环境能够应用wamp较为不便。 数据库名字是test,数据表的名字是student,外面有四个字段,一个是id,也就是主键(主动递增),还有名字,年龄,问题。最初先应用sql语句插入六个测试记录。 CREATE DATABASE `test` DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci;USE test;CREATE TABLE `student` ( `id` INT NOT NULL AUTO_INCREMENT , `name` VARCHAR(20) NOT NULL , `age` INT NOT NULL , `score` DOUBLE NOT NULL , PRIMARY KEY (`id`)) ENGINE = MyISAM; INSERT INTO `student` VALUES (1, '小红', 26, 83);INSERT INTO `student` VALUES (2, '小白', 23, 93);INSERT INTO `student` VALUES (3, '小明', 34, 45);INSERT INTO `student` VALUES (4, '张三', 12, 78);INSERT INTO `student` VALUES (5, '李四', 33, 96);INSERT INTO `student` VALUES (6, '魏红', 23, 46);2.2 应用IDEA创立我的项目我应用maven工程形式,我的项目目录: ...

November 28, 2020 · 4 min · jiezi

关于jdbc:JDBC总结

一:Junit单元测试框架junit(单元测试框架): 能够不必增加main函数,也不必创立类的实例就能够执行一个办法 可能用单元测试执行的办法必须满足如下条件:1)办法必须是公共的(public)2)办法必须是非动态的3)办法必须是无返回值的(void)4)办法必须是无参数的junit(单元测试框架)罕用的三个注解: @Test、@before、@after @Test:应用了该注解的办法,每次选中办法名,右键-->Run as-->junit test都能够执行该办法@before:应用了该注解的办法,每次会在@Test标记的办法之前执行。也就是说,每次在执行@Test标记的办法之前都会执行@before标记的办法@after:应用了该注解的办法,每次会在@Test标记的办法之后执行。也就是说,每次在执行@Test标记的办法之后都会执行@after标记的办法二:JDBC概述1.什么是JDBC:通过JAVA语言连贯数据库 2.如何通过JDBC程序连贯数据库: //1.注册数据库驱动Class.forName("com.mysql.jdbc.Driver");//2.获取数据库连贯Connection conn=DriverManager.getConnection("jdbc:mysql:///jt_db?characterEncoding=utf-8","root","root");//3.获取传输器Statement stat=conn.createStatement();//4.发送SQL语句到服务器执行,并返回后果String sql="select * from account";ResultSet rs=stat.executeQuery(sql);//5.解决执行的后果while(rs.next()) { int id=rs.getInt("id"); String name=rs.getString("name"); Double money=rs.getDouble("money"); System.out.println(id+","+name+","+money); }//6.开释资源frs.close(); stat.close(); conn.close();System.out.println("JdbcTest01.testFindAll01()");}

November 8, 2020 · 1 min · jiezi

关于jdbc:JDBC预防SQL注入预处理对象PrepareStatement

PreparedStatement接口介绍PreparedStatement是Statement接口的子接口,继承于父接口中所有的办法.它是一个预编译的SQL语句对象预编译: 是指SQL语句被预编译,并存储在PreparedStatement对象中. 而后能够应用此对象屡次市郊地执行该语句PreparedStatement特点因为有事后编译的性能, 进步SQL的执行效率能够无效的避免SQL注入的问题,安全性更高获取PreparedStatement对象与Statement相似, 都是通过Connection进行创立创立 Connection接口中的办法阐明PreparedStatement prepareStatement(String sql)指定预编译的SQL语句预编译的SQL语句: SQL语句中应用占位符?PreparedStatement接口罕用办法与Statement相似,接口罕用办法名都是int executeUpdate()与ResultSet executeQuery与Statement不同,上述两个办法是没有参数的.因为在结构PrepareStatement对象时,就曾经传入SQL进行了预编译罕用办法阐明int executeUpdate()执行insert / update / delete 语句.返回受影响的行数ResultSet executeQuery()执行select语名.返回后果集对象Result应用PreparedStatement的步骤编定SQL语句,未知内容应用 ? 占位select * from test_02 where name = ? and password = ?获取PreparedStatement对象设置理论参数: 调用setXxx办法执行参数化SQL敞开资源应用PreparedStatement实现登录案例代码示例public class LoginTest02 { public static void main(String[] args) throws SQLException { // 1.获取连贯 Connection conn = JDBCUtils.getConnection(); // 2.应用占位符创立sql语句,获取prepareStatement对象 // 应用?占位符的形式来设置参数 String sql = "select * from test_02 where name = ? and password = ?"; PreparedStatement preparedStatement = conn.prepareStatement(sql); // 3.提醒用户输出用户名和明码 Scanner sc = new Scanner(System.in); System.out.println("请输出用户名:"); String user = sc.next(); System.out.println("请输出明码:"); String pass = sc.next(); // 4.替换占位符,执行sql语句 // 设置参数,应用setXXX(int 占位符地位 从1开始, 要设置/替换的值)的办法设置占位符的参数 preparedStatement.setString(1,user); // 设置第一个问号值为name preparedStatement.setString(2,pass); ResultSet resultSet = preparedStatement.executeQuery(); // 5.处理结果集,判断是否登录胜利 if(resultSet.next()){ System.out.println("登录胜利!欢送:" + user); }else{ System.out.println("登录失败!"); } // 6.敞开对象 JDBCUtils.close(conn,preparedStatement,resultSet); }}运行后果(失常) ...

September 5, 2020 · 1 min · jiezi

关于jdbc:JDBCSQL注入案例模拟用户登录

需要承接上文JDBC-SQL注入简介. 在大略理解了什么是SQL注入之后,接下来应用test_02数据库表模仿用户登录操作. 提醒用户输出用户名和明码,应用Statement字符串拼接的形式实现用户登录 步骤JDBC-编写工具类调用JDBCUtils工具类获取连贯调用JDBCUtils工具类获取语句执行平台(即Statement对象)提醒用户输出用户名和明码执行字符串接接后的sql语句判断是否登录胜利调用JDBCUtils工具类敞开对象代码示例public class LoginTest01 { public static void main(String[] args) throws SQLException { // 1.获取连贯 Connection conn = JDBCUtils.getConnection(); // 2.获取执行平台,即Statement对象 Statement sqlExecute = JDBCUtils.createStatement(conn); // 3.提醒用户输出用户名和明码 Scanner sc = new Scanner(System.in); System.out.println("请输出用户名: "); String user = sc.nextLine(); System.out.println("请输出明码: "); String pass = sc.nextLine(); // 4.执行字符串拼接后的sql语句 String sql = "select * from test_02 where name = '" + user + "' and password = '" + pass + "';"; System.out.println(sql);//查看执行的sql语句 ResultSet resultSet = sqlExecute.executeQuery(sql); // 5.判断是否登录胜利 if(resultSet.next()){ System.out.println("登录胜利,欢送:" + user); }else{ System.out.println("登录失败!"); } // 5.敞开对象 JDBCUtils.close(conn,sqlExecute,resultSet); }}运行后果一(失常输出) ...

September 5, 2020 · 1 min · jiezi

关于jdbc:JDBCSQL注入简述

什么是SQL注入SQL注入即是指web应用程序对用户输出数据的合法性没有判断或过滤不严,攻击者能够在web应用程序中当时定义好的查问语句的结尾上增加额定的SQL语句,在管理员不知情的状况下实现非法操作,以此来实现坑骗数据库服务器执行非受权的任意查问,从而进一步失去相应的数据信息。(百度百科)其实简略说就是,在输出零碎要求的内容之外,又增加了额定的内容,造成返回数据数据库返回谬误内容,从而给攻击者提供可乘之机 接下应用mysql进行阐明 数据库操作演示首先我曾经在数据库建好一张表test_02,数据内容如下: 以id=1的数据为例 如果name='wdm' and password='0704',那么是能够正确返回这条数据的,这里就模仿wdm用户登录胜利 如果name='wdm' and password='1111' or '1'='1'.能够看到,其实这里对于用户wdm来说,明码曾经输出谬误.但因为前面有一个or '1'='1'这个恒为真的语句并且应用or 一真为真,同假为假,数据库表却返回了正确的后果. 如果说这是一个登录操作,此时用户就曾经登录胜利了.这显示不是咱们想要的成果

September 5, 2020 · 1 min · jiezi

关于jdbc:JDBC概述

什么是JDBCJDBC(Java Data Base Connectivity) 是Java拜访数据库的标准规范.是一种用于执行SQL语句的Java API.能够以多种关系数据舞蹈提供对立拜访. 它由一组用Java语言编写的类和接口组成. 是Java拜访数据库的标准规范JDBC原理JDBC是接口,驱动是接口的实现.没有驱动将无奈实现数据库连贯,从而不能操作数据库每个数据库厂商都须要提供本人的驱动,用来连贯本人公司的数据库. 也就是说,驱动个别都由数据库生成厂商提供 总结JDBC就是由sun公司定义的一套操作所有关系型数据库的规定(接口)数据库厂商就须要实现这套接口,提供数据库驱动jar包咱们能够应用这套接口进行编程,真正执行的代码是对应驱动包中的实现类附:导入连贯mysql数据库的jar包JDBC-idea导入mysql连贯java的jar包

September 4, 2020 · 1 min · jiezi

关于jdbc:JDBC的基本使用

1.应用步骤:导包加载驱动类class.forName(....)DriverManager获取sql对象写sql语句Statement与prepareStatement不一样的执行sql语句,前者是间接sql对象 . 执行语句,而后者是预处理,即在创立sql对象时,就用sql语句,最初在用对象执行操作。2.JDBCUtils工具类:就是把应用步骤的共性,提出来,放在一个类中,这样当前就能间接调用创立sql对象的办法就行了。值得一提的是,如果咱们应用配置文件,扩展性更好,在应用其余数据库时,咱们只须要更改配置文件properties。获取配置文件能够应用properties对象。获取jar包的绝对路径能够应用classLoader类加载器。这样咱们就不必本人增加门路了。 public class JDBCUtils { private static String url; private static String user; private static String password; private static String driver; /** * 文件的读取,只须要读取一次即可拿到这些值。应用动态代码块 */ static{ //读取资源文件,获取值。 try { //1. 创立Properties汇合类。 Properties pro = new Properties(); //获取src门路下的文件的形式--->ClassLoader 类加载器 ClassLoader classLoader = JDBCUtils.class.getClassLoader(); URL res = classLoader.getResource("jdbc.properties"); String path = res.getPath(); //2. 加载文件 pro.load(new FileReader(path)); //3. 获取数据,赋值 url = pro.getProperty("url"); user = pro.getProperty("user"); password = pro.getProperty("password"); driver = pro.getProperty("driver"); //4. 注册驱动 Class.forName(driver); } catch (IOException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } } /** * 获取连贯 * @return 连贯对象 */ public static Connection getConnection() throws SQLException { return DriverManager.getConnection(url, user, password); } /** * 开释资源 * @param stmt * @param conn */ public static void close(Statement stmt,Connection conn){ if( stmt != null){ try { stmt.close(); } catch (SQLException e) { e.printStackTrace(); } } if( conn != null){ try { conn.close(); } catch (SQLException e) { e.printStackTrace(); } } } /** * 开释资源 * @param stmt * @param conn */ public static void close(ResultSet rs,Statement stmt, Connection conn){ if( rs != null){ try { rs.close(); } catch (SQLException e) { e.printStackTrace(); } } if( stmt != null){ try { stmt.close(); } catch (SQLException e) { e.printStackTrace(); } } if( conn != null){ try { conn.close(); } catch (SQLException e) { e.printStackTrace(); } } } }3.DBC管制事务:在执行sql语句时,如果程序执行过程中,产生了异样。比方有两个sql语句,一个是转账操作,一个人存转的这笔钱,如果第一个操作完结后产生了异样。那么这笔钱就会不胫而走,所以为了防止这样的状况,能够应用事务。 ...

August 23, 2020 · 3 min · jiezi

关于jdbc:jdbc

jdbc概述jdbc(java database connectivity)java数据库连贯技术//注册数据库驱动,让jdbc连贯mysql驱动程序1.class.forName("com.mysql.jdbc.Driver");//获取数据库连贯,连贯上数据库的url地址2.Connection conn=DriverManager.getConnection{"jdbc:mysql://localhost:3306/具体的库名?characterEncoding=utf-8","root","root");//获取传输器3.Statement stat = conn.createStatement();//发送sql到服务器执行并且返回执行后果4. String sql= "select * from account"; ResultSet rs= stat.executeQuery( sql );//处理结果,这里的解决形式不惟一,能够有很多种解决形式能够通过同理数组的形式,将值进行批改赋值,5.while(rs.next()){int id =//资源开释,这外面的资源开释时,越是晚获取执行的就越先敞开资源6. rs.close(); stat.close(); conn.close(); jdbcUtil.close(conn.stat.rs)//jdbc工具类,能够对立开释资源 resultSet后果集对象//用于封装sql语句查问到的后果 (1)遍历数据行的办法 next()-- 能够通过while()语句去执行循环遍历数组 (2)获取数据的办法 PrepareStatement//在开发中咱们用的更多的传输器对象是PreparedStatement对象,PreparedStatement是Statement的子接口,比Statement更加平安,并且可能进步程序执行的效率。 ### sql注入攻打 即便不输出明码只输出用户名也能够实现登陆胜利,这就是sql注入攻打 ### 避免SQL注入攻打 (1)应用正则表达式在提交参数前进行校验, (2)应用prepareStatement ### 正则表达式 正则表达式(regular expression)形容了一种字符串匹配的模式(pattern),能够用来查看一个串是否含有某种子串、将匹配的子串替换或者从某个串中取出合乎某个条件的子串等。//自行百度 数据库连接池 连接池:就是将连贯寄存在容器中,供整个程序共享,能够实现连贯的复用,缩小连贯创立和敞开的次数,从而进步程序执行的效率! ### 如何应用C3P0连接池 所有的连接池都要实现一个接口——DataSource(数据源),因而连接池也被叫做数据源! C3p0连接池的应用步骤 1.导入 jar 2.创立数据库连接池(对象) 3.设置根本信息 设置连贯数据库的根本信息 (1)形式一:(不举荐) 间接将参数通过 pool.setXxx办法设置给c3p0程序 (2)形式二:将连贯参数提取到properties文件中(举荐) 在类目录下(开发时能够放在src或者相似的源码目录下),增加一个c3p0.properties文件 (3)形式三:将连贯参数提取到xml文件中(举荐) 在类目录下(开发时能够放在src或者相似的源码目录下),增加一个c3p0-config.xml文件 4.从连接池中获取一个连贯对象并且进行应用 5.用完连贯后将连贯还回连接池中 System.out.println("TestJdbc.main()....");//为了防止程序出现异常,能够抛出异样解决,

August 6, 2020 · 1 min · jiezi

关于jdbc:JDBC连接那些事

分享一篇对于JDBC连贯数据库的好文:JDBC连贯数据库的四种形式:DriverManager,DataSource,DBCP,C3P0 以及Oracle官网举荐应用dataSource进行数据库连贯教程:jdbc连贯Oracle教程连贯代码示例 这也解释了为什么在写spring我的项目时,咱们要配置datasource了,而不是通过DriverManager来获取连贯。

August 3, 2020 · 1 min · jiezi

如何在云上使用confdACM管理敏感数据

在前面的一些文章中,我们介绍了如何在云上安全的存放配置数据,但是上面的方法都是有代码侵入性的,也就是说需要修改应用程序,本文会讲解如何使用 confd+ACM 在不修改代码的情况下动态修改应用所需的配置,并且可以重新启动应用加载最新的配置。这样做可以在无代码侵入的情况下加强应用程序的安全性和运维效率: 安全性:应用程序的数据可能是敏感数据,ACM 具有健壮的访问控制机制,可以对敏感数据进行加密来安全的保存密码、API密钥、证书等敏感信息;运维效率:当需要修改应用的某些配置内容时,如果只有一两台机器可以手工操作,但是当涉及几十上百台数量的时候,confd+ACM可以通过配置的发布批量进行配置修改和重启操作;无代码侵入:通过confd+ACM的组合可以做到无需修改应用代码即可达到让应用配置动态生效的效果 下面以应用的数据库配置为例讲解如何使用confd+ACM安全管理应用配置 准备工作在操作本文的示例之前需要配置好开通ACM和对confd的使用有基本概念,ACM的开通及其基本使用可以参考:这里confd的基本使用可以参考:这里 创建confd配置文件创建confd所需的toml格式配置文件 vim /etc/confd/conf.d/myapp.toml指定模版文件,ACM中的加密配置需要以/cipher-开头check_cmd用于检验配置的正确性,防止错误配置导致应用加载失败reload_cmd用于重启应用或者让应用动态加载配置 [template]src = "jdbc.properties.tmpl"dest = "/tmp/jdbc.properties"keys = ["/cipher-myapp/database/jdbc",]#check_cmd = "check config is correct"reload_cmd = "restart app"创建模版文件vim /etc/confd/templates/jdbc.properties.tmplgetv从ACM中获取对应dataId的配置:/cipher-myapp/database/jdbc对应的dataId为cipher-myapp.database.jdbcconfd基于kms会自动对/cipher-开头的配置进行解密 {{$data := json (getv "/cipher-myapp/database/jdbc")}}jdbc.url={{$data.url}}jdbc.username={{$data.username}}jdbc.password={{$data.password}}在ACM上创建所需的配置文件创建dataId为cipher-myapp.database.jdbc的配置文件,group使用默认的DEFAULT_GROUP即可,配置内容为 {"url":"jdbc:mysql://localhost:3306/dbName","username":"testuser","password":"testpassword"} 启动confd和官网文档不同的是,要支持解密功能,需要设置confd的-openKMS开关,并且设置kms服务的regionId,这个信息可以从示例代码中获得 confd -backend nacos -endpoint {endpoint}:8080 -namespace {namespace} -accessKey {accessKey} -secretKey {secretKey} -openKMS true -regionId {regionId} -watch 生成配置文件查看生成的/tmp/jdbc.properties配置文件,如果生成了该文件,并且文件内容如下则说明整个流程运行正常 jdbc.url=jdbc:mysql://localhost:3306/dbNamejdbc.username=testuserjdbc.password=testpassword变更ACM配置内容当需要修改数据库的连接串的时候,直接在ACM上修改cipher-myapp.database.jdbc配置,confd会重新生成数据库配置文件,并让应用加载最新配置。当然在实际生产环境中,可以使用ACM的Beta功能对几台机器先进行灰度发布,检验没问题再继续全量发布 本文演示了如何使用confd+ACM安全管理敏感数据,ACM安全配置更多信息还可以参考:这里 本文作者:风卿,Nacos 社区 Committer 本文作者:中间件小哥阅读原文 本文为云栖社区原创内容,未经允许不得转载。

July 11, 2019 · 1 min · jiezi

AnalyticDB-for-PG-如何作为数据源对接帆软-FineBI

AnalyticDB for PostgreSQL 基于开源数据库 Greenplum 构建,兼容Greenplum 和 PostgreSQL 的语法,接口和生态。本章节介绍如何通过FineBI连接 分析型数据库PostgreSQL版 并进行报表开发。 准备工作 开始使用FineBI之前,用户需要先完成以下准备工作。下载并安装FineBI 操作步骤 首先进行”新建数据连接“,并选择 "Greenplum Database"。 之后将 JDBC URL,数据库名称,用户名密码等输入进行连接测试。 注意事项 对于新安装的FineBI,第一次连接 Greenplum 或 PostgreSQL 数据源时,需要先下载其 JDBC Driver,可以按操作步骤下载并将对应JDBC 驱动安装到 FineBI 目录。AnalyticDB for PostgreSQL 既支持 PostgreSQL JDBC Driver,也支持 Greenplum 社区 Driver。 本文作者:陆封阅读原文 本文为云栖社区原创内容,未经允许不得转载。

June 25, 2019 · 1 min · jiezi

使用JDBC和Spring访问关系数据

本指南将引导您完成使用Spring访问关系数据的过程。你要构建什么您将使用Spring构建一个JdbcTemplate应用程序来访问存储在关系数据库中的数据。你需要什么大约15分钟最喜欢的文本编辑器或IDEJDK 1.8或更高版本Gradle 4+或Maven 3.2+您还可以将代码直接导入IDE:使用STS构建/导入入门指南使用IntelliJ IDEA导入入门指南如何完成本指南与大多数Spring入门指南一样,您可以从头开始并完成每个步骤,或者您可以绕过您已熟悉的基本设置步骤。无论哪种方式,您最终都会使用工作代码。要从头开始,请继续使用Gradle构建。要跳过基础知识,请执行以下操作:下载并解压缩本指南的源存储库,或使用Git克隆它:git clone https://github.com/spring-guides/gs-relational-data-access.git进入gs-relational-data-access/initial跳转到创建Customer对象。完成后,可以根据gs-relational-data-access/complete中的代码检查结果。使用Gradle构建首先,设置一个基本的构建脚本。在使用Spring构建应用程序时,您可以使用任何您喜欢的构建系统,但此处包含了使用Gradle和Maven所需的代码。如果您不熟悉这两者,请参阅使用Gradle构建Java项目或使用Maven构建Java项目。创建目录结构在您选择的项目目录中,创建以下子目录结构;例如,在nix系统上使用mkdir -p src/main/java/hello:└── src └── main └── java └── hello创建Gradle构建文件下面是最初的Gradle构建文件。buildscript { repositories { mavenCentral() } dependencies { classpath(“org.springframework.boot:spring-boot-gradle-plugin:2.0.3.RELEASE”) }}apply plugin: ‘java’apply plugin: ’eclipse’apply plugin: ‘idea’apply plugin: ‘org.springframework.boot’apply plugin: ‘io.spring.dependency-management’bootJar { baseName = ‘gs-relational-data-access’ version = ‘0.1.0’}repositories { mavenCentral()}sourceCompatibility = 1.8targetCompatibility = 1.8dependencies { compile(“org.springframework.boot:spring-boot-starter”) compile(“org.springframework:spring-jdbc”) compile(“com.h2database:h2”) testCompile(“junit:junit”)}在Spring Boot gradle plugin提供了许多便捷的功能:它收集类路径上的所有jar并构建一个可运行的“über-jar”,这使得执行和传输服务更加方便。它搜索public static void main()标记为可运行类的方法。它提供了一个内置的依赖项解析器,它设置版本号以匹配Spring Boot依赖项。您可以覆盖任何您希望的版本,但它将默认为Boot的所选版本集。使用Maven构建首先,设置一个基本的构建脚本。在使用Spring构建应用程序时,您可以使用任何您喜欢的构建系统,但此处包含了使用Maven所需的代码。如果您不熟悉Maven,请参阅使用Maven构建Java项目。创建目录结构在您选择的项目目录中,创建以下子目录结构;例如,在nix系统上使用mkdir -p src/main/java/hello:└── src └── main └── java └── hellopom.xml<?xml version=“1.0” encoding=“UTF-8”?><project xmlns=“http://maven.apache.org/POM/4.0.0" xmlns:xsi=“http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation=“http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>org.springframework</groupId> <artifactId>gs-relational-data-access</artifactId> <version>0.1.0</version> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.0.3.RELEASE</version> </parent> <properties> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc</artifactId> </dependency> <dependency> <groupId>com.h2database</groupId> <artifactId>h2</artifactId> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build></project>在Spring Boot gradle plugin提供了许多便捷的功能:它收集类路径上的所有jar并构建一个可运行的“über-jar”,这使得执行和传输服务更加方便。它搜索public static void main()标记为可运行类的方法。它提供了一个内置的依赖项解析器,它设置版本号以匹配Spring Boot依赖项。您可以覆盖任何您希望的版本,但它将默认为Boot的所选版本集。使用IDE构建阅读如何将本指南直接导入使用STS构建/导入入门指南。阅读使用IntelliJ IDEA导入入门指南中如何使用本指南。创建一个Customer对象您将在下面使用的简单数据访问逻辑管理客户的名字和姓氏。要在应用程序级别表示此数据,请创建一个Customer类。package hello;public class Customer { private long id; private String firstName, lastName; public Customer(long id, String firstName, String lastName) { this.id = id; this.firstName = firstName; this.lastName = lastName; } @Override public String toString() { return String.format( “Customer[id=%d, firstName=’%s’, lastName=’%s’]”, id, firstName, lastName); } // getters & setters omitted for brevity}存储和检索数据Spring提供了一个名为JdbcTemplate的模板类,可以轻松使用SQL关系数据库和JDBC。大多数JDBC代码都陷入资源获取,连接管理,异常处理和一般错误检查之中,这与代码要实现的内容完全无关,JdbcTemplate负责这一切。您所要做的就是专注于手头的任务。src/main/java/hello/Application.javapackage hello;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.boot.CommandLineRunner;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;import org.springframework.jdbc.core.JdbcTemplate;import java.util.Arrays;import java.util.List;import java.util.stream.Collectors;@SpringBootApplicationpublic class Application implements CommandLineRunner { private static final Logger log = LoggerFactory.getLogger(Application.class); public static void main(String args[]) { SpringApplication.run(Application.class, args); } @Autowired JdbcTemplate jdbcTemplate; @Override public void run(String… strings) throws Exception { log.info(“Creating tables”); jdbcTemplate.execute(“DROP TABLE customers IF EXISTS”); jdbcTemplate.execute(“CREATE TABLE customers(” + “id SERIAL, first_name VARCHAR(255), last_name VARCHAR(255))”); // Split up the array of whole names into an array of first/last names List<Object[]> splitUpNames = Arrays.asList(“John Woo”, “Jeff Dean”, “Josh Bloch”, “Josh Long”).stream() .map(name -> name.split(” “)) .collect(Collectors.toList()); // Use a Java 8 stream to print out each tuple of the list splitUpNames.forEach(name -> log.info(String.format(“Inserting customer record for %s %s”, name[0], name[1]))); // Uses JdbcTemplate’s batchUpdate operation to bulk load data jdbcTemplate.batchUpdate(“INSERT INTO customers(first_name, last_name) VALUES (?,?)”, splitUpNames); log.info(“Querying for customer records where first_name = ‘Josh’:”); jdbcTemplate.query( “SELECT id, first_name, last_name FROM customers WHERE first_name = ?”, new Object[] { “Josh” }, (rs, rowNum) -> new Customer(rs.getLong(“id”), rs.getString(“first_name”), rs.getString(“last_name”)) ).forEach(customer -> log.info(customer.toString())); }}@SpringBootApplication 是一个便利注释,添加了以下所有内容:@Configuration 标记该类作为应用程序上下文的bean定义的源。@EnableAutoConfiguration 告诉Spring Boot开始根据类路径设置,其他bean和各种属性设置添加bean。@ComponentScan告诉Spring在包中寻找其他组件,配置和服务,允许它找到控制器。main()方法使用Spring Boot的SpringApplication.run()方法来启动应用程序。您是否注意到没有一行XML?也没有web.xml文件。此Web应用程序是100%纯Java,您无需处理配置任何管道或基础结构。main()方法使用Spring Boot的SpringApplication.run()方法来启动应用程序。您是否注意到没有一行XML?也没有web.xml文件。此Web应用程序是100%纯Java,您无需处理配置任何管道或基础结构。Spring Boot支持H2,一种内存中的关系数据库引擎,并自动创建连接。因为我们使用的是spring-jdbc,Spring Boot会自动创建一个JdbcTemplate。@Autowired自动加载JdbcTemplate并使其可用。这个Application类实现了Spring Boot CommandLineRunner,这意味着它将run()在加载应用程序上下文后执行该方法。首先,使用JdbcTemplate’s execute方法安装一些DDL 。其次,您获取字符串列表并使用Java 8流,将它们拆分为Java数组中的firstname/lastname对。然后使用JdbcTemplate’s batchUpdate方法在新创建的表中安装一些记录。方法调用的第一个参数是查询字符串,最后一个参数(Objects 的数组)包含要替换为“?”字符的查询的变量。对于单个插入语句,JdbcTemplate’s `insert方法很好。但对于多个,最好使用batchUpdate。使用?的参数,以避免SQL注入攻击通过指示JDBC来绑定变量。最后,使用query方法在表中搜索与条件匹配的记录。您再次使用“?”参数为查询创建参数,在进行调用时传入实际值。最后一个参数是用于将每个结果行转换为新Customer对象的Java 8 lambda 。Java 8 lambdas很好地映射到单个方法接口,如Spring的RowMapper。如果您使用的是Java 7或更早版本,则可以轻松插入匿名接口实现,并具有与lambda expresion正文所包含的相同的方法体,并且它可以毫不费力地使用Spring。构建可执行的JAR您可以使用Gradle或Maven从命令行运行该应用程序。或者,您可以构建一个包含所有必需依赖项,类和资源的可执行JAR文件,并运行该文件。这使得在整个开发生命周期中,跨不同环境等将服务作为应用程序发布,版本和部署变得容易。如果您使用的是Gradle,则可以使用运行该应用程序./gradlew bootRun。或者您可以使用构建JAR文件./gradlew build。然后你可以运行JAR文件:java -jar build/libs/gs-relational-data-access-0.1.0.jar如果您使用的是Maven,则可以使用该应用程序运行该应用程序./mvnw spring-boot:run。或者您可以使用构建JAR文件./mvnw clean package。然后你可以运行JAR文件:java -jar target/gs-relational-data-access-0.1.0.jar上面的过程将创建一个可运行的JAR。您也可以选择构建经典WAR文件。显示日志输出,您可以从日志中看到它在后台线程上。您应该每隔5秒钟看到计划任务:2015-06-19 10:58:31.152 INFO 67731 — [ main] hello.Application : Creating tables2015-06-19 10:58:31.219 INFO 67731 — [ main] hello.Application : Inserting customer record for John Woo2015-06-19 10:58:31.220 INFO 67731 — [ main] hello.Application : Inserting customer record for Jeff Dean2015-06-19 10:58:31.220 INFO 67731 — [ main] hello.Application : Inserting customer record for Josh Bloch2015-06-19 10:58:31.220 INFO 67731 — [ main] hello.Application : Inserting customer record for Josh Long2015-06-19 10:58:31.230 INFO 67731 — [ main] hello.Application : Querying for customer records where first_name = ‘Josh’:2015-06-19 10:58:31.242 INFO 67731 — [ main] hello.Application : Customer[id=3, firstName=‘Josh’, lastName=‘Bloch’]2015-06-19 10:58:31.242 INFO 67731 — [ main] hello.Application : Customer[id=4, firstName=‘Josh’, lastName=‘Long’]2015-06-19 10:58:31.244 INFO 67731 — [ main] hello.Application : Started Application in 1.693 seconds (JVM running for 2.054)概要恭喜!您刚刚使用Spring开发了一个简单的JDBC客户端。 ...

January 23, 2019 · 3 min · jiezi

Mybatis从入门到精通——从JDBC编程开始

[toc]JDBC是什么 (Java DataBase Connectivity)jdbc是一种Java编程语言和各种数据库之间数据库无关连接的行业标准,JDBC API为基于SQL的数据库访问提供了调用级API数据库无关在没有JDBC之前,我们需要编写不同的程序对接不同厂商的数据库系统,像下图所示,需要针对不同的数据库api编程,可想而知,当我们需要更换数据库系统而产生的大量重复工作,增加不必要的开发成本而JDBC的出现,统一了Java程序访问不同数据库系统的api,应用程序通过调用JDBC来操作数据库时,实际上是右数据库系统厂商提供的JDBC驱动程序来完成的。这样一来,即使要更换数据库系统,也仅仅是更换相应的驱动程序就可以了。(本文使用mysql的驱动程序)JDBC API主要完成以下三个工作:建立与数据库的连接或访问任何表格数据源发送SQL语句到数据库处理数据返回的结果这三个工作中都有其对应的jdbc api来完成各自的任务。应用程序可以使用这些api 来操作数据库系统了JDBC APIDriverManager管理JDBC驱动的服务类,主要功能是获取Connection对象(示例程序中的第2步)getConnection方法Connection getConnection(url,username,password)url写法:jdbc:mysql://localhost:3306/mydb本地数据库简写:jdbc:mysql:///mydbjdbc:协议mysql:子协议localhost:主机名3306:端口号mydb:数据库名称Connection数据库连接对象,每个Connection对象表示一个连接会话。Connection的常用方法1. 返回Statement对象Statement createStatement()PreparedStatement PreparedStatement(String sql):Statement的子类,将SQL语句提交到数据库进行预编译CallableStatement prepareCall(String sql):Statement的子类,处理存储过程2. 处理事务的常用方法void setAutoCommit(boolean autoCommit):设置是否自动提交,默认truevoid commit:提交事务void rollback:事务回滚Savepoint setSavepoint:创建一个保存点Savepoint setSavepoint(Stirng name):指定名称来创建一个保存点void rollback(Savepoint savepoint):将事务回滚到指定的保存点void setTransactionIsolation(int level):设置事务隔离级别Statement执行具体的SQL语句,以及执行DDL、DCL、DML语句。Statement子类 PreparedStatement预编译的Statement对象SQL语句一次编译多次执行允许数据库预编译SQL语句(常带有参数或者叫占位符),这样一来,以后每次只需要改变SQL命令的参数,而不需要每次都编译SQL语句,性能得到了提升PreparedStatement主要方法void setXXX(int parmIndex,XXX value):设置预编译语句的参数Statement常用方法ResultSet executeQuery(String sql):只能执行查询语句,返回ResultSetint executeUpdate(String sql):主要用于执行DML语句的,返回受影响行数。boolean execute(String sql):执行任何SQL语句addBatch(String sql):添加到批处理executeBatch():执行批处理clearBatch():清空批处理ResultSet结果集:查询结果的封装ResultSet主要方法移动指针的方法next():移动指针到ResultSet记录的下一行,如果存在该条记录返回true…….获取当前行、指定列的值getInt()、getString()针对不同数据类型的方法,通用的两个泛型方法<T> T getObject(int columnIndex,Class<T> type)、<T> T getObject(String columnLabel,Class<T> type)JDBC实际操作建库建表的SQL语句– 建库CREATE DATABASE mydb CHARACTER SET utf8 COLLATE utf8_general_ci;– 建表CREATE TABLE IF NOT EXISTS user( id INT UNSIGNED AUTO_INCREMENT, username VARCHAR(100) NOT NULL, password VARCHAR(40) NOT NULL, name VARCHAR(40) NOT NULL, PRIMARY KEY ( id ))ENGINE=InnoDB DEFAULT CHARSET=utf8;示例程序框架我们会在业务逻辑代码start的地方开始具体的执行操作public void excute() throws SQLException { Connection conn = null; PreparedStatement smt = null; ResultSet resultset = null; try { //1、加载驱动 Class.forName(“com.mysql.jdbc.Driver”); //2、获取Connection对象 conn = DriverManager.getConnection(“jdbc:mysql://localhost:3306/mydb?characterEncoding=utf-8”, “root”, “******”); if (conn != null) { System.out.println(“连接成功”); } conn.setAutoCommit(false); //业务逻辑代码start //插入数据操作 String insertSql = “insert into user(username,password,name) values(?,?,?)”; smt = conn.prepareStatement(insertSql); smt.setString(1, “xiaoming”); smt.setString(2, “123”); smt.setString(3, “小明”); int result = smt.executeUpdate(); if (result > 0) { System.out.println(“添加成功”); } smt.clearParameters(); //修改数据操作 String updateSql = “update user set name=? where id=?”; smt = conn.prepareStatement(updateSql); smt.setString(1, “小牛”); smt.setInt(2, 2); int updateResult = smt.executeUpdate(); if (updateResult > 0) { System.out.println(“修改成功”); } smt.clearParameters(); //查询数据操作 String sql = “select *from user where id =?”; smt= conn.prepareStatement(sql); smt.setInt(1,3); resultset = smt.executeQuery(); while (resultset.next()) { int uid = resultset.getInt(“id”); System.out.println(String.format(“id:%s,username:%s,password:%s,name:%s”, uid, resultset.getString(“username”), resultset.getString(“password”), resultset.getString(“name”))); } //业务逻辑代码end conn.commit(); } catch (Exception e) { System.out.println(e); conn.rollback(); } //4、释放资源 写到finally里面,防止报错不能执行资源回收 finally { if (conn != null) { try { conn.close(); } catch (SQLException e) { e.printStackTrace(); } } if (smt != null) { try { smt.close(); } catch (SQLException e) { e.printStackTrace(); } } if (resultset != null) { try { resultset.close(); } catch (SQLException e) { e.printStackTrace(); } } } }执行结果连接成功添加成功修改成功id:3,username:xiaoming,password:123,name:小明业务逻辑代码中如果有错误,连接对象将执行回滚操作,保证数据的一致性。 ...

December 27, 2018 · 2 min · jiezi

“分库分表 ?选型和流程要慎重,否则会失控

更多文章关注微信公众号《小姐姐味道》 https://mp.weixin.qq.com/s?__…数据库中间件之分库分表恭喜你,贵公司终于成长到一定规模,需要考虑高可用,甚至分库分表了。但你是否知道分库分表需要哪些要素?拆分过程是复杂的,提前计划,不要等真正开工,各种意外的工作接踵而至,以至失控。本文意图打开数据库中间件的广度,而不考虑实现深度,至于库表垂直和水平分的概念和缘由,不做过多解释。所以此文面向的是有一定研发经验,正在寻找选型和拆分流程的专业人士。切入层次以下,范围界定在JAVA和MySQL中。我们首先来看一下分库分表切入的层次。① 编码层在同一个项目中创建多个数据源,采用if else的方式,直接根据条件在代码中路由。Spring中有动态切换数据源的抽象类,具体参见AbstractRoutingDataSource。如果项目不是很庞大,使用这种方式能够快速的进行分库。但缺点也是显而易见的,需要编写大量的代码,照顾到每个分支。当涉及跨库查询、聚合,需要循环计算结果并合并的场景,工作量巨大。如果项目裂变,此类代码大多不能共用,大多通过拷贝共享。长此以往,码将不码。② 框架层这种情况适合公司ORM框架统一的情况,但在很多情况下不太现实。主要是修改或增强现有ORM框架的功能,在SQL中增加一些自定义原语或者hint来实现。通过实现一些拦截器(比如Mybatis的Interceptor接口),增加一些自定义解析来控制数据的流向,效果虽然较好,但会改变一些现有的编程经验。很多情况要修改框架源码,不推荐。③ 驱动层基于在编码层和框架层切入的各种缺点,真正的数据库中间件起码要从驱动层开始。什么意思呢?其实就是重新编写了一个JDBC的驱动,在内存中维护一个路由列表,然后将请求转发到真正的数据库连接中。像TDDL、ShardingJDBC等,都是在此层切入。包括Mysql Connector/J的Failover协议(具体指“load balancing”、“replication”、“farbic”等),也是直接在驱动上进行修改。请求流向一般是这样的:④ 代理层代理层的数据库中间件,将自己伪装成一个数据库,接受业务端的链接。然后负载业务端的请求,解析或者转发到真正的数据库中。像MySQL Router、MyCat等,都是在此层切入。请求流向一般是这样的:⑤ 实现层SQL特殊版本支持,如Mysql cluster本身就支持各种特性,mariadb galera cluster支持对等双主,Greenplum支持分片等。需要换存储,一般是解决方案,就不在讨论之列了。技术最终都会趋于一致,选择任何一种、都是可行的。但最终选型,受开发人员熟悉度、社区活跃度、公司切合度、官方维护度、扩展性,以及公司现有的数据库产品等多方位因素影响。选择或开发一款合适的,小伙伴们会幸福很多。驱动层和代理层对比通过以上层次描述,很明显,我们选择或开发中间件,就集中在驱动层和代理层。在这两层,能够对数据库连接和路由进行更强的控制和更细致的管理。但它们的区别也是明显的。驱动层特点仅支持JAVA,支持丰富的DB驱动层中间件仅支持Java一种开发语言,但支持所有后端关系型数据库。如果你的开发语言固定,后端数据源类型丰富,推荐使用此方案。占用较多的数据库连接驱动层中间件要维护很多数据库连接。比如一个分了10个 库 的表,每个java中的Connection要维护10个数据库连接。如果项目过多,则会出现连接爆炸(我们算一下,如果每个项目6个实例,连接池中minIdle等于5,3个项目的连接总数是 1065*3 = 900 个)。像Postgres这种每个连接对应一个进程的数据库,压力会很大。数据聚合在业务实例执行数据聚合,比如count sum等,是通过多次查询,然后在业务实例的内存中进行聚合。路由表存在于业务方实例内存中,通过轮询或者被动通知的途径更新路由表即可。集中式管理所有集群的配置管理都集中在一个地方,运维负担小,DBA即可完成相关操作。典型实现代理层特点异构支持,DB支持有限代理层中间件正好相反。仅支持一种后端关系型数据库,但支持多种开发语言。如果你的系统是异构的,并且都有同样的SLA要求,则推荐使用此方案。运维负担大代理层需要维护数据库连接数量有限(MySQL Router那种粘性连接除外)。但作为一个独立的服务,既要考虑单独部署,又要考虑高可用,会增加很多额外节点,更别提用了影子节点的公司了。另外,代理层是请求唯一的入口,稳定性要求极高,一旦有高耗内存的聚合查询把节点搞崩溃了,都是灾难性的事故。典型实现共同点篇幅有限,不做过多讨论。访问各中间件宣传页面,能够看到长长的Feature列表,也就是白名单;也能看到长长的限制列表,也就是黑名单。限定了你怎么玩,在增强了分布式能力后,分库分表本身就是一个阉割的数据库。使用限制确保数据均衡 拆分数据库的数据尽量均匀,比如按省份分user库不均匀,按userid取模会比较均匀不用深分页 不带切分键的深分页,会取出所有库所取页数之前的所有数据在内存排序计算。容易造成内存溢出。减少子查询 子查询会造成SQL解析紊乱,解析错误的情况,尽量减少SQL的子查询。事务最小原则 尽量缩小单机事务涉及的库范围,即尽可能减少夸库操作,将同类操作的库/表分在一起数据均衡原则 拆分数据库的数据尽量均匀,比如按省份分user库不均匀,按userid取模会比较均匀特殊函数 distinct、having、union、in、or等,一般不被支持。或者被支持,使用之后会增加风险,需要改造。产品建议聚焦在MyCat和ShardingJDBC上。另外,还有大量其他的中间件,不熟悉建议不要妄动。数据库中间件不好维护,你会发现大量半死不活的项目。以下列表,排名不分先后,有几个是只有HA功能,没有拆分功能的:Atlas、Kingshard、DBProxy、mysql router、MaxScale、58 Oceanus、ArkProxy、Ctrip DAL、Tsharding、Youtube vitess、网易DDB、Heisenberg、proxysql、Mango、DDAL、Datahekr、MTAtlas、MTDDL、Zebra、Cobar、Cobar汗、几乎每个大厂都有自己的数据库中间件(还发现了几个喜欢拿开源组件加公司前缀作为产品的),只不过不给咱用罢了。流程解决方案无论是采用哪个层面切入进行分库分表,都面临以下工作过程。信息收集统计影响的业务和项目项目范围越大,分库难度越高。有时候,一句复杂的SQL能够涉及四五个业务方,这种SQL都是需要重点关注的。确定分库分表的规模,是只分其中的几张表,还是全部涉及。分的越多,工作量越大,几乎是线性的。还有一些项目是牵一发动全身的。举个例子,下面这个过程,影响的链路就不仅是分库这么简单了。确定参与人员除了分库分表组件的技术支持人员,最应该参与的是对系统、对现有代码最熟悉的几个人。只有他们能够确定哪些SQL该废弃掉、SQL的影响面等。确定分库分表策略确定分库分表的维度和切分键。切分键(就是路由数据的column)一旦确定,是不允许修改的,所以在前期架构设计上,应该首先将其确立下来,才能进行后续的工作;数据维度多意味着有不同的切分键,达到不同条件查询的效果。这涉及到数据的冗余(多写、数据同步),会更加复杂。前期准备数据规整库表结构不满足需求,需要提前规整。比如,切分键的字段名称不同或者类型各异。在实施分库分表策略时,这些个性会造成策略过大不好维护。扫描所有SQL将项目中所有的SQL扫描出来,逐个判断是否能够按照切分键正常运行。在判断过程中肯定会有大量不合规的SQL,则都需要给出改造方案,这是主要的工作量之一。验证工具支持直接在原有项目上进行改动和验证是可行的,但会遇到诸多问题,主要是效率太低。我倾向于首先设计一些验证工具,输入要验证的SQL或者列表,然后打印路由信息和结果进行判断。技术准备建议以下提到的各个点,都找一个例子体验一下,然后根据自己的团队预估难度。以下:中间件所有不支持的SQL类型整理容易造成崩溃的注意事项不支持的SQL给出处理方式考虑一个通用的主键生成器考虑没有切分键的SQL如何处理考虑定时任务等扫全库的如何进行遍历考虑跨库跨表查询如何改造准备一些工具集实施阶段数据迁移分库分表会重新影响数据的分布,无论是全量还是增量,都会涉及到数据迁移,所以Databus是必要的。一种理想的状态是所有的增删改都是消息,可以通过订阅MQ进行双写。但一般情况下,仍然需要去模拟这个状态,比如使用Canal组件。怎么保证数据安全的切换,我们分其他章节进行讨论。充足的测试分库分表必须经过充足的测试,每一句SQL都要经过严格的验证。如果有单元测试或者自动化测试工具,完全的覆盖是必要的。一旦有数据进行了错误的路由,尤其是增删改,将会创造大量的麻烦。在测试阶段,将验证过程输出到单独的日志文件,充足测试后review日志文件是否有错误的数据流向。SQL复验强烈建议统一进行一次SQL复验。主要是根据功能描述,确定SQL的正确性,也就是通常说的review。演练在非线上环境多次对方案进行演练,确保万无一失。制定新的SQL规范分库分表以后,项目中的SQL就加了枷锁,不能够随意书写了。很多平常支持的操作,在拆分环境下就可能运行不了了。所以在上线前,涉及的SQL都应该有一个确认过程,即使已经经过了充足的测试。题外话没有支持的活别接,干不成。分库分表是战略性的技术方案,很多情况无法回退或者回退方案复杂。如果要拆分的库表涉及多个业务方,公司技术人员复杂,CTO要亲自挂帅进行协调,并有专业仔细的架构师进行监督。没有授权的协调人员会陷入尴尬的境地,导致流程失控项目难产。真正经历过的人,会知道它的痛!

December 5, 2018 · 1 min · jiezi

JDBC 4.2 Specifications 中文翻译 -- 第十一章 连接池

在基本的 DataSource 实现中,客户端的 Connection 对象与物理数据库连接有着1:1的关系。当 Connection 被关闭以后,物理连接也会被关闭。因此,连接的频繁打开、初始化以及关闭,会在一个客户端会话中上演多次,带来了过重的性能消耗。而连接池就能解决这个问题,连接池维护了一系列物理数据库连接的缓存,可以被多个客户端会话重复使用。连接池能够极大地提高性能和可扩展性,特别是在一个三层架构的环境中,大量的客户端可以共享一个数量比较小的物理数据库连接池。在图11-1中,JDBC 驱动提供了一个 ConnectionPoolDataSource 的实现,应用服务器可以用它来创建和管理连接池。连接池的管理策略跟具体的实现有关,也跟具体的应用服务器有关。应用服务器对客户端提供了一个 DataSource 接口的具体实现,使得连接池化对于客户端来说是透明的。最终,客户端使用 DataSource API 就能和之前使用 JNDI 一样,获得了更好的性能和可扩展性。下文将会介绍 ConnectionPoolDataSource 接口、PooledConnection 接口以及 ConnectionEvent 类,这三个组成部分是一个相互合作的关系,下文将以一个经典线程池的实现的角度,逐步描述这几部分。这一章也会介绍基本的 DataSource 对象和池化的 DataSource 对象之间的区别,此外,还会讨论一个池化的连接如何能够维护一堆可重用的 PreparedStatement 对象。尽管本章中的所有讨论都是假设在三层架构环境下的,但连接的池化在两层架构的环境下也同样有用。在两层架构的环境中,JDBC 驱动既实现了 DataSource 接口,也实现 ConnectionPoolDataSource 接口,这种实现方式允许客户端打开或者关闭多个连接。11.1 ConnectionPoolDataSource 和 PooledConnection一般来说, 一个 JDBC 驱动会去实现 ConnectionPoolDataSource 接口,应用服务器可以使用这个接口来获得 PooledConnection 对象,以下代码展示了 getPooledConnection 方法的两种版本public interface ConnectionPoolDataSource { PooledConnection getPooledConnection() throws SQLException; PooledConnection getPooledConnection(String user, String password) throws SQLException;}一个 PooledConnection 对象代表一条与数据源之间的物理连接。JDBC 驱动对于 PooledConnection 的实现,则会封装所有与维护这条连接相关的细节。应用服务器则会在它的 DataSource 接口的实现中,缓存和重用这些 PooledConnection。当客户端调用 DataSource.getConnection 方法时,应用服务器将会使用物理 PooledConnection 去获取一个逻辑 Connection 对象。以下代码是 PooledConnection 接口的一些方法定义:public interface PooledConnection { Connection getConnection() throws SQLException; void close() throws SQLException; void addConnectionEventListener( ConnectionEventListener listener); void addStatementEventListener( StatementEventListener listener); void removeConnectionEventListener( ConnectionEventListener listener); void removeStatementEventListener( StatementEventListener listener);}当客户端使用完连接以后,它使用 Connection.close 方法来关闭这条逻辑连接,这个动作只是关闭了逻辑连接,但并不会关闭物理连接。物理连接会被归还到池子里,以待重用。在这里,连接的池化对于客户端来说完全是透明的,客户端能像使用非池化连接那样去使用池化连接。需要注意的是,当对池化的连接调用 Connection.close() 方法时,之前通过 Connection.setClientInfo 设置的属性将会被清除掉。11.2 连接事件回忆先前说过的,当 Connection.close 方法被调用后,底层的物理连接 PooledConnection 就可以再次被重用。当一个 PooledConnection 可以被回收的时候,将会使用 JavaBean 风格的事件去通知连接池管理器(应用服务器)。为了发生连接事件时能被通知到,连接池管理器必须实现 ConnectionEventListener 接口,然后 PooledConnection 会将其注册为连接事件的一个监听者。ConnectionEventListener 接口定义了两个方法,也体现出了可能发生的两种不同的事件:connectionClosed 事件 — 当逻辑连接 Connection.close 被调用时产生此事件connectionErrorOccurred — 当出现一些致命的错误,比如说数据库宕机导致连接丢失的时候,会触发这个事件连接池管理器通过调用 PooledConnection.addConnectionEventListener 方法来将自己注册为一个 PooledConnection 的监听者。一般情况下,注册的动作都发生在将连接归还到池子里之前。JDBC 驱动负责在对应的事件发生的时候,调用回调方法,这两个方法都需要一个 ConnectionEvent 对象作为参数,通过这个对象可以判断到底是哪个 PooledConnection 被关闭了或者发生了错误。当客户端关闭了逻辑连接的时候,JDBC 驱动会通过调用监听者所实现的 connectionClosed 方法来通知监听者,此时,监听者(连接池管理器)可以将该连接归还到池子里以便重用。当致命性错误发生时,JDBC 驱动首先会调用监听者实现的 connectionErrorOccurred 方法,然后再抛出一个 SQLException 异常。这个时候,监听者就可以通过 PooledConnection.close 方法来将物理连接关闭。11.3 三层架构环境中的连接池化以下步骤列出了客户端使用连接池池化时,实际上发生的事情:客户端调用 DataSource.getConnection 方法应用服务器在它自己支持连接池的 DataSource 实现中,查找是否有可用的 PooledConnection 对象如果没有可用的 PooledConnection 对象,应用服务器调用 ConnectionPoolDataSource.getPooledConnection 来创建一条物理连接,JDBC 驱动的具体实现会负责连接创建的具体细节,并把它交给应用服务器管理。无论是新建的 PooledConnection 还是已经创建好的处于可用状态的,应用服务器会对这条连接进行一些标识,标记它处于正在使用的状态。应用服务器调用 PooledConnection.getConnection 方法来获得一个逻辑上的 Connection 对象,这个对象底层实际上关联了一个物理的 PooledConnection 对象,客户端调用 DataSource.getConnection 方法,返回值拿到的是逻辑上的 Connection 对象。应用服务器通过调用 PooledConnection.addConnectionEventListener 方法将它自己注册为一个 ConnectionEventListener,当 PooledConnection 处于可用状态时,应用服务器就会得到相应的事件通知。由 DataSource.getConnection 方法返回的逻辑 Connection 对象,依然是使用 Connection API,直到 Connection.close 被调用之前,底层的 PooledConnection 都处于使用状态,不可被重用。即使在没有应用服务器的两层架构环境中,连接依然可以做到池化。这种情况下,JDBC 驱动需要实现 DataSource 接口和 ConnectionPoolDataSource 接口。11.4 DataSource 实现与连接池化抛开对性能和扩展性的提升不说,客户端使用 DataSource 接口的时候,不需要去关心它底层的实现是否池化,客户端面向的是一套统一的,无差别的使用方式。常规的 DataSource 实现,即不实现连接池化功能的实现,一般由 JDBC 驱动实现,通常有以下两个观点被认为是正确的:DataSource.getConnection 方法创建一个新的 Connection 对象来代表一条真正的物理连接,并且封装了所有维护和管理这条物理连接的细节。Connection.close 方法关闭底层的物理连接并释放相关的资源在一个实现了池化的 DataSource 实现中,情况则有些不一样,以下几个观点被认为是正确的:在 DataSource 的实现中,包含了一个提供了连接池化功能的模块,这个模块要怎么实现没有一个统一的标准,因人而异。这个模块会缓存一系列 PooledConnection 对象。DataSource 的实现类,通常处于驱动实现的 ConnectionPoolDataSource 和 PooledConnection 接口的上层。DataSource.getConnection 方法会调用 PooledConnection 方法去获得对底层物理连接的一个句柄,如果已有的连接池里没有现成可用的连接,那么这个时候就需要新建物理连接,只有在这种情况下,新建物理连接对性能的消耗才体现出来。当需要创建新的物理连接的时候,ConnectionPoolDataSource 的 getPooledConnection 会被调用,对于物理连接的管理细节,则委托给了 PooledConnection 对象。Connection.close 方法被调用时,只是关闭逻辑上的连接句柄,并不会关闭实际上的物理连接。连接池管理者此时会收到一个事件通知,被告知一个 PooledConnection 处于可重用状态了。如果此时客户端仍然企图使用这个逻辑上的连接句柄,那么只会得到一个 SQLException 异常。一个物理 PooledConnection 在它的整个生命周期中,可能会产生许多的逻辑 Connection 对象,但只有最近一次产生的 Connection 对象才是有效的,当 PooledConnection.getConnection 方法被调用时,先前已经存在的 Connection 对象,将会被自动关闭。这种情况下,关闭不会产生相应的事件去通知监听者。这给了应用服务器一种从客户端强行拿走连接的方式,这种情形可能很少见,但是当应用服务器需要进行强制关闭时,这个特性可能会很有用连接池的管理者通过调用 PooledConnection.close 方法来关闭物理连接,一般发生以下情况时,才会这么做:当应用服务器正常退出时,当需要重新初始化连接的缓存时,或者是该连接上发生一些不可恢复的致命性错误时。11.5 部署进行连接池化的部署,需要提供一个客户端代码可以接触到的 DataSource 对象,并且还需要把一个 ConnectionPoolDataSource 对象注册到 JNDI 中。第一步,部署 ConnectionPoolDataSource,如下代码所示:// ConnectionPoolDS implements the ConnectionPoolDataSource// interface. Create an instance and set properties.com.acme.jdbc.ConnectionPoolDS cpds = new com.acme.jdbc.ConnectionPoolDS();cpds.setServerName(“bookserver”);cpds.setDatabaseName(“booklist”);cpds.setPortNumber(9040);cpds.setDescription(“Connection pooling for bookserver”);// Register the ConnectionPoolDS with JNDI, using the logical name// “jdbc/pool/bookserver_pool”Context ctx = new InitialContext();ctx.bind(“jdbc/pool/bookserver_pool”, cpds); 上述步骤做好以后,ConnectionPoolDataSource 对象就可以被对客户端代码可见的 DataSource 使用了,DataSource 的部署需要依赖于先前部署的 ConnectionPoolDataSource,如下代码所示:// PooledDataSource implements the DataSource interface.// Create an instance and set properties.com.acme.appserver.PooledDataSource ds =new com.acme.appserver.PooledDataSource();ds.setDescription(“Datasource with connection pooling”);// Reference the previously registered ConnectionPoolDataSourceds.setDataSourceName(“jdbc/pool/bookserver_pool”);// Register the DataSource implementation with JNDI, using the logical// name “jdbc/bookserver”.Context ctx = new InitialContext();ctx.bind(“jdbc/bookserver”, ds);到此,客户端代码就可以使用这个 DataSource 了。11.6 池化连接的 Statement 重用JDBC 规范对于 statement 的池化也提供了一些支持。statement 池化这个特性,能让应用层像 connection 重用一样,对 PreparedStatement 进行重用,这个特性需要以连接池化为基础。下图展示了 PooledConnection 与 PreparedStament 之间的关系。逻辑 Connection 可以透明地使用多个 PreparedStatement 对象。上图中,连接池和 statement 池由应用服务器来实现。不过,这些功能其实也可以由驱动来实现,或者是数据源来实现。这里我们对于 statement 池化的讨论,其实是适用于以上提到的所有实现方式的。11.6.1 使用池化 Statement对于 statement 的重用,必须对应用透明。也就是说,从应用开发的角度,对一个 statement 的使用,不需要关心它是否是池化的实现。statement 在底层会一直保持处于打开状态,应用层的代码也不需要改变。如果应用层关闭了这个 statement,它依然需要调用 Connection.prepareStatement 方法来继续使用它。statement 的池化对于应用层来说,使用方式上是透明的,应用层唯一能感知到不同的,是它带来的明显的性能提升。应用层需要通过调用 DatabaseMetadata 的 supportStatementPooling 方法,来判断一个数据源是否支持 statement 重用。在很多情况下,对于 statement 的重用,是一种非常有意义的优化,尤其是负责的 prepared statement。不过,需要注意的是,大量的 statement 处于打开状态,有可能会对资源带来影响。11.6.2 关闭池化 Statement一旦应用层关闭了一个 statement,无论它是否是池化的,它都不能再继续被使用了,否则会导致异常抛出。以下几个方法会关闭一个池化的 statement:Statement.close — 由应用层调用。如果一个 statement 是池化的,调用这个方法会关闭逻辑上的 statement,但不会关闭底层的已经池化的物理 statement。Connection.close — 由应用层调用。非池化连接 — 关闭底层的物理连接和由这个连接创建的所有 statement。这样做是必要的,因为垃圾回收机制无法检测到外部的资源什么时候会被释放。池化连接 — 仅关闭逻辑上的连接和这个连接所创建的逻辑 statement,底层的物理连接以及相关的 statement 不会被关闭。PooledConnection.close — 由连接池管理者调用。会关闭底层的物理连接以及所有相关的 statement。应用层无法直接关闭一个已经池化的物理 statement,这是连接池管理器做的事情。PooledConnection.close 方法关闭物理连接以及所有的关联 statement,释放掉相关的资源。应用层也无法直接控制 statement 应该如何被池化。一个池化的 statement 总是与一个 PooledConnection 相关联的,ConnectionPoolDataSource 可以用来对池化做一些属性设置。11.7 statement 事件如果连接池管理器支持 statement 池化,它必须实现 StatementEventListener 接口,然后将自己注册为 PooledConnection 对象的监听者。这个接口定义了以下两个方法,用来监听有可能发生在一个 PreparedStatement 对象上的两种事件。statementClosed — 当与 PooledConnection 对象相关联的逻辑 PreparedStatement 对象被关闭时触发,也就是说,当应用层调用 PreparedStatement.close 方法时。statementErrorOccurred — 当 JDBC 驱动监测到 PreparedStatement 对象不可用时触发。连接池管理器通过 PooledConnection.addStatementEventListener 方法将自己注册为监听者。一般来说,在连接池管理器返回一个 PreparedStatement 对象给应用层使用之前,它必须先把自己注册为一个监听者。当对应的事件发生时,驱动会调用 StatementEventListener 的 statementClosed 方法和 statementErrorOccurred 方法,这两个方法都接收一个 statementEvent 对象作为参数,这个参数就可以用来判断是发生了关闭事件还是异常事件。当 JDBC 应用关闭逻辑 statement ,或者一些错误发生时,JDBC 驱动会调用相关的方法,这个时候,连接池管理器它就可以将这个 statement 放回池子以便重用,或者是抛出异常。11.8 ConnectionPoolDataSource 属性JDBC 的 API 定义了一系列的属性来设置与池化相关的属性:属性名类型描述maxStatementsint允许池化的最大 statement 数,0 代表不池化initialPoolSizeint当连接池创建时需要创建的初始物理连接数minPoolSizeint连接池最小物理连接数maxPoolSizeint连接池最大物理连接数,0代表无限制maxIdleTimeint连接空闲最大空闲时间,0代表无限制propertyCycleint属性生效时间,单位为秒连接池的配置风格遵循 JavaBean 风格。连接池厂商如果需要增加配置属性,那这些新增的属性名不应与已有的标准属性名重复。与 DataSource 的实现一样,ConnectionPoolDataSource 的实现也必须为每个属性增加 setter 和 getter 方法,以下代码是一个示例:VendorConnectionPoolDS vcp = new VendorConnectionPoolDS();vcp.setMaxStatements(25);vcp.setInitialPoolSize(10);vcp.setMinPoolSize(1);vcp.setMaxPoolSize(0);vcp.setMaxIdleTime(0);vcp.setPropertyCycle(300);应用服务器会根据设置的属性,来决定应该如何管理相关的池子。ConnectionPoolDataSource 的配置属性无须被 JDBC 客户端直接访问。一些管理工具需要访问的话,建议通过反射的方式。 ...

November 24, 2018 · 3 min · jiezi