南通做网站软件,香水网站设计网页,出名的建站网站,优秀的html5网站 2016文章目录一 JDBC概述1 Java中的数据存储技术2 什么是JDBC3 JDBC程序的编写步骤二 Java连接数据库的方式三 使用 PreparedStatement 实现 CRUD 操作1 数据库的调用的三个接口2 增Create/删Delete/改Update 操作3 查Retrieval操作4 批量插入操作四 数据库事务1 事务2 事务的 ACID…
文章目录一 JDBC概述1 Java中的数据存储技术2 什么是JDBC3 JDBC程序的编写步骤二 Java连接数据库的方式三 使用 PreparedStatement 实现 CRUD 操作1 数据库的调用的三个接口2 增Create/删Delete/改Update 操作3 查Retrieval操作4 批量插入操作四 数据库事务1 事务2 事务的 ACID 性质3 三种导致数据自动提交的操作4 MySQL 的四种隔离级别5 根据 ACID 改进实现五 DAO(Data Access Object) 逻辑架构六 数据库连接池1 作用2 C3P0 数据库连接池3 DBCP 数据库连接池4 Druid 数据库连接池实用 *七 Apache-DBUtils 实现 CRUD 操作 *1 实现2 自定义 Handler3 资源的关闭一 JDBC概述
1 Java中的数据存储技术
JDBCJava Database Connectivity直接访问数据库JDOJava Data Object技术第三方工具如Hibernate, Mybatis 等。
后两种本质上是更好地封装了JDBC。
2 什么是JDBC
独立于特定数据库管理系统、通用的SQL数据库存取和操作的公共接口一组API定义了用来访问数据库的标准Java类库使用这些类库可以以一种标准的方法、方便地访问数据库资源。JDBC为访问不同的数据库提供了一种统一的途径为开发者屏蔽了一些细节问题。
3 JDBC程序的编写步骤 二 Java连接数据库的方式
一个数据库连接就是一个 socket 连接。socket (IP : port)这种方式实现了数据与代码的分离实现了解耦如果需要修改配置文件信息可以避免程序重新打包。 public void getConnection5() throws Exception{//1.读取配置文件中的4个基本信息InputStream is ClassLoader.getSystemClassLoader().getResourceAsStream(jdbc.properties);Properties pros new Properties();pros.load(is);String user pros.getProperty(user);String password pros.getProperty(password);String url pros.getProperty(url);String driverClass pros.getProperty(driverClass);//2.加载驱动注释部分会自动运行Class clazz Class.forName(driverClass); //也可省略 Class clazz //Driver driver (Driver) clazz.newInstance();//DriverManager.registerDriver(driver);//3.获取连接Connection conn DriverManager.getConnection(url, user, password);}三 使用 PreparedStatement 实现 CRUD 操作
1 数据库的调用的三个接口
Statement已淘汰用于执行静态 SQL 语句并返回它所生成结果的对象PreparedStatementSQL 语句被预编译并存储在此对象中所以一个 PreparedStatement 的实例表示一条预编译过的 SQL 语句可以使用此对象多次高效地执行该语句是Statement的子接口CallableStatement用于执行 SQL 存储过程是Statement的子接口。
2 增Create/删Delete/改Update 操作
根据有无返回值将增删改归为一类方法查归为一类方法。 /* 定义时 */public void commonOps(String sql, Object... args) {Connection conn null;PreparedStatement ps null;try {// 1.连接数据库获得conn并根据sql预加载psconn JDBCUtils.getMyConnection(); // 自己封装的方法实现同二部分ps conn.prepareStatement(sql); // PreparedStatement的特性预编译sql语句// 2.填充占位符for (int i 0; i args.length; i) {ps.setObject(i 1, args[i]); // sql索引从1开始}// 3.执行ps.execute();} catch (Exception e) {e.printStackTrace();} finally {// 4.关闭资源JDBCUtils.closeMyResources(conn, ps);}}/* 使用时 */public void deleteTest() {String sql delete from customers where name ?;String delete_name 张三;commonOps(sql, delete_name);}3 查Retrieval操作
ResultSet 的 next() 执行两步操作如果下一项不为空返回 true并将指针下移。和 Iterator 的方法相比相当于一起执行了 hasNext() 和 next()ResultSetMetaData 用于获取查询结果 ResultSet 的元数据在此获取的是表的 列数、列名。和 getColumnName() 相比getColumnLabel() 避免了表名和属性名不统一方便反射操作。如果表列名和属性名不一致则在输入sql语句时需要将查询的别名设置为属性名。ORM object relational mapping思想表的一行代表一个实例表的一列代表一个属性。
执行步骤
执行连接获取预编译 sql 的对象 ps 填充 sql 语句的占位符执行得到rs遍历 rs 的每一条记录使用反射为每条记录建立一个对象并填充对应属性值返回结果。 public T ListT allMultiResultCommonSelect(ClassT clazz, String sql, Object... args) {Connection conn null;PreparedStatement ps null;ResultSet rs null;ListT result new ArrayList(); // 返回结果try {conn JDBCUtils.getMyConnection();ps conn.prepareStatement(sql);for (int i 0; i args.length; i) {ps.setObject(i 1, args[i]);}rs ps.executeQuery();while (rs.next()) {// 获取元数据以得到结果的列数ResultSetMetaData rsmd rs.getMetaData();// 利用*反射*动态创建类T res clazz.newInstance();int col rsmd.getColumnCount();// 向customer中填入查询结果for (int i 0; i col; i) {Object property_value rs.getObject(i 1); // 获取当前属性的值String property_name rsmd.getColumnLabel(i 1); // 和 getColumnName相比getColumnLabel避免了表名和属性名不统一Field field res.getClass().getDeclaredField(property_name); // 使用反射更改属性值field.setAccessible(true);field.set(res, property_value);}result.add(res);}return result;} catch (Exception e) {e.printStackTrace();} finally {JDBCUtils.closeMyResources(conn, ps, rs);}return null;}4 批量插入操作
使用了两个技巧
批处理 sql 1.addBatch()、executeBatch()、clearBatch()。MySQL 服务器默认是关闭批处理的将 rewriteBatchedStatementstrue 写在配置文件的url后面以开启。关闭自动提交在所有的 sql 执行完之后统一提交。 public void testInsert3() {Connection conn null;PreparedStatement ps null;try {conn JDBCUtils.getConnection();//设置不允许自动提交数据conn.setAutoCommit(false);String sql insert into goods(name)values(?);ps conn.prepareStatement(sql);for(int i 1;i 1000000;i){ps.setObject(1, name_ i);//1.将一条sql加入到batchps.addBatch();if(i % 500 0){//2.执行batchps.executeBatch();//3.清空batchps.clearBatch();}}//提交数据conn.commit();} catch (Exception e) { e.printStackTrace();}finally{JDBCUtils.closeResource(conn, ps);}}四 数据库事务
1 事务
事务是一组逻辑操作单元使数据从一种状态变换到另一种状态。事务的操作要么全部执行要么全不执行。
2 事务的 ACID 性质
原子性DBMS保证事务的操作要么全部执行要么全不执行。一致性用户保证如果事务的程序正确并且事务启动时数据库处于一致状态则事务结束时数据库也要处于一致状态。隔离性DBMS保证一个事务的执行不受其它事务的影响。持久性DBMS保证事务一旦提交事务对数据库的修改一定全部持久地写到数据库中。
3 三种导致数据自动提交的操作
DDL(Data Definition Language) 操作一旦执行都会自动提交。DML(Data Manipulation Language) 默认情况下一旦执行就会自动提交。可以通过 conn.setAutoCommit(false) 的方式取消DML操作的自动提交。若此时 Connection 没有被关闭还可能被重复使用则需要恢复其自动提交状态 setAutoCommit(true)。尤其是在使用数据库连接池技术时执行close()方法前建议恢复自动提交状态。默认在关闭连接时自动提交。
4 MySQL 的四种隔离级别
READ UNCOMMITTED 读未提交可能发生脏读、不可重复读、幻读READ COMMITTED 读提交可能发生不可重复读、幻读REPEATABLE READ 可重复读可能发生幻读SERIALIZABLE 串行化不会发生以上情况但效率极低。
幻读举例例如第一个事务对一个表中的数据进行了修改比如这种修改涉及到表中的全部数据行。同时第二个事务也修改这个表中的数据这种修改是向表中插入一行新数据。那么就会发生操作第一个事务的用户发现表中还存在没有修改的数据行就好象发生了幻觉一样。
5 根据 ACID 改进实现
执行前关闭 connection 的自动提交。在哪个方法创建的资源在哪个方法关闭。finally 块负责方法的关闭事务的提交以及恢复 connection 的自动提交。catch 块负责 rollback。
五 DAO(Data Access Object) 逻辑架构 DAO 基类设置为泛型类因为要供所有的表使用。DAO 接口非泛型类因为一张表对应一个接口查询的返回对象类型是确定的。
六 数据库连接池
1 作用
为数据库连接建立一个“缓冲池”。预先在缓冲池中放入一定数量的连接当需要建立数据库连接时只需从“缓冲池”中取出一个使用完毕之后再放回去。当数据库访问结束后程序还是像以前一样关闭数据库连接conn.close(); 但没有关闭数据库的物理连接仅仅把数据库连接释放归还给数据库连接池。数据库连接池负责分配、管理和释放数据库连接允许应用程序重复使用一个现有的数据库连接而不是重新建立一个。数据库连接池在初始化时将创建一定数量的数据库连接放到连接池中。通过限制最小和最大的连接数保证数据库对连接数的控制。使用时数据库连接池只需创建一个其中的连接可以创建多个。创建数据库连接池可以用静态代码块进行初始化。
2 C3P0 数据库连接池
C3P0 是一个开源组织提供的一个数据库连接池速度相对较慢稳定性较好。
使用 ComboPooledDataSource 类代替之前使用的 DriverManager 进行连接的获取。 public void xmlTest () throws Exception {ComboPooledDataSource cpds new ComboPooledDataSource(my_config);Connection conn cpds.getConnection();}其中 c3p0-config.xml 的内容如下文件名不能更改
?xml version1.0 encodingUTF-8?
c3p0-confignamed-config namemy_config!-- 提供获取连接的4个基本信息 --property namedriverClasscom.mysql.cj.jdbc.Driver/propertyproperty namejdbcUrljdbc:mysql:///test/propertyproperty nameuserroot/propertyproperty namepassword123/property!-- 进行数据库连接池管理的基本信息 --!-- 当数据库连接池中的连接数不够时c3p0一次性向数据库服务器申请的连接数 --property nameacquireIncrement5/property!-- c3p0数据库连接池中初始化时的连接数 --property nameinitialPoolSize10/property!-- c3p0数据库连接池维护的最少连接数 --property nameminPoolSize10/property!-- c3p0数据库连接池维护的最多的连接数 --property namemaxPoolSize100/property!-- c3p0数据库连接池最多维护的Statement的个数 --property namemaxStatements50/property!-- 每个连接中可以最多使用的Statement的个数 --property namemaxStatementsPerConnection2/property/named-config
/c3p0-config3 DBCP 数据库连接池
Tomcat 的连接池采用该连接池实现。速度相对C3P0较快但存在 bug不够稳定。 Testpublic void propertiesTest() throws Exception {/* 加载配置文件 */Properties properties new Properties();// 获取输入流的方法1// FileInputStream is new FileInputStream(new File(src/dbcp.properties));// 方法2InputStream is ClassLoader.getSystemClassLoader().getResourceAsStream(dbcp.properties);properties.load(is);/* 创建连接池 */DataSource source BasicDataSourceFactory.createDataSource(properties);/* 获取连接 */Connection conn source.getConnection();}4 Druid 数据库连接池实用 * Testpublic void getConnectionTest () throws Exception {/* 加载配置文件 */Properties properties new Properties();InputStream is ClassLoader.getSystemClassLoader().getResourceAsStream(druid.properties);properties.load(is);/* 创建连接池 */DataSource source DruidDataSourceFactory.createDataSource(properties);/* 获取连接 */Connection conn source.getConnection();}七 Apache-DBUtils 实现 CRUD 操作 *
1 实现
public class ApacheUtilsTest {// 初始化数据库连接池private static DataSource source;static {InputStream is ClassLoader.getSystemClassLoader().getResourceAsStream(druid.properties);Properties properties new Properties();try {properties.load(is);ApacheUtilsTest.source DruidDataSourceFactory.createDataSource(properties);} catch (IOException e) {e.printStackTrace();} catch (Exception e) {e.printStackTrace();}}// 增删改Testpublic void insertTest() throws SQLException {QueryRunner queryRunner new QueryRunner();Connection conn ApacheUtilsTest.source.getConnection();String sql insert into customers (name, email, birth) values (?, ?, ?);int count queryRunner.update(conn, sql, 伍佰, 500gmail.com, 1966-08-08);System.out.println(count);conn.close();}// 查记录Testpublic void selectTest() throws SQLException {QueryRunner queryRunner new QueryRunner();Connection conn ApacheUtilsTest.source.getConnection();String sql select name, email from customers where id ?;/* 查询一条记录 */// ResultSetHandler 接口的一个实现类用于封装表的一条记录// BeanHandlerCustomer beanHandler new BeanHandler(Customer.class);// Customer customer queryRunner.query(conn, sql, beanHandler, 19);/* 查询多条记录 */// ResultSetHandler 接口的一个实现类用于封装表的多条记录返回结果相应地使用 ListCustomer 类型BeanListHandlerCustomer beanListHandler new BeanListHandler(Customer.class);ListCustomer customers queryRunner.query(conn, sql, beanListHandler, 18);/** 查询多条记录还有 MapListHandler将一条记录作为一个 Mapkey 是列名value 是列的具体值* 返回值类型是 ListMapString, Object** 同理查询一条记录的 MapHandler 的返回值类型是 MapString, Object** 所以xxHandler 代表查询的返回值类型是 xx ??* */System.out.println(customers);conn.close();}// 查特殊值使用 ScalarHandlerTestpublic void selectScalarTest() throws SQLException{QueryRunner queryRunner new QueryRunner();Connection conn ApacheUtilsTest.source.getConnection();String sql select count(*) from customers;ScalarHandler scalarHandler new ScalarHandler();Long count (Long) queryRunner.query(conn, sql, scalarHandler);System.out.println(count);}
}2 自定义 Handler
ResultSetHandler 是所有 Handler 实现的接口。自定义 Handler重写 ResultSetHandler 接口下的 handle() 方法即可。
class MyHandler implements ResultSetHandlerCustomer {Overridepublic Customer handle(ResultSet resultSet) throws SQLException {// 返回一条查询结果if (resultSet.next()) {int id resultSet.getInt(id);String name resultSet.getString(name);String email resultSet.getString(email);Date birth resultSet.getDate(birth);return new Customer(id, name, email, birth);} else {return null;}}}3 资源的关闭
import org.apache.commons.dbutils.DbUtils;DbUtils.closeQuietly(connection);
DbUtils.closeQuietly(preparedStatement);
DbUtils.closeQuietly(resultSet);