JAVA Database Connectivity java 数据库链接.java
JDBC(Java DataBase Connectivity,java数据库链接)是一种用于执行SQL语句的Java API,能够为多种关系数据库提供统一访问,它由一组用Java语言编写的类和接口组成。JDBC提供了一种基准,据此能够构建更高级的工具和接口,使数据库开发人员可以编写数据库应用程序,同时,JDBC也是个商标名。mysql
SUN公司提供的一种数据库访问规则、规范, 因为数据库种类较多,而且java语言使用比较普遍,sun公司就提供了一种规范,让其余的数据库提供商去实现底层的访问规则。 咱们的java程序只要使用sun公司提供的jdbc驱动便可。sql
咱们安装好数据库以后,咱们的应用程序也是不能直接使用数据库的,必需要经过相应的数据库驱动程序,经过驱动程序去和数据库打交道。其实也就是数据库厂商的JDBC接口实现,即对Connection等接口的实现类的jar文件。
数据库
1.Driver接口编程
Driver接口由数据库厂家提供,做为java开发人员,只须要使用Driver接口就能够了。在编程中要链接数据库,必须先装载特定厂商的数据库驱动程序,不一样的数据库有不一样的装载方法。如:缓存
装载MySql驱动:Class.forName("com.mysql.jdbc.Driver");安全
装载Oracle驱动:Class.forName("oracle.jdbc.driver.OracleDriver");性能优化
2.Connection接口服务器
Connection与特定数据库的链接(会话),在链接上下文中执行sql语句并返回结果。DriverManager.getConnection(url, user, password)方法创建在JDBC URL中定义的数据库Connection链接上。微信
链接MySql数据库:Connection conn = DriverManager.getConnection("jdbc:mysql://host:port/database", "user", "password");
链接Oracle数据库:Connection conn = DriverManager.getConnection("jdbc:oracle:thin:@host:port:database", "user", "password");
链接SqlServer数据库:Connection conn = DriverManager.getConnection("jdbc:microsoft:sqlserver://host:port; DatabaseName=database", "user", "password");
经常使用方法:
3.Statement接口
用于执行静态SQL语句并返回它所生成结果的对象。
三种Statement类:
经常使用Statement方法:
4.ResultSet接口
ResultSet提供检索不一样类型字段的方法,经常使用的有:
ResultSet还提供了对结果集进行滚动的方法:
使用后依次关闭对象及链接:ResultSet → Statement → Connection
DriverManager.registerDriver(new com.mysql.jdbc.Driver());
//DriverManager.getConnection("jdbc:mysql://localhost/test?user=SIHAI&password=SIHAI"); //2. 创建链接 参数一: 协议 + 访问的数据库 , 参数二: 用户名 , 参数三: 密码。 conn = DriverManager.getConnection("jdbc:mysql://localhost/student", "root", "root");
//3. 建立statement , 跟数据库打交道,必定须要这个对象 st = conn.createStatement();
//4. 执行查询 , 获得结果集 String sql = "select * from t_stu"; rs = st.executeQuery(sql);
//5. 遍历查询每一条记录 while(rs.next()){ int id = rs.getInt("id"); String name = rs.getString("name"); int age = rs.getInt("age"); System.out.println("id="+id + "===name="+name+"==age="+age); }
if (rs != null) { try { rs.close(); } catch (SQLException sqlEx) { } // ignore rs = null; }
/** * 释放资源 * @param conn * @param st * @param rs */ public static void release(Connection conn , Statement st , ResultSet rs){ closeRs(rs); closeSt(st); closeConn(conn); } private static void closeRs(ResultSet rs){ try { if(rs != null){ rs.close(); } } catch (SQLException e) { e.printStackTrace(); }finally{ rs = null; } } private static void closeSt(Statement st){ try { if(st != null){ st.close(); } } catch (SQLException e) { e.printStackTrace(); }finally{ st = null; } } private static void closeConn(Connection conn){ try { if(conn != null){ conn.close(); } } catch (SQLException e) { e.printStackTrace(); }finally{ conn = null; } }
/** * 获取链接对象 * @return */ public static Connection getConn(){ Connection conn = null; try { Class.forName(driverClass); //静态代码块 ---> 类加载了,就执行。 java.sql.DriverManager.registerDriver(new Driver()); //DriverManager.registerDriver(new com.mysql.jdbc.Driver()); //DriverManager.getConnection("jdbc:mysql://localhost/test?user=monty&password=greatsqldb"); //2. 创建链接 参数一: 协议 + 访问的数据库 , 参数二: 用户名 , 参数三: 密码。 conn = DriverManager.getConnection(url, name, password); } catch (Exception e) { e.printStackTrace(); } return conn; }
driverClass=com.mysql.jdbc.Driver url=jdbc:mysql://localhost/student name=root password=root
static{ try { //1. 建立一个属性配置对象 Properties properties = new Properties(); InputStream is = new FileInputStream("jdbc.properties"); //对应文件位于工程根目录 //使用类加载器,去读取src底下的资源文件。 后面在servlet //对应文件位于src目录底下 //InputStream is = JDBCUtil.class.getClassLoader().getResourceAsStream("jdbc.properties"); //导入输入流。 properties.load(is); //读取属性 driverClass = properties.getProperty("driverClass"); url = properties.getProperty("url"); name = properties.getProperty("name"); password = properties.getProperty("password"); } catch (Exception e) { e.printStackTrace(); } }
源代码以下:
public class JDBCUtil { static String driverClass = null; static String url = null; static String name = null; static String password= null; static{ try { //1. 建立一个属性配置对象 Properties properties = new Properties(); InputStream is = new FileInputStream("jdbc.properties"); //使用类加载器,去读取src底下的资源文件。 后面在servlet // InputStream is = JDBCUtil.class.getClassLoader().getResourceAsStream("jdbc.properties"); //导入输入流。 properties.load(is); //读取属性 driverClass = properties.getProperty("driverClass"); url = properties.getProperty("url"); name = properties.getProperty("name"); password = properties.getProperty("password"); } catch (Exception e) { e.printStackTrace(); } } /** * 获取链接对象 * @return */ public static Connection getConn(){ Connection conn = null; try { Class.forName(driverClass); //静态代码块 ---> 类加载了,就执行。 java.sql.DriverManager.registerDriver(new Driver()); //DriverManager.registerDriver(new com.mysql.jdbc.Driver()); //DriverManager.getConnection("jdbc:mysql://localhost/test?user=monty&password=greatsqldb"); //2. 创建链接 参数一: 协议 + 访问的数据库 , 参数二: 用户名 , 参数三: 密码。 conn = DriverManager.getConnection(url, name, password); } catch (Exception e) { e.printStackTrace(); } return conn; } /** * 释放资源 * @param conn * @param st * @param rs */ public static void release(Connection conn , Statement st , ResultSet rs){ closeRs(rs); closeSt(st); closeConn(conn); } private static void closeRs(ResultSet rs){ try { if(rs != null){ rs.close(); } } catch (SQLException e) { e.printStackTrace(); }finally{ rs = null; } } private static void closeSt(Statement st){ try { if(st != null){ st.close(); } } catch (SQLException e) { e.printStackTrace(); }finally{ st = null; } } private static void closeConn(Connection conn){ try { if(conn != null){ conn.close(); } } catch (SQLException e) { e.printStackTrace(); }finally{ conn = null; } } }
INSERT INTO t_stu (NAME , age) VALUES ('wangqiang',28) INSERT INTO t_stu VALUES (NULL,'wangqiang2',28)
// 1. 获取链接对象 conn = JDBCUtil.getConn(); // 2. 根据链接对象,获得statement st = conn.createStatement(); //3. 执行添加 String sql = "insert into t_stu values(null , 'aobama' , 59)"; //影响的行数, ,若是大于0 代表操做成功。 不然失败 int result = st.executeUpdate(sql); if(result >0 ){ System.out.println("添加成功"); }else{ System.out.println("添加失败"); }
DELETE FROM t_stu WHERE id = 6
// 1. 获取链接对象 conn = JDBCUtil.getConn(); // 2. 根据链接对象,获得statement st = conn.createStatement(); //3. 执行添加 String sql = "delete from t_stu where name='aobama'"; //影响的行数, ,若是大于0 代表操做成功。 不然失败 int result = st.executeUpdate(sql); if(result >0 ){ System.out.println("删除成功"); }else{ System.out.println("删除失败"); }
SELECT * FROM t_stu
// 1. 获取链接对象 conn = JDBCUtil.getConn(); // 2. 根据链接对象,获得statement st = conn.createStatement(); // 3. 执行sql语句,返回ResultSet String sql = "select * from t_stu"; rs = st.executeQuery(sql); // 4. 遍历结果集 while (rs.next()) { String name = rs.getString("name"); int age = rs.getInt("age"); System.out.println(name + " " + age); }
UPDATE t_stu SET age = 38 WHERE id = 1;
// 1. 获取链接对象 conn = JDBCUtil.getConn(); // 2. 根据链接对象,获得statement st = conn.createStatement(); //3. 执行添加 String sql = "update t_stu set age = 26 where name ='qyq'"; //影响的行数, ,若是大于0 代表操做成功。 不然失败 int result = st.executeUpdate(sql); if(result >0 ){ System.out.println("更新成功"); }else{ System.out.println("更新失败"); }
这个命名不必定须要这样,可是这样的命名更容易懂得测试的意思,因此建议命名见名知意。
右键工程 --- add Library --- Junit --- Junit4
/** * 使用junit执行单元测试 */ public class TestDemo { @Test public void testQuery() { // 查询 Connection conn = null; Statement st = null; ResultSet rs = null; try { // 1. 获取链接对象 conn = JDBCUtil.getConn(); // 2. 根据链接对象,获得statement st = conn.createStatement(); // 3. 执行sql语句,返回ResultSet String sql = "select * from t_stu"; rs = st.executeQuery(sql); // 4. 遍历结果集 while (rs.next()) { String name = rs.getString("name"); int age = rs.getInt("age"); System.out.println(name + " " + age); } } catch (Exception e) { e.printStackTrace(); } finally { JDBCUtil.release(conn, st, rs); } } @Test public void testInsert(){ // 查询 Connection conn = null; Statement st = null; try { // 1. 获取链接对象 conn = JDBCUtil.getConn(); // 2. 根据链接对象,获得statement st = conn.createStatement(); //3. 执行添加 String sql = "insert into t_stu values(null , 'aobama' , 59)"; //影响的行数, ,若是大于0 代表操做成功。 不然失败 int result = st.executeUpdate(sql); if(result >0 ){ System.out.println("添加成功"); }else{ System.out.println("添加失败"); } } catch (Exception e) { e.printStackTrace(); }finally{ JDBCUtil.release(conn, st); } } @Test public void testDelete(){ // 查询 Connection conn = null; Statement st = null; try { // 1. 获取链接对象 conn = JDBCUtil.getConn(); // 2. 根据链接对象,获得statement st = conn.createStatement(); //3. 执行添加 String sql = "delete from t_stu where name='aobama'"; //影响的行数, ,若是大于0 代表操做成功。 不然失败 int result = st.executeUpdate(sql); if(result >0 ){ System.out.println("删除成功"); }else{ System.out.println("删除失败"); } } catch (Exception e) { e.printStackTrace(); }finally{ JDBCUtil.release(conn, st); } } @Test public void testUpdate(){ // 查询 Connection conn = null; Statement st = null; try { // 1. 获取链接对象 conn = JDBCUtil.getConn(); // 2. 根据链接对象,获得statement st = conn.createStatement(); //3. 执行添加 String sql = "update t_stu set age = 26 where name ='qyq'"; //影响的行数, ,若是大于0 代表操做成功。 不然失败 int result = st.executeUpdate(sql); if(result >0 ){ System.out.println("更新成功"); }else{ System.out.println("更新失败"); } } catch (Exception e) { e.printStackTrace(); }finally{ JDBCUtil.release(conn, st); } } }
Data Access Object 数据访问对象
DAO(Data Access Object) 数据访问对象是一个面向对象的数据库接口,它显露了 Microsoft Jet 数据库引擎(由 Microsoft Access 所使用),并容许 Visual Basic 开发者经过 ODBC 像直接链接到其余数据库同样,直接链接到 Access 表。DAO 最适用于单系统应用程序或小范围本地分布使用。
/** * 定义操做数据库的方法 */ public interface UserDao { /** * 查询全部 */ void findAll(); }
public class UserDaoImpl implements UserDao{ @Override public void findAll() { Connection conn = null; Statement st = null; ResultSet rs = null; try { //1. 获取链接对象 conn = JDBCUtil.getConn(); //2. 建立statement对象 st = conn.createStatement(); String sql = "select * from t_user"; rs = st.executeQuery(sql); while(rs.next()){ String userName = rs.getString("username"); String password = rs.getString("password"); System.out.println(userName+"="+password); } } catch (Exception e) { e.printStackTrace(); }finally { JDBCUtil.release(conn, st, rs); } } }
@Test public void testFindAll(){ UserDao dao = new UserDaoImpl(); dao.findAll(); }
String sql = "select * from t_user where username='"+ username +"' and password='"+ password +"'"; UserDao dao = new UserDaoImpl(); dao.login("admin", "100234khsdf88' or '1=1"); SELECT * FROM t_user WHERE username='admin' AND PASSWORD='100234khsdf88' or '1=1' //前面先拼接sql语句, 若是变量里面带有了 数据库的关键字,那么一并认为是关键字。 不认为是普通的字符串。 rs = st.executeQuery(sql);
该对象就是替换前面的statement对象。
String sql = "insert into t_user values(null , ? , ?)"; ps = conn.prepareStatement(sql); //给占位符赋值 从左到右数过来,1 表明第一个问号, 永远你是1开始。 ps.setString(1, userName); ps.setString(2, password);
(1) 使用PreparedStatement,代码的可读性和可维护性比Statement高。
(2) PreparedStatement 能最大可能提升性能。
DBServer会对预编译语句提供性能优化。由于预编译语句有可能被重复调用,因此语句在被DBServer的编译器编译后的执行代码被缓存下来,那么下次调用时只要是相同的预编译语句就不须要编译,只要将参数直接传入编译过的语句执行代码中就会获得执行。
在statement语句中,即便是相同操做但由于数据内容不同,因此整个语句自己不能匹配,没有缓存语句的意义。事实是没有数据库会对普通语句编译后的执行代码缓存。这样每执行一次都要对传入的语句编译一次。
(3) PreparedStatement能保证安全性,但 Statement有sql注入等安全问题。
1. 概述
在数据库中,所谓事务是指一组逻辑操做单元,使数据从一种状态变换到另外一种状态。
为确保数据库中数据的一致性,数据的操纵应当是离散的成组的逻辑单元:当它所有完成时,数据的一致性能够保持,而当这个单元中的一部分操做失败,整个事务应所有视为错误,全部从起始点之后的操做应所有回退到开始状态。
事务的操做:先定义开始一个事务,而后对数据做修改操做,这时若是提交(COMMIT),这些修改就永久地保存下来,若是回退(ROLLBACK),数据库管理系统将放弃您所做的全部修改而回到开始事务时的状态。
2. 事务的ACID属性
2.1 原子性(Atomicity)
原子性是指事务是一个不可分割的工做单位,事务中的操做要么都发生,要么都不发生。
2.2 一致性(Consistency)
事务必须使数据库从一个一致性状态变换到另一个一致性状态。(数据不被破坏)
2.3 隔离性(Isolation)
事务的隔离性是指一个事务的执行不能被其余事务干扰,即一个事务内部的操做及使用的数据对并发的其余事务是隔离的,并发执行的各个事务之间不能互相干扰。
2.4 持久性(Durability)
持久性是指一个事务一旦被提交,它对数据库中数据的改变就是永久性的,接下来的其余操做和数据库故障不该该对其有任何影响。
3. JDBC 事务处理
在JDBC中,事务默认是自动提交的,每次执行一个 SQL 语句时,若是执行成功,就会向数据库自动提交,而不能回滚。
为了让多个 SQL 语句做为一个事务执行,需调用 Connection 对象的 setAutoCommit(false); 以取消自动提交事务:
conn.setAutoCommit(false);
在全部的 SQL 语句都成功执行后,调用 commit(); 方法提交事务
conn.commit();
在出现异常时,调用 rollback(); 方法回滚事务,通常再catch模块中执行回滚操做。
conn.rollback();
能够经过Connection的getAutoCommit()方法来得到当前事务的提交方式。
注意:在MySQL中的数据库存储引擎InnoDB支持事务,MyISAM不支持事务。
1. 概述
当须要批量插入或者更新记录时。能够采用Java的批量更新机制,这一机制容许多条语句一次性提交给数据库批量处理。一般状况下比单独提交处理更有效率。
JDBC的批量处理语句包括下面两个方法:
一般咱们会遇到两种批量执行SQL语句的状况:
2. Statement批量处理
Statement sm = conn.createStatement(); sm.addBatch(sql1); sm.addBatch(sql2); ... //批量处理 sm.executeBatch() //清除sm中积攒的参数列表 sm.clearBatch();
3. PreparedStatement批量传参
preparedStatement ps = conn.preparedStatement(sql); for(int i=1;i<100000;i++){ ps.setInt(1, i); ps.setString(2, "name"+i); ps.setString(3, "email"+i); ps.addBatch(); if((i+1)%1000==0){ //批量处理 ps.executeBatch(); //清空ps中积攒的sql ps.clearBatch(); } }
注意:MySQL不支持批量处理。
批量处理应该设置一个上限,当批量处理列表中的sql累积到必定数量后,就应该执行,并在执行完成后,清空批量列表。
通常在excel导入数据的时候会用到批处理。
1. 概述
Java 经过JDBC得到链接之后,获得一个Connection 对象,能够从这个对象得到有关数据库管理系统的各类信息,包括数据库中的各个表,表中的各个列,数据类型,触发器,存储过程等各方面的信息。根据这些信息,JDBC能够访问一个实现事先并不了解的数据库。
获取这些信息的方法都是在DatabaseMetaData类的对象上实现的,而DataBaseMetaData对象是在Connection对象上得到的。
2. 获取数据库元数据
DatabaseMetaData 类中提供了许多方法用于得到数据源的各类信息,经过这些方法能够很是详细的了解数据库的信息:
3. ResultSetMetaData
可用于获取关于 ResultSet 对象中列的类型和属性信息的对象:
1. Statement
Statement stmt = conn.createStatement(type,concurrency);
2. PreparedStatement
PreparedStatement stmt = conn.prepareStatement(sql,type,concurrency);
type说明:
ResultSet的Type | 说明 |
---|---|
TYPE_FORWARD_ONLY | 结果集不能滚动,只可向前滚动 |
TYPE_SCROLL_INSENSITIVE | 双向滚动,但不及时更新,就是若是数据库里的数据修改过,并不在ResultSet中反应出来 |
TYPE_SCROLL_SENSITIVE | 双向滚动,并及时跟踪数据库的更新,以便更改ResultSet中的数据 |
Concurrency(并发类型)说明:
ResultSet的Concurrency(并发类型) | 说明 |
---|---|
CONCUR_READ_ONLY | 结果集不可用于更新数据库 |
CONCUR_UPDATABLE | 结果集能够用于更新数据库 |
3. ResultSet滚动的结果集使用
First: 将指针移动到此 ResultSet 对象的第一行
Last: 将指针移动到此 ResultSet 对象的最后一行
beforeFirst: 将指针移动到此 ResultSet 对象的开头,正好位于第一行以前
afterLast: 将指针移动到此 ResultSet 对象的末尾,正好位于最后一行以后
isFirst: 检索指针是否位于此 ResultSet 对象的第一行
isLast: 检索指针是否位于此 ResultSet 对象的最后一行
isBeforeFirst: 检索指针是否位于此 ResultSet 对象的第一行以前
isAfterLast: 检索指针是否位于此 ResultSet 对象的最后一行以后
Relative: 按相对行数(或正或负)移动指针
Next: 将指针从当前位置下移一行
Previous: 将指针移动到此 ResultSet 对象的上一行
Absolute: 将指针移动到此 ResultSet 对象的给定行编号
如:
rs.absolute(80); //将指针移动到ResultSet 对象的第80行记录。
注意:该特性对Oralce数据有效。可是在Mysql数据库中无效,Mysql只支持TYPE_SCROLL_INSENSITIVE,CONCUR_READ_ONLY
。
1. 为何要使用JDBC链接池
普通的JDBC数据库链接使用 DriverManager 来获取,每次向数据库创建链接的时候都要将 Connection 加载到内存中,再验证用户名和密码。须要数据库链接的时候,就向数据库要求一个,执行完成后再断开链接。这样的方式将会消耗大量的资源和时间。数据库的链接资源并无获得很好的重复利用.若同时有几百人甚至几千人在线,频繁的进行数据库链接操做将占用不少的系统资源,严重的甚至会形成服务器的崩溃。
对于每一次数据库链接,使用完后都得断开。不然,若是程序出现异常而未能关闭,将会致使数据库系统中的内存泄漏,最终将致使重启数据库。
这种开发不能控制被建立的链接对象数,系统资源会被毫无顾及的分配出去,如链接过多,也可能致使内存泄漏,服务器崩溃。
为解决传统开发中的数据库链接问题,能够采用数据库链接池技术。
2. 数据库链接池(connection pool)
数据库链接池的基本思想就是为数据库链接创建一个“缓冲池”。预先在缓冲池中放入必定数量的链接,当须要创建数据库链接时,只需从“缓冲池”中取出一个,使用完毕以后再放回去。
数据库链接池负责分配、管理和释放数据库链接,它容许应用程序重复使用一个现有的数据库链接,而不是从新创建一个。
数据库链接池在初始化时将建立必定数量的数据库链接放到链接池中,这些数据库链接的数量是由最小数据库链接数来设定的。不管这些数据库链接是否被使用,链接池都将一直保证至少拥有这么多的链接数量。链接池的最大数据库链接数量限定了这个链接池能占有的最大链接数,当应用程序向链接池请求的链接数超过最大链接数量时,这些请求将被加入到等待队列中。
3. 数据库链接池工做原理
4. 使用数据库链接池的优势
(1)资源重用:
因为数据库链接得以重用,避免了频繁建立,释放链接引发的大量性能开销。在减小系统消耗的基础上,另外一方面也增长了系统运行环境的平稳性。
(2)更快的系统反应速度
数据库链接池在初始化过程当中,每每已经建立了若干数据库链接置于链接池中备用。此时链接的初始化工做均已完成。对于业务请求处理而言,直接利用现有可用链接,避免了数据库链接初始化和释放过程的时间开销,从而减小了系统的响应时间。
(3)新的资源分配手段
对于多应用共享同一数据库的系统而言,可在应用层经过数据库链接池的配置,实现某一应用最大可用数据库链接数的限制,避免某一应用独占全部的数据库资源。
(4)统一的链接管理,避免数据库链接泄露
在较为完善的数据库链接池实现中,可根据预先的占用超时设定,强制回收被占用链接,从而避免了常规数据库链接操做中可能出现的资源泄露。
5. 经常使用数据库链接池介绍
JDBC 的数据库链接池使用 javax.sql.DataSource 来表示,DataSource 只是一个接口,该接口一般由服务器(Weblogic, WebSphere, Tomcat)提供实现,也有一些开源组织提供实现,如:
其中,DBCP和C3P0用得比较多。
Tomcat 在 7.0 之前的版本都是使用 commons-dbcp 作为链接池的实现。
数据源和数据库链接不一样,数据源无需建立多个,它是产生数据库链接的工厂,所以整个应用只须要一个数据源便可。
当数据库访问结束后,程序仍是像之前同样关闭数据库链接:conn.close();
但它并无关闭数据库的物理链接,它仅仅把数据库链接释放,归还给了数据库链接池。
大概基本的就是这么多了,但愿可以帮助到你们,有问题能够交流沟通。
文章有不当之处,欢迎指正,若是喜欢微信阅读,你也能够关注个人微信公众号:
好好学java
,获取优质学习资源。