1.简介
JDBC(Java DataBase Connectivity) 是一种可用于执行SQL语句的Java API,是一套面向对象的应用程序接口,
统一了数据库的访问方式,数据库厂商提供了实现接口的类,称为‘驱动程序’。因此JDBC并不能直接访问数据库,
需要依赖数据库厂商提供的JDBC驱动程序。
--SQL语言:
数据定义语言(Data Definition Language,DDL)如:create,alter,drop等
数据操纵语言(Data Manipulation Language,DML)如update,delete等
数据查询语言(Data Query language,DQL),查询
数据控制语言(Data Control Language,DCL),如grant,revoke等
事务控制语言(Transaction Control Language,TCL),如commit,rollback等
2.准备工作,以Mysql为例
a.安装了Mysql 数据库
b.下载mysql驱动包并在项目中导入jar包:mysql-connector-java-5.1.42-bin.jar
3.JDBC的操作
步骤:
1.注册驱动
2.连接数据库
3.创建语句对象,用于执行SQL
4.执行SQL
5.处理执行结果
6.关闭连接
一、JDBC基本操作
1)在数据库先建立一张表Student
CREATE TABLE student( id INT AUTO_INCREMENT, NAME VARCHAR(20), age INT , PRIMARY KEY(id) );
2)jdbc实现
package com.huan.jdbc; import java.sql.Connection; import java.sql.DriverManager; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; public class JDBCDemo01 { public static void main(String[] args) { //声明连接 Connection connection = null; try { //加载驱动 Class.forName("com.mysql.jdbc.Driver"); //数据库连接信息 //地址 jdbc:mysql://地址/数据库 String url = "jdbc:mysql://localhost:3306/huan"; String user = "root"; String password = "root"; //获得连接 connection = DriverManager.getConnection(url, user, password); //语句对象 Statement st = connection.createStatement(); String sql = "insert into student (name,age) values (‘zhangsan‘,30)"; //执行sql DDL-execute()方法 DML-executeUpdate()方法 DQL-executeQuery()方法 int n = st.executeUpdate(sql); String querySql = "select id,name,age from student"; ResultSet rs = st.executeQuery(querySql); //处理结果 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); } //释放资源 rs.close(); st.close(); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); }finally { //关闭连接 if(connection != null){ try { connection.close(); } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } } }
上面先执行了一条插入语句,由于主键id是自增的,因此直插入了名字zhangsan和年龄30,然后查询了表的数据
输出如下:
id:1 name:zhangsan age:30
二、JDBC连接的封装
由于连接数据库的过程都是一样,为了提高代码的重用,可以将数据库的连接封装起来:
首先将数据库的连接信息放到配置文件中,比如在项目根目录下新建文件db.properties,如下:
#db.properties jdbc.driver=com.mysql.jdbc.Driver jdbc.url=jdbc:mysql://localhost:3306/huan jdbc.user=root jdbc.password=root
JDBCUtil类
package com.huan.jdbc; import java.io.FileInputStream; import java.sql.Connection; import java.sql.DriverManager; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import java.util.Properties; public class JDBCUtil { private static String driverClass; private static String url; private static String user; private static String password; //静态块加载数据库配置属性 static{ Properties config = new Properties(); try { config.load(new FileInputStream("db.properties")); } catch (Exception e) { e.printStackTrace(); } driverClass = config.getProperty("jdbc.driver"); url = config.getProperty("jdbc.url"); user = config.getProperty("jdbc.user"); password =config.getProperty("jdbc.password"); } //获取数据库连接 public static Connection getConnection() throws SQLException { try { Class.forName(driverClass); Connection connection = DriverManager.getConnection(url, user, password); return connection; } catch (ClassNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); throw new SQLException("没有找到驱动",e); } } //关闭资源 public static void close(Connection connection,Statement st,ResultSet rs){ if(connection != null){ try { connection.close(); } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); } } if(st != null){ try { st.close(); } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); } } if(rs != null){ try { rs.close(); } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } }
测试JDBCUtil
package com.huan.jdbc; import java.sql.Connection; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; public class JDBCUtilTest { public static void main(String[] args) { Connection connection = null; Statement st = null; ResultSet rs =null; try { connection = JDBCUtil.getConnection(); String sql = "select id ,name ,age from student" ; st = connection.createStatement(); rs = st.executeQuery(sql); 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); } } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); }finally { JDBCUtil.close(connection, st, rs); } } }
输出:
id:1 name:zhangsan age:30
三 SQL预处理及事务
SQL预处理:在之前的处理中都是将sql语句发送到数据库,有数据库的sql解释器把sql语句生成底层的内部命令,然后执行命令,完成操作,当前不断的
向数据库发送sql语句,会增加数据库sq解释器的负担,影响执行速度。
而Connection的prepareStatement(Stirng sql)方法能够对sql语句进行预处理,生成数据底层的命令,并封装在PrepareStatement对
象中,通过对应的方法执行底层数据库的命令,从而减轻数据库的负担,提高访问速度。
并且之前的sql都是拼接的,当带参数拼接时容易造成SQL的注入(参数中含有sql成分,改变原有的sql语句逻辑),而PrepareStatement在
编译之后,其中的sql执行计划已经确定,当替换参数时不会改变执行计划,因此可以避免sql注入。
事务控制: 当我们在编写逻辑的时候,存在多条的插入或者更新语句,前面的sql成功执行之后出现错误,这时导致业务逻辑中断,而执行成功的数据已经
存入数据库,从而导致数据不完整。为避免这种事情的发生就需要手动进行事务控制。
1.关闭自动提交--connection.setAutoCommit(false);
2.业务执行完成后提交-connection.commit();
3.在遇到异常时回滚-connection.rollback();
package com.huan.jdbc; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.ResultSetMetaData; import java.sql.SQLException; import java.sql.Statement; public class PrepareStatementDemo { public static void main(String[] args) { Connection connection = null; //SQL预处理对象 PreparedStatement ps = null; ResultSet rs =null; try { connection = JDBCUtil.getConnection(); connection.setAutoCommit(false); //?代表参数 String sql = "insert into student (name,age) values(?,?)"; //编译预执行计划 在数据库上创建执行计划 //(第二个参数可以不要-Statement.RETURN_GENERATED_KEYS 代表返回主键,没有的话下面的要去掉ps.getGeneratedKeys()会) ps = connection.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS); //?的位置 和 替换的值 ps.setString(1, "lisi"); ps.setInt(2, 20); //n 更新结果的数量 int n = ps.executeUpdate(); System.out.println("成功插入"+n+"条数据"); //获取插入成功数据的主键 ResultSet keySet = ps.getGeneratedKeys(); int key = -1; while(keySet.next()){ key = keySet.getInt(1); } System.out.println("插入数据的主键:"+key); //查询 String querySql = "select id , name ,age from student"; ps = connection.prepareStatement(querySql); rs = ps.executeQuery(); //获取结果集的元数据相关信息 ResultSetMetaData metaData = rs.getMetaData(); //列数 int count = metaData.getColumnCount(); for(int i = 1; i <= count; i++){ System.out.print(metaData.getColumnName(i)+" "); } System.out.println(); //查询结果打印 while(rs.next()){ int id = rs.getInt("id"); String name = rs.getString("name"); int age = rs.getInt("age"); System.out.println(id+" "+name+" "+age); } //提交 connection.commit(); } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); if(connection != null){ try { //出现异常回滚 connection.rollback(); } catch (SQLException e1) { // TODO Auto-generated catch block e1.printStackTrace(); } } }finally { JDBCUtil.close(connection, ps, rs); } } }
输出结果:
成功插入1条数据 插入数据的主键:2 id name age 1 zhangsan 30 2 lisi 20
成功插入了 lisi 这条记录,且返回主键为2.最后打印了列名和查询得到的数据
这里用了事务的回滚,如果保存完之后再查询的中间出现异常,数据是不会存到数据库的,可以动手试下。
四、JDBC的批量操作
PrepareStatement和Statement都提供了可以批量操作SQL的方法
1.将sql添加至缓存区
Statement: addBatch(sql)方法,可以将sql直接添加Statement缓存区;
PrepareStatement:需要先执行setxxx(n,参数)方法将参数赋值,然后调用addBatch(sql)方法
2.执行executeBatch()将参数批量发送到数据库,并且执行
3.可以使用clearBatch()清空缓存区的参数或者sql
package com.huan.jdbc; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.SQLException; import java.sql.Statement; import java.util.Arrays; import org.junit.Test; public class JDBCBatchDemo { @Test public void testJdbcPrepareBatch(){ Connection connection = null; PreparedStatement ps = null; try { connection = JDBCUtil.getConnection(); String sql = "insert into student (name,age) values (?,?)"; ps = connection.prepareStatement(sql); ps.setString(1, "ps1"); ps.setInt(2, 11); ps.addBatch(); ps.setString(1, "ps2"); ps.setInt(2, 12); ps.addBatch(); int [] n = ps.executeBatch(); System.out.println(Arrays.toString(n)); } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); } } @Test public void testJdbcStatementBatch(){ Connection connection = null; Statement st = null; try { connection = JDBCUtil.getConnection(); String sql1 = "insert into student(name,age) values(‘statement1‘,20)"; String sql2 = "insert into student(name,age) values(‘statement2‘,30)"; String sql3 = "insert into student(name,age) values(‘statement3‘,40)"; st = connection.createStatement(); //批量添加 st.addBatch(sql1); st.addBatch(sql2); st.addBatch(sql3); //执行 int [] n = st.executeBatch(); System.out.println(Arrays.toString(n)); } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); }finally{ JDBCUtil.close(connection, st, null); } } }
testJdbcStatementBatch()测试方法完成后查看数据库,数据成功添加:
testJdbcPrepareBatch()测试方法执行之后查看数据库,数据成功添加:
五、连接池(数据源)的使用
连接池能够重用数据库的连接,控制数据库的连接总数,当关闭从连接池中获取的连接时,只是将此连接归还给连接池,没有真正的与数据库断开连接。
重用的连接池有:DBCP和c3p0
1)c3p0的使用
官网下载jar包,将c3p0-0.9.5.2.jar和mchange-commons-java-0.2.11.jar加入项目中。
package com.huan.jdbc; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import com.mchange.v2.c3p0.ComboPooledDataSource; public class C3p0DataSourceDemo { public static void main(String[] args) { Connection connection = null; try { ComboPooledDataSource cpds = new ComboPooledDataSource(); //配置连接池参数 cpds.setDriverClass("com.mysql.jdbc.Driver"); cpds.setJdbcUrl("jdbc:mysql://localhost:3306/huan"); cpds.setUser("root"); cpds.setPassword("root"); //连接池的管理策略 ... //最小连接数量 cpds.setMinPoolSize(5); //最大连接数量 cpds.setMaxPoolSize(20); //超时时间ms cpds.setCheckoutTimeout(10000); //获取连接 connection = cpds.getConnection(); String sql = "select id,name,age from student"; PreparedStatement ps = connection.prepareStatement(sql); ResultSet rs = ps.executeQuery(); 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); } rs.close(); ps.close(); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); }finally { try { connection.close(); } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } }
输出结果:
id:1 name:zhangsan age:30 id:2 name:lisi age:20 id:3 name:statement1 age:20 id:4 name:statement2 age:30 id:5 name:statement3 age:40 id:6 name:ps1 age:11 id:7 name:ps2 age:12
2)DBCP的使用
官网下载jar包,将commons-logging-1.2.jar,commons-dbcp2-2.1.1.jar和commons-pool2-2.4.2.jar下载后导入项目
package com.huan.jdbc; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import org.apache.commons.dbcp2.BasicDataSource; public class DBCPDemo { public static void main(String[] args) { Connection connection = null; try { BasicDataSource bds = new BasicDataSource(); // 连接参数 bds.setDriverClassName("com.mysql.jdbc.Driver"); bds.setUrl("jdbc:mysql://localhost:3306/huan"); bds.setUsername("root"); bds.setPassword("root"); // 管理策略参数 bds.setInitialSize(5); bds.setMinIdle(5); bds.setMaxTotal(20); bds.setMaxWaitMillis(10000); connection = bds.getConnection(); String sql = "select id,name,age from student"; PreparedStatement ps = connection.prepareStatement(sql); ResultSet rs = ps.executeQuery(); 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); } rs.close(); ps.close(); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } finally { try { if (connection != null) { connection.close(); } } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } }
输出结果:
id:1 name:zhangsan age:30 id:2 name:lisi age:20 id:3 name:statement1 age:20 id:4 name:statement2 age:30 id:5 name:statement3 age:40 id:6 name:ps1 age:11 id:7 name:ps2 age:12
关于JDBC的一些总结就写这么多了~~