JDBC
- JDBC(java database connectivity)是一个独立于特定数据库管理系统,通用的SQL数据库存取和操作的公共接口(一组API).定义了用来访问数据库的标准java类库,使用这些类库可以以一种标准的方法,方便地访问数据库资源.
- jdbc为访问不同的数据库提供了统一的途径,为开发者屏蔽了一些细节问题
- jdbc的目标是使java程序员使用jdbc可以连接任何提供了jdbc驱动程序的数据库系统,这样就使得程序员无需对特定的数据库系统的特点有过多的了解,从而大大简化和加快了开发过程
1.快速入门
1.0前置要求
-
//将jar驱动包导入到项目中,然后添加到项目
1.1注册驱动
-
Driver driver = new Driver(); //有异常直接抛出即可 //!!!需要将导入的driver驱动包路径改为com.mysql.cj.jdbc.Driver
1.2获取连接信息
-
//1.获取url连接信息 String url = "jdbc:mysql://localhost:3306/test?serverTimezone=UTC"; // jdbc:mysql://为固定部分 // localhost为操作数据库的地址名(localhost为本机) // :3306位端口号 // test为数据库名字 // serverTimezone=UTC为指定时区,UTC为世界标准时间
-
//2.获取数据库的账号密码,并且将账号和密码封装到properties对象里面 Properties properties = new Properties(); properties.setProperty("user","root"); properties.setProperty("password","123456"); //user和password为固定的,不能修改 //root为数据库的账号 //123456位数据库的密码
-
//3.连接 Connection connect = driver.connect(url,properties); //将返回的结果封装为对象 //driver为驱动包的名字 //url为连接信息 //properties为账号密码
1.3执行SQL语句
-
//1.定义SQL语句 String sql = "insert into test.actor values(null,'张三','男','2004-11-29','1111')"; //创建statement对象,并且连接数据库 Statement statement = connect.createStatement(); //执行SQL语句 //获取返回结果(行数),如果大于0就是执行成功,否则就是失败 int rows = statement.executeUpdate(sql); //判断是否执行成功 System.out.println(rows>0?"成功":"失败");
1.4释放资源
-
//关闭连接,释放资源 statement.close(); connect.close();
2.连接方式
2.1Driver
-
快速入门就是方式1
2.2反射
-
//1.利用反射获取驱动类信息 Class<?> Rclass = Class.forName("com.mysql.cj.jdbc.Driver"); //括号内为驱动类的路径 //有异常直接抛出即可 //2.获取驱动类 Driver driver = (Driver) Rclass.newInstance(); //有异常直接抛出即可
-
下面跟driver方式一样
-
//3.获取连接信息 //url连接信息 String url = "jdbc:mysql://localhost:3306/test?serverTimezone=UTC"; //账户密码 Properties properties = new Properties(); properties.setProperty("user","root"); properties.setProperty("password","123456"); //连接 Connection connect = driver.connect(url,properties); //有异常直接抛出即可 //4.执行SQL //SQL语句 String sql = "insert into test.actor values(null,'张三','男','2004-11-29','1111')"; Statement statement = connect.createStatement(); int rows = statement.executeUpdate(sql); System.out.println(rows>0?"成功":"失败"); //5.关闭连接 tatement.close(); connect.close();
-
好处:动态加载,更加灵活,减少依赖性
2.3DriverManager
-
///1.利用反射获取驱动类信息 Class<?> Rclass = Class.forName("com.mysql.cj.jdbc.Driver"); //有异常直接抛出即可 //2.获取驱动类 Driver driver = (Driver) Rclass.newInstance(); //有异常直接抛出即可 //3.获取连接信息 //创建url连接信息和账号密码 String url = "jdbc:mysql://localhost:3306/test?serverTimezone=UTC"; //url信息 String user = "root"; //数据库账户 String password = "123456"; //数据库密码 //4.注册drivermanager驱动并且连接 DriverManager.registerDriver(driver); //连接 Connection connection = DriverManager.getConnection(url,user,password); //将url,账户,密码信息都传入进来
2.4反射进阶版
-
//5.1.6版本后的驱动可以不写第一步 //1.使用反射加载driver类 Class.forName("com.mysql.cj.jdbc.Driver"); //有异常直接抛出即可 //2.获取连接信息 String url = "jdbc:mysql://localhost:3306/test?serverTimezone=UTC"; //url信息 String user = "root"; //数据库账户 String password = "123456"; //数据库密码 //3.连接 Connection connection = DriverManager.getConnection(url,user,password); //将url,账户,密码信息都传入进来
2.5反射进阶版的偷懒版
-
//1.在项目中新建一个文本,将连接信息放入进去 user=root password=123456 url=jdbc:mysql://localhost:3306/test?serverTimezone=UTC driver=com.mysql.cj.jdbc.Driver //不要有空格
-
//2.在main方法中获取连接信息 Properties properties = new Properties(); properties.load(new FileInputStream("src\\SqlInfromation"));//导入文本信息 String user = properties.getProperty("user");//账户 String password = properties.getProperty("password");//密码 String driver = properties.getProperty("driver");//驱动路径 String url = properties.getProperty("url");//url //2.连接 Connection connection = DriverManager.getConnection(url,user,password);
3.PreparedStatement
- 用于执行静态SQL语句并返回其生成的结果的对象.
- 在连接建立后,需要对数据库进行访问,执行命令(SQL语句)可以通过
- statement[存在SQL注入问题]
- SQL注入是利用某些系统没有对用户输入的数据进行充分的检查,而在用户输入数据中注入非法的SQL语句(命令),恶意攻击数据库.
- 防范SQL注入,用preparedstatement就行了
- preparedstatement[预处理]
- callablestatement[存储过程]
- statement[存在SQL注入问题]
3.1增删改
-
好处
- 不用拼接SQL语句,减少错误
- 可以解决SQL注入问题
- 大大减少了编译次数,效率较高
-
//1.preparedstatement执行的SQL语句中的参数用问号来表示,调用preparedstatement对象的setxxx()方法来设置这些参数 setxxx的参数1为SQL语句中的参数的索引(从1开始) setxxx的参数2为设置的SQL语句中的参数的值 //2.调用executequery(),返回resultset对象 //3.调用executeupdate(),执行更新,包括增删改
//增删改 //1.连接 Class<?> Rclass = Class.forName("com.mysql.cj.jdbc.Driver"); Driver driver = (Driver) Rclass.newInstance(); String url = "jdbc:mysql://localhost:3306/test?serverTimezone=UTC"; Properties properties = new Properties(); properties.setProperty("user","root"); properties.setProperty("password","123456"); Connection connect = driver.connect(url,properties); //2.预编译SQL语句 String sql = "update test.actor set name=? where id=?"; PreparedStatement ps = connect.prepareStatement(sql); //3.填充占位符 ps.setString(1,"李四"); //参数一从1开始,参数2为问号 ps.setInt(2,5); //4.执行 int i = ps.executeUpdate(); //5.判断 System.out.println(i>0?"成功":"失败"); //6.释放资源 connect.close(); s.close();
3.2结果集(查)
-
迭代获取数据库的数据
-
//查 //1.连接数据库 Class<?> Rclass = Class.forName("com.mysql.cj.jdbc.Driver"); Driver driver = (Driver) Rclass.newInstance(); String url = "jdbc:mysql://localhost:3306/test?serverTimezone=UTC"; Properties properties = new Properties(); properties.setProperty("user","root"); properties.setProperty("password","123456"); Connection connect = driver.connect(url,properties); //2.得到statement Statement statement = connect.createStatement(); //3.查询的信息 String sql = "select * from actor"; //4.使用resultset结果集获取信息 java.sql.ResultSet resultSet = statement.executeQuery(sql); //5.循环取出数据 //next返回的结果为真或假,真就执行下一个列 //向上的为previous while (resultSet.next()){ System.out.println(resultSet.getInt(1)+" "+ resultSet.getString(2)+" "+ resultSet.getString(3)+" "+ resultSet.getString(4)); }//括号内为第几列(也可以为字段名)的信息,类型要一一对应 //6.释放资源 connect.close(); statement.close();
3.3JdbcUtils
-
将连接和释放资源的代码封装在一个类中,需要用到时调用即可
-
//1.连接,调用工具类的方法 Connection connection = JdbcUtils.getConnection(); //2.预编译SQL语句 String sql = "delete from test.actor where id=?"; PreparedStatement ps = connection.prepareStatement(sql); //3.填充占位符 ps.setObject(1,5); //4.执行 ps.execute(); //5.释放资源,调用工具类的方法 JdbcUtils.closeResourece(connection,ps);
-
//总结 //1.连接数据库 //2.预编译SQL语句,返回PreparedStatement的实例 //3.填充占位符(问号) //4.执行 //5.判断 //6.释放资源
3.4BLOB
-
MySQL中,blob是一个二进制大型对象,是一个可以存储大量数据的容器,能容纳不同大小的数据
-
插入blob类型的数据必须使用preparedstatement,因为blob类型的数据无法使用字符串拼接写的.
-
MySQL的四种blob类型除了在存储的最大信息量上不同外,他们是一样的
类型 大小 TinyBlob 最大255字节 Blob 最大65K MediumBlob 最大16M LongBlob 最大4G -
如果存储的文件过大,数据库的性能会下降
使用
-
//连接数据库 Connection connection = JdbcUtils.getConnection(); //预编译SQL语句 String sql = "insert into customers(name,email,birth)values(?,?,?)"; PreparedStatement preparedStatement = connection.prepareStatement(sql); //填充占位符 preparedStatement.setObject(1,"张三"); preparedStatement.setObject(2,"[email protected]"); preparedStatement.setObject(3,"200x-11-29"); //执行 preparedStatement.execute(); //释放资源 JdbcUtils.closeResourece(connection,preparedStatement);
4.事务
- 一组逻辑操作单元,使数据从一种状态变换到另一种状态.
4.1事务处理
- 数据一旦提交,就不可回滚
- 数据什么时候意味着提交?
- 当一个连接对象被创建时,默认情况下是自动提交事务:每次执行一个SQL语句是时,如果执行成功,就会向数据库自动提交,而不能回滚
- 关闭数据库连接,数据就会自动的提交.如果多个操作,每个操作使用 的是自己单独的连接,则无法保证事务,即同一个事务的多个操作必须在同一个连接下.
- JDBC程序中为了让多个SQL语句作为一个事务执行
- 调用Connection对象的setAutoCommit(false),以取消自动提交事务
- 在所有的SQL语句都成功执行后,调用commit();方法提交事务
- 在出现异常时,调用rollback();方法回滚事务
4.2事务操作
-
-- 查看 SELECT @@AUTOCOMMIT; -- 设置 SET @@AUTOCOMMIT=0; -- 开启事务 setAutoCommit(true为自动提交事务,false为手动提交事务)
4.3事务的ACID属性
-
原子性(Atomicity)
原子性是指事务是一个不可分割的工作单位,事务中的操作要么都发生,要么都不发生
-
一致性(Consistency)
事务必须使数据库从一个一致性状态变换到另外一个一致性状态
-
隔离性(Isolation)
事务的隔离性是指一个事务的执行不能被其他事务干扰,即一个事务内部的操作及操作及事务的数据对并发的其他事务是隔离的,并发执行的各个事务之间不能互相干扰
-
持久性(Durablity)
持久性是指一个事务一旦被提交,它对数据库中数据的改变就是永久性的,接下来的其他操作和数据库故障不应该对其有任何影响
4.4事务问题
-
脏读
一个事务读到另外一个事务还没有提交的数据
-
不可重复读
一个事务先后读取同一条记录,但两次读取的数据不同,称之为不可重复读
-
幻读
一个事务按照条件查询时,没有对应的数据行,但是在插入数据时,又发现这行数据已经存在,好像出现了'幻影'
-
数据库事务的隔离性:数据库系统必须具有隔离并发运行各个事务的能力,使他们不会互相影响,避免各种并发问题
-
一个事务与其他事务隔离的程度称为隔离级别,数据库规定了多种事务隔离级别,不同隔离级别对应不同的干扰程度.隔离级别越高,数据一致性就越好,但并发性越弱.
4.5隔离级别
隔离级别 | 描述 |
---|---|
READ UNCOMMITTED(读未提交数据) | 允许事务读取未被其他事务提交的变更,脏读,不可重复读,幻读的问题都会出现 |
READ COMMITED(读已提交数据) | 只允许事务读取已经被其他事务提交的变更,可以避免脏读,但不可重复读和幻读问题仍然可能出现 |
REPEATEBLE READ(可重复读) | 确保事务可以多次从一个字段中读取相同的值,在这个事务持续期间,禁止其他事务对这个字段进行更新,可以避免脏读和不可重复读,但幻读的问题仍然存在 |
SERIALIZABLE(串行化) | 确保事务可以从一个表中读取相同的行,在这个事务持续期间,禁止其他事务对该表执行插入,更新和删除操作,所有并发问题都可以避免,但性能十分低下. |
- Oracle仅支持READ COMMITED和SERIALIABLE两个隔离级别,默认为READ COMMITED.
- MySQL四种都支持,默认的为REPEATABLE READ;
- 隔离级别大小
- Read uncommitted<Read committed(建议使用)<Repeatable Read(默认)<Serializable
- 越大隔离性越高,但性能也越低
4.6操作隔离级别
-
每启动一个MySQL程序,就会获得一个单独的数据库连接,每个数据库连接都有一个全局变量@@tx_isolation,表示当前的事务隔离级别
-
-- 查看隔离级别 SELECT @@TRANSACTION_ISLATION -- 设置隔离级别 SET [SESSION | GLOBAL] -- 隔离范围,默认session(会话) TRANSACTION ISOLATION LEVEL {READ UNCOMMITTED COMMITTED | REPEATABLE READ | SERIALIZABLE}} -- 隔离级别
5.数据库连接池
5.1技术
- 数据库连接池的基本思想:技术为数据库连接建立一个'缓冲池',预先在缓冲池中放入一定数量的连接,当需要建立数据库连接时,只需从'缓冲池'中取出一个,使用完毕之后再放回去.
- 数据库连接池负责分配,管理和释放数据库连接,它允许应用程序重复使用一个现有的数据库连接,而不是重新建立一个;
- 数据库连接池在初始化时将创建一 定数星的数据库连接放到连接池中,这些数据库连接的数量是由最小数据库连接数来设定的。无论这些数据库连接是否被使用,连接池都将一 直保证至少拥有这么多的连接数量。连接池的最大数据库连接数量限定了这个连接池能占有的最大连接数,当应用程序向连接池请求的连接数超过最大连
接数量时,这些请求将被加入到等待队列中。
5.2连接池
- JDBC的数据库连接池使用javax.sql.DataSource来表示,DataSource只是一个接口,该接口通常由服务器(Webloglc,WebSphere,Tomcat)提供实现,也有一些开源组织提供实现:
- DBCP是Apache提供的数据库连接池,Tomcat服务器自带dbcp数据库连接池,速度相对c3p0J较快,但因自身存在bug,Hibernate3已不再提供支持
- C3P0是一个开源组织提供的一一个数据库连接池,速度相对较慢,稳定性还可以,hibernate官方推荐使用
- Proxool是sourceforge下的一个开源项目数据库连接池,有监控连接池状态的功能, 稳定性较c3p0差一点
- BoneCP是一个开源组织提供的数据库连接池.速度快
- Druid是阿里提供的数据库连接池,据说是集DBCP,C3P0,Proxool优点于一身的数据库连接池,但是速度不确定是否有BoneCP快
- DataSource通常被称为数据源,它包含连接池和连接池管理两个部分,习惯上也经常把DataSource称为连接池
- DataSource用来取代DriverManager来获取Connection ,获取速度快,同时可以大幅度提高数据库访问速度.
5.3使用(Druid)
-
//1.导入jar包 //2.配置文件 url=jdbc:mysql:///test username=root password=123456 driverClassName=com.mysql.cj.jdbc.Driver initialSize=10 maxActive=10
-
//3.导入配置文件 Properties pros = new Properties(); pros.load(new FileInputStream("C:\\Users\\Administrator\\IdeaProjects\\Javaweb\\JDBC\\src\\Druid\\Druid.Properties")); //4.获取连接池对象 DataSource dataSource = DruidDataSourceFactory.createDataSource(pros); //5.连接数据库 Connection connection = dataSource.getConnection();
5.4配置文件
属性 | 说明 | 建议值 |
---|---|---|
url | 数据库的jdbc连接地址。一般为连接oracle/mysql。示例如下: | |
mysql : jdbc:mysql://ip:port/dbname?option1&option2&… | ||
oracle : jdbc:oracle:thin:@ip:port:oracle_sid | ||
username | 登录数据库的用户名 | |
password | 登录数据库的用户密码 | |
initialSize | 启动程序时,在连接池中初始化多少个连接 | 10-50已足够 |
maxActive | 连接池中最多支持多少个活动会话 | |
maxWait | 程序向连接池中请求连接时,超过maxWait的值后,认为本次请求失败,即连接池 | 100 |
没有可用连接,单位毫秒,设置-1时表示无限等待 | ||
minEvictableIdleTimeMillis | 池中某个连接的空闲时长达到 N 毫秒后, 连接池在下次检查空闲连接时,将 | 见说明部分 |
回收该连接,要小于防火墙超时设置 | ||
net.netfilter.nf_conntrack_tcp_timeout_established的设置 | ||
timeBetweenEvictionRunsMillis | 检查空闲连接的频率,单位毫秒, 非正整数时表示不进行检查 | |
keepAlive | 程序没有close连接且空闲时长超过 minEvictableIdleTimeMillis,则会执 | true |
行validationQuery指定的SQL,以保证该程序连接不会池kill掉,其范围不超 | ||
过minIdle指定的连接个数。 | ||
minIdle | 回收空闲连接时,将保证至少有minIdle个连接. | 与initialSize相同 |
removeAbandoned | 要求程序从池中get到连接后, N 秒后必须close,否则druid 会强制回收该 | false,当发现程序有未 |
连接,不管该连接中是活动还是空闲, 以防止进程不会进行close而霸占连接。 | 正常close连接时设置为true | |
removeAbandonedTimeout | 设置druid 强制回收连接的时限,当程序从池中get到连接开始算起,超过此 | 应大于业务运行最长时间 |
值后,druid将强制回收该连接,单位秒。 | ||
logAbandoned | 当druid强制回收连接后,是否将stack trace 记录到日志中 | true |
testWhileIdle | 当程序请求连接,池在分配连接时,是否先检查该连接是否有效。(高效) | true |
validationQuery | 检查池中的连接是否仍可用的 SQL 语句,drui会连接到数据库执行该SQL, 如果 | |
正常返回,则表示连接可用,否则表示连接不可用 | ||
testOnBorrow | 程序 申请 连接时,进行连接有效性检查(低效,影响性能) | false |
testOnReturn | 程序 返还 连接时,进行连接有效性检查(低效,影响性能) | false |
poolPreparedStatements | 缓存通过以下两个方法发起的SQL: | true |
public PreparedStatement prepareStatement(String sql) | ||
public PreparedStatement prepareStatement(String sql, | ||
int resultSetType, int resultSetConcurrency) | ||
maxPoolPrepareStatementPerConnectionSize | 每个连接最多缓存多少个SQL | 20 |
filters | 这里配置的是插件,常用的插件有: | stat,wall,slf4j |
监控统计: filter:stat | ||
日志监控: filter:log4j 或者 slf4j | ||
防御SQL注入: filter:wall | ||
connectProperties | 连接属性。比如设置一些连接池统计方面的配置。 | |
druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000 | ||
比如设置一些数据库连接属性: | ||