概述

数据持久化

  • 持久化(persistence):把数据保存到可掉电式存储设备中以供之后使用。大多数情况下,特别是企业级应用,数据持久化意味着将内存中的数据保存到硬盘上加以”固化”,而持久化的实现过程大多通过各种关系数据库来完成。
  • 持久化的主要应用是将内存中的数据存储在关系型数据库中,当然也可以存储在磁盘文件、XML数据文件中。

java中的数据存储技术

  1. JDBC直接访问数据库
  2. JDO技术
  3. 第三方O/R工具,如Hibernate, mybatis 等

什么是JDBC?

  • JDBC(Java Database Connectivity)是一个独立于特定数据库管理系统通用的SQL数据库存取和操作的公共接口(一组API),定义了用来访问数据库的标准Java类库,(java.sql,javax.sql)使用这个类库可以以一种标准的方法、方便地访问数据库资源
  • JDBC为访问不同的数据库提供了一种统一的途径,为开发者屏蔽了一些细节问题。
  • 由各种数据库管理系统的开发公司提供JDBC驱动,Java程序员直接使用JDBC连接这些DBMS,大大简化了开发的流程。

JDBC体系结构

  • 面向应用的API:Java API,抽象接口,供应用程序开发人员使用(连接数据库,执行SQL语句,获得结果)。
  • 面向数据库的API:Java Driver API,供开发商开发数据库驱动程序用。

JDBC API

JDBC API 是一系列的接口,它使得应用程序能够进行数据库联接,执行SQL语句,并且得到返回结果。

JDBC访问数据库的步骤

获取数据库连接

Driver接口

java.sql.Driver 接口是所有 JDBC 驱动程序需要实现的接口。这个接口是提供给数据库厂商使用的,不同数据库厂商提供不同的实现

步骤

  1. 获取Driver实现类对象
  2. 提供要连接的数据库
  3. 提供连接需要的用户名和密码
  4. 获取连接

下面将通过五种方式逐步进阶

方式一

 @Testpublic void test1() throws SQLException {// 获取Driver实现类对象Driver driver = new com.mysql.jdbc.Driver();// url:http://localhost:8080/gmall/keyboard.jpg// jdbc:mysql:协议// localhost:ip地址// 3306:默认mysql的端口号// test:test数据库String url = "jdbc:mysql://localhost:3306/test";// 将用户名和密码封装在Properties中Properties info = new Properties();info.setProperty("user","root");info.setProperty("password","073838");Connection connection = driver.connect(url,info);System.out.println(connection);}

运行结果:

com.mysql.jdbc.JDBC4Connection@449b2d27

方式一出现了第三方的API,因此引入方式二进行改进,使其有更好的移植性

方式二

public void test2() throws Exception {// 1.获取Driver实现类对象:使用反射Class clazz = Class.forName("com.mysql.jdbc.Driver");Driver driver = (Driver) clazz.newInstance();// 2.提供要连接的数据库String url = "jdbc:mysql://localhost:3306/test";// 3.提供连接需要的用户名和密码Properties info = new Properties();info.setProperty("user","root");info.setProperty("password","073838");// 4.获取连接Connection connection = driver.connect(url,info);System.out.println(connection);}

方式三:使用DriverManager替换Driver

DriverManager 类是驱动程序管理器类,负责管理驱动程序

 public void test3() throws Exception {// 1.获取Driver实现类对象:使用反射Class clazz = Class.forName("com.mysql.jdbc.Driver");Driver driver = (Driver) clazz.newInstance();// 2.提供另外三个连接的基本信息:String url = "jdbc:mysql://localhost:3306/test";String user = "root";String password = "073838";// 注册驱动DriverManager.registerDriver(driver);// 获取连接Connection connection = DriverManager.getConnection(url, user, password);System.out.println(connection);}

通常不用显式调用 DriverManager 类的 registerDriver() 方法来注册驱动程序类的实

例,因为 Driver 接口的驱动程序类包含了静态代码块,在这个静态代码块中,会

调用 DriverManager.registerDriver() 方法来注册自身的一个实例,因此对方式三进行改造

方式四:可以只是加载驱动,不用显式的注册驱动

public void test4() throws Exception {// 1.提供另外三个连接的基本信息:String url = "jdbc:mysql://localhost:3306/test";String user = "root";String password = "073838";// 2.加载DriverClass.forName("com.mysql.jdbc.Driver");// 3.获取连接Connection conn = DriverManager.getConnection(url, user, password);System.out.println(conn);}

Driver类加载的静态代码块:

static {try {java.sql.DriverManager.registerDriver(new Driver());} catch (SQLException E) {throw new RuntimeException("Can't register driver!");}}

方式四还暴露了账户名和密码,对次进行最后的改进:将数据库连接需要的4个基本信息声明在配置文件中,通过读取配置文件的方式,获取连接

方式五

配置文件:jdbc.propertoes(注意不要加空格)

user=root
password=073838
url=jdbc:mysql://localhost:3306/test
driver=com.mysql.jdbc.Driver

具体实现:

public void test5() throws Exception {//1.读取配置文件中的4个基本信息InputStream is = ConnectionTest.class.getClassLoader().getResourceAsStream("jdbc.properties");Properties properties = new Properties();properties.load(is);String user = properties.getProperty("user");String password = properties.getProperty("password");String url = properties.getProperty("url");String driverClass = properties.getProperty("driver");//2.加载驱动Class.forName("driver");//3.获取连接Connection connection = DriverManager.getConnection(url, user, password);System.out.println(connection);}

该方式的好处:

1.实现了数据与代码的分离。实现了解耦

2.如果需要修改配置文件信息,可以避免程序重新打包。

使用Statement操作数据库的弊端

​ 获取数据库的连接后,我们可以对数据库进行增删改查操作(CRUD),在 java.sql 包中有 3 个接口分别定义了对数据库的调用的不同方式:

Statement

​ ⇡PreparedStatement

​ ⇡CallableStatement

一般我们不使用Statement来访问数据库,因为会发生SQL注入的问题,下面通过例子来演示SQL注入问题

public void testLogin() {Scanner scanner = new Scanner(System.in);System.out.print("请输入用户名:");String user = scanner.nextLine();System.out.print("请输入密码:");String password = scanner.nextLine();//SELECT user,password FROM user_table WHERE user = '1' or ' AND password = '=1 or '1' = '1'String sql = "SELECT user,password FROM user_table WHERE user = '"+ user +"' AND password = '"+ password +"'";User returnUser = get(sql,User.class);if(returnUser != null){System.out.println("登录成功");}else{System.out.println("用户名不存在或密码错误");}}

当我们输入的用户名和密码非法时,可能不会被检测到,但却能对数据库进行访问进行非法操作。

  • SQL 注入是利用某些系统没有对用户输入的数据进行充分的检查,而在用户输入数据中注入非法的 SQL 语句段或命令(如:SELECT user, password FROM user_table WHERE user=‘a’ OR 1 = ’ AND password = ’ OR ‘1’ = ‘1’) ,从而利用系统的 SQL 引擎完成恶意行为的做法

  • 对于 Java 而言,要防范 SQL 注入,只要用 PreparedStatement(从Statement扩展而来) 取代 Statement 就可以了

使用PreparedStatement进行CRUD操作

提供整个过程获取数据库连接、关闭资源的工具类

package com.deserts.util;import java.io.InputStream;
import java.sql.*;
import java.util.Properties;public class JDBCUtils {/*** 获取数据库的连接* @return Connection* @throws Exception*/public static Connection getConnection() throws Exception {// 1.读取配置文件中的4个基本信息InputStream is = ClassLoader.getSystemClassLoader().getResourceAsStream("jdbc.properties");Properties properties = new Properties();properties.load(is);String user = properties.getProperty("user");String password = properties.getProperty("password");String url = properties.getProperty("url");String driverClass = properties.getProperty("driver");// 2.加载驱动Class.forName(driverClass);//3.获取连接Connection connection = DriverManager.getConnection(url, user, password);return connection;}/*** 关闭资源的操作* @param connection* @param ps*/public static void closeResource(Connection connection, PreparedStatement ps) {try {if(ps != null)ps.close();} catch (SQLException e) {e.printStackTrace();}try {if(connection != null)connection.close();} catch (SQLException e) {e.printStackTrace();}}/*** 关闭资源的操作* @param connection* @param ps* @param resultSet*/public static void closeResource(Connection connection, PreparedStatement ps, ResultSet resultSet) {try {if(ps != null)ps.close();} catch (SQLException e) {e.printStackTrace();}try {if(connection != null)connection.close();} catch (SQLException e) {e.printStackTrace();}try {if(resultSet != null)resultSet.close();} catch (SQLException e) {e.printStackTrace();}}
}

通用的增删改操作

主要思想:

获取数据库连接→预编译sql语句,获取PrepareStatement的实例ps→根据传入的可变参数填充占位符→调用ps.excute()执行→关闭资源

package com.deserts.crud;import com.deserts.util.JDBCUtils;
import org.junit.Test;import java.sql.Connection;
import java.sql.Date;
import java.sql.PreparedStatement;
import java.text.ParseException;
import java.text.SimpleDateFormat;public class PreparedStatementUpdateTest {//封装通用的增删改操作private void update(String sql, Object ...args) {Connection connection = null;PreparedStatement ps = null;try {//1.获取数据库连接connection = JDBCUtils.getConnection();//2.预编译sql语句,获取PrepareStatement的实例ps = connection.prepareStatement(sql);//3.填充占位符for (int i = 0; i < args.length ; i++) {ps.setObject(i+1,args[i]);}//4.执行ps.execute();} catch (Exception e) {e.printStackTrace();} finally {//5.关闭资源JDBCUtils.closeResource(connection, ps);}}//修改@Testpublic void test1(){String sql = "update `order` set order_name = ? where order_id = ?";update(sql,"MM",2);}//插入@Testpublic void test2() throws ParseException {String sql = "insert into `order` values(?,?,?)";SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");java.util.Date date = sdf.parse("2000-01-01");update(sql,5,"PP",new Date(date.getTime()));}//删除@Testpublic void test3(){String sql = "delete from `order` where order_id = ?";update(sql,5);}}

对Custmoer表的PreparedStatement查询操作

说明:本过程需要提供Customer对象,用于保存从表中取出的数据

对表中单个对象(一列)的特定查询操作测试

//对单个查询的操作测试@Testpublic void queryTest1(){Connection connection = null;PreparedStatement ps = null;ResultSet resultSet = null;try {//1.获取数据库连接connection = JDBCUtils.getConnection();String sql = "select id,name,email,birth from customers where id = ?";//2.预编译sql语句ps = connection.prepareStatement(sql);ps.setObject(1,5);//3.返回结果集resultSet = ps.executeQuery();//4.处理结果集if (resultSet.next()){//获取当前这条数据的各个字段值int id = resultSet.getInt(1);String name = resultSet.getString(2);String email = resultSet.getString(3);Date birth = resultSet.getDate(4);//将数据封装为对象Customer customer = new Customer(id, name, email, birth);System.out.println(customer);}} catch (Exception e) {e.printStackTrace();} finally {//关闭资源JDBCUtils.closeResource(connection,ps,resultSet);}}

查询结果:

对表中单个对象(一列)的通用查询操作测试

主要思想:

获取数据库连接→预编译sql语句,获取PrepareStatement的实例ps→根据传入的可变参数填充占位符→调用ps.excuteQuery(),获取结果集→根据结果集获取结果集的元数据→处理结果集→关闭资源

处理结果集的思路:

由结果集获取列值getObject()→由元数据获取列名getCulumnName()→通过反射给指定对象赋值→返回对象

package com.deserts.crud1;import com.deserts.bean.Customer;
import com.deserts.util.JDBCUtils;
import org.junit.Test;import java.lang.reflect.Field;
import java.sql.*;public class CustomerForQuery {/*** 针对Customer表一个对象的通用查询操作* @param sql* @param args* @return*/public Customer queryForCustomer(String sql, Object...args){Connection connection = null;PreparedStatement ps = null;ResultSet resultSet = null;try {//1.获取数据库连接connection = JDBCUtils.getConnection();//2.预编译sql语句ps = connection.prepareStatement(sql);//3.填充占位符,返回结果集for(int i = 0;i < args.length;i++){ps.setObject(i + 1, args[i]);}resultSet = ps.executeQuery();//4.获取结果集的元数据 :ResultSetMetaDataResultSetMetaData metaData = resultSet.getMetaData();//5.通过ResultSetMetaData获取结果集中的列数int columnCount = metaData.getColumnCount();//6.处理结果集if (resultSet.next()){Customer customer = new Customer();for (int i = 0; i < columnCount ; i++) {//获取列值Object columnValue = resultSet.getObject(i + 1);//获取每个列的列名String columnName = metaData.getColumnName(i + 1);//给customer对象指定的columnName属性,赋值为columnValue:通过反射Field field = customer.getClass().getDeclaredField(columnName);field.setAccessible(true);field.set(customer,columnValue);}return customer;}} catch (Exception e) {e.printStackTrace();} finally {JDBCUtils.closeResource(connection,ps,resultSet);}return null;}//测试通用查询操作@Testpublic void queryTest2(){String sql = "select id,name,email,birth from customers where id = ?";Customer customer = queryForCustomer(sql,5);System.out.println(customer);sql = "select id,name,email from customers where id = ?";Customer customer1 = queryForCustomer(sql, 10);System.out.println(customer1);}
}

查询结果:

对Order表的PreparedStatement查询操作

说明:本过程同样需要提供Order对象

主要思想:

该过程的思想与查询Customer表一致,需要注意的是,Order对象的属性名与order表的列名并不相同,这时需要在查询过程SQL语句中给列名取别名,而获取列的别名用的方法是getColumnTable()(没有别名时返回列名)

package com.deserts.crud1;import com.deserts.bean.Order;
import com.deserts.util.JDBCUtils;
import org.junit.Test;import java.lang.reflect.Field;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.util.ArrayList;public class OrderForQuery {private ArrayList<Order> orderQuery(String sql, Object...args){Connection connection = null;PreparedStatement ps = null;ResultSet resultSet = null;try {ArrayList<Order> list = new ArrayList<>();connection = JDBCUtils.getConnection();ps = connection.prepareStatement(sql);for (int i = 0; i < args.length ; i++) {ps.setObject(i+1,args[i]);}resultSet = ps.executeQuery();ResultSetMetaData metaData = resultSet.getMetaData();int columnCount = metaData.getColumnCount();while (resultSet.next()){Order order = new Order();for (int i = 0; i < columnCount; i++) {Object columnValue = resultSet.getObject(i + 1);String columnLabel = metaData.getColumnLabel(i+1);Field field = Order.class.getDeclaredField(columnLabel);field.setAccessible(true);field.set(order, columnValue);}list.add(order);}return list;} catch (Exception e) {e.printStackTrace();} finally {JDBCUtils.closeResource(connection,ps,resultSet);}return null;}@Testpublic void orderQueryTest(){String sql = "select order_id orderId,order_name orderName from `order` where order_id < ?";ArrayList<Order> orders = orderQuery(sql, 5);orders.forEach(System.out::println);}
}

查询结果:

对不同表的通用查询操作

由上面两个张表的查询,我们可以总结出对不同表的通用查询操作,其中查询多条记录只需将处理结果集的if(resultSet.next())改成while(resultSet.next())即可

package com.deserts.crud2;import com.deserts.bean.Customer;
import com.deserts.bean.Order;
import com.deserts.util.JDBCUtils;
import org.junit.Test;import java.lang.reflect.Field;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.util.ArrayList;public class PreparedStatementQueryTest {private <T> ArrayList<T> simpleQuery(Class<T> clazz,String sql, Object...args){Connection connection = null;PreparedStatement ps = null;ResultSet resultSet = null;try {ArrayList<T> list = new ArrayList<>();connection = JDBCUtils.getConnection();ps = connection.prepareStatement(sql);for (int i = 0; i < args.length ; i++) {ps.setObject(i+1,args[i]);}resultSet = ps.executeQuery();ResultSetMetaData metaData = resultSet.getMetaData();int columnCount = metaData.getColumnCount();while (resultSet.next()){T t = clazz.newInstance();for (int i = 0; i < columnCount; i++) {Object columnValue = resultSet.getObject(i + 1);String columnLabel = metaData.getColumnLabel(i+1);Field field = clazz.getDeclaredField(columnLabel);field.setAccessible(true);field.set(t, columnValue);}list.add(t);}return list;} catch (Exception e) {e.printStackTrace();} finally {JDBCUtils.closeResource(connection,ps,resultSet);}return null;}@Testpublic void queryTest(){String sql = "select id,name,email from customers where id < ?";ArrayList<Customer> customers = simpleQuery(Customer.class, sql, 10);customers.forEach(System.out::println);System.out.println();sql = "select order_id orderId,order_name orderName from `order` where order_id < ?";ArrayList<Order> orders = simpleQuery(Order.class, sql, 5);orders.forEach(System.out::println);}
}

查询结果:

查询过程图解

两种思想、两种技术

两种思想:

1.面向接口编程思想

2.ORM编程思想

两种技术:

1.JDBC元数据:ResultSetMetaData

2.通过反射,获取指定的属性,并赋值

Java与SQL数据类型对应表

操作BLOB类型字段

BLOB类型

插入BLOB类型的数据必须使用PreparedStatement,因为BLOB类型的数据无法使用字符串拼接写的。

MySQL中BLOB的四种类型:

注意点:

如果在指定了相关的Blob类型以后,还报错:xxx too large,那么在mysql的安装目录下,找my.ini文件加上如下的配置参数: max_allowed_packet=16M。同时注意:修改了my.ini文件之后,需要重新启动mysql服务。

使用PreparedStatement插入和查询BLOB类型字段

package com.deserts.bolbtest;import com.deserts.util.JDBCUtils;
import org.junit.Test;import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.sql.*;public class Blobtest1 {//像表中插入含有blob类型字段的记录@Testpublic void test1() throws Exception {Connection connection = JDBCUtils.getConnection();String sql = "insert into customers (name,email,birth,photo)values(?,?,?,?)";PreparedStatement ps = connection.prepareStatement(sql);ps.setObject(1,"deserts");ps.setObject(2,"deserts@126.com");ps.setObject(3,"1986-5-30");InputStream is = new FileInputStream("E:\\IDEA Projects\\deserts\\deserts.jpg");ps.setBlob(4,is);ps.execute();JDBCUtils.closeResource(connection,ps);System.out.println("插入成功!");}//查询表中含有blob字段的记录@Testpublic void test2(){Connection connection = null;PreparedStatement ps = null;ResultSet resultSet = null;InputStream is = null;FileOutputStream fos = null;try {connection = JDBCUtils.getConnection();String sql = "select id,name,email,birth,photo from customers where id = ?";ps = connection.prepareStatement(sql);ps.setInt(1,19);resultSet = ps.executeQuery();is = null;fos = null;if (resultSet.next()) {int id = resultSet.getInt("id");String name = resultSet.getString("name");String email = resultSet.getString("email");Date birth = resultSet.getDate("birth");Customer customer = new Customer(id,name,email,birth);System.out.println(customer);Blob photo = resultSet.getBlob("photo");is = photo.getBinaryStream();fos = new FileOutputStream("deserts1.jpg");byte[] buffer = new byte[1024];int len;while ((len = is.read(buffer)) != -1){fos.write(buffer,0,len);}}} catch (Exception e) {e.printStackTrace();} finally {JDBCUtils.closeResource(connection,ps,resultSet);if (is != null) {try {is.close();} catch (IOException e) {e.printStackTrace();}}if (fos != null) {try {fos.close();} catch (IOException e) {e.printStackTrace();}}}}
}

使用PreparedStatement批量插入

批量插入用到的方法

  • addBatch(String):添加需要批量处理的SQL语句或是参数;

  • executeBatch():执行批量处理语句;

  • clearBatch():清空缓存的数据

高效进行批量插入

方式一:使用Statement进行批量插入,效率过低

方式二:使用Preparedstament进行批量插入

 @Testpublic void test2(){Connection connection = null;PreparedStatement ps = null;try {long start = System.currentTimeMillis();connection = JDBCUtils.getConnection();String sql = "insert into goods(id,name) values(?,?)";ps = connection.prepareStatement(sql);for (int i = 1; i <= 20000; i++) {ps.setInt(1,i);ps.setString(2,"name_" + i);ps.execute();}long end = System.currentTimeMillis();System.out.println("花费的时间:" + (end - start));//718571} catch (Exception e) {e.printStackTrace();} finally {JDBCUtils.closeResource(connection,ps);}}

花费时间为:718571毫秒

方式三:使用批量插入相关方法进行优化

@Testpublic void test3(){Connection connection = null;PreparedStatement ps = null;try {long start = System.currentTimeMillis();connection = JDBCUtils.getConnection();String sql = "insert into goods(id,name) values(?,?)";ps = connection.prepareStatement(sql);for (int i = 1; i <= 20000; i++) {ps.setInt(1,i);ps.setString(2,"name_"+i);ps.addBatch();if (i % 500 == 0){ps.executeBatch();ps.clearBatch();}}long end = System.currentTimeMillis();System.out.println("花费的时间:" + (end - start));//3260} catch (Exception e) {e.printStackTrace();} finally {JDBCUtils.closeResource(connection,ps);}}

花费的时间:3260毫秒

方式四:设置事务,不自动提交事务

@Testpublic void test4(){Connection connection = null;PreparedStatement ps = null;try {long start = System.currentTimeMillis();connection = JDBCUtils.getConnection();connection.setAutoCommit(false);String sql = "insert into goods(id,name) values(?,?)";ps = connection.prepareStatement(sql);for (int i = 1; i <= 20000; i++) {ps.setInt(1,i);ps.setString(2,"name_"+i);ps.addBatch();if (i % 500 == 0){ps.executeBatch();ps.clearBatch();}}connection.commit();long end = System.currentTimeMillis();System.out.println("花费的时间:" + (end - start));//2213} catch (Exception e) {e.printStackTrace();} finally {JDBCUtils.closeResource(connection,ps);}}

花费的时间:2213毫秒

综上,我们采用方式四:使用批量提交相关方法,并设置事务不自动提交

java中使用事务

数据自动提交的情况

  • DDL操作一旦执行,都会自动提交;set autocommit = false 对DDL操作失效
  • DML默认情况下,一旦执行,就会自动提交。我们可以通过set autocommit = false的方式取消DML操作的自动提交。
  • 默认在关闭连接时,会自动的提交数据

Java中控制事务用到的方法

  • 获取当前连接的隔离级别:connection.getTransactionIsolation()
  • 设置数据库的隔离级别:Connection.TRANSACTION_READ_COMMITTED
  • 取消自动提交数据: connection.setAutoCommit(false)

考虑事务后的增删改查

通用的增删改操作—version 2.0 (考虑上事务)

 public int update(Connection connection, String sql, Object... args){PreparedStatement ps = null;try {ps = connection.prepareStatement(sql);for (int i = 0; i < args.length; i++) {ps.setObject(i+1,args[i]);}return ps.executeUpdate();} catch ( Exception e) {e.printStackTrace();} finally {JDBCUtils.closeResource(null,ps);}return 0;}

通用的查询操作,用于返回数据表中的一条记录(version 2.0:考虑上事务)

  public <T> T getInstance(Connection connection,Class<T> clazz,String sql, Object... args) throws Exception {PreparedStatement ps = null;try {ps = connection.prepareStatement(sql);ResultSet resultSet = ps.executeQuery();ResultSetMetaData metaData = resultSet.getMetaData();int columnCount = metaData.getColumnCount();if (resultSet.next()) {T t = clazz.newInstance();for (int i = 0; i < columnCount; i++) {Object columnValue = resultSet.getObject(i + 1);String name = metaData.getColumnLabel(i + 1);Field field = clazz.getDeclaredField(name);field.setAccessible(true);field.set(t,columnValue);}return t;}} catch (Exception e) {e.printStackTrace();} finally {JDBCUtils.closeResource(null,ps);}return null;}

DAO及其相关实现类

BaseDAO

package com.deserts.dao;import com.deserts.util.JDBCUtils;import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;public abstract class BaseDAO<T> {private Class<T> clazz = null;public BaseDAO() {//获取当前BaseDAO的子类继承的父类中的泛型Type genericSuperclass = this.getClass().getGenericSuperclass();ParameterizedType paramType = (ParameterizedType) genericSuperclass;Type[] typeArguments = paramType.getActualTypeArguments();//获取了父类的泛型参数clazz = (Class<T>) typeArguments[0];//泛型的第一个参数}// 通用的增删改操作---version 2.0 (考虑上事务)public int update(Connection conn, String sql, Object... args) {// sql中占位符的个数与可变形参的长度相同!PreparedStatement ps = null;try {// 1.预编译sql语句,返回PreparedStatement的实例ps = conn.prepareStatement(sql);// 2.填充占位符for (int i = 0; i < args.length; i++) {ps.setObject(i + 1, args[i]);// 小心参数声明错误!!}// 3.执行return ps.executeUpdate();} catch (Exception e) {e.printStackTrace();} finally {// 4.资源的关闭JDBCUtils.closeResource(null, ps);}return 0;}// 通用的查询操作,用于返回数据表中的一条记录(version 2.0:考虑上事务)public T getInstance(Connection conn, String sql, Object... args) {PreparedStatement ps = null;ResultSet rs = null;try {ps = conn.prepareStatement(sql);for (int i = 0; i < args.length; i++) {ps.setObject(i + 1, args[i]);}rs = ps.executeQuery();// 获取结果集的元数据 :ResultSetMetaDataResultSetMetaData rsmd = rs.getMetaData();// 通过ResultSetMetaData获取结果集中的列数int columnCount = rsmd.getColumnCount();if (rs.next()) {T t = clazz.newInstance();// 处理结果集一行数据中的每一个列for (int i = 0; i < columnCount; i++) {// 获取列值Object columValue = rs.getObject(i + 1);// 获取每个列的列名// String columnName = rsmd.getColumnName(i + 1);String columnLabel = rsmd.getColumnLabel(i + 1);// 给t对象指定的columnName属性,赋值为columValue:通过反射Field field = clazz.getDeclaredField(columnLabel);field.setAccessible(true);field.set(t, columValue);}return t;}} catch (Exception e) {e.printStackTrace();} finally {JDBCUtils.closeResource(null, ps, rs);}return null;}// 通用的查询操作,用于返回数据表中的多条记录构成的集合(version 2.0:考虑上事务)public List<T> getForList(Connection conn, String sql, Object... args) {PreparedStatement ps = null;ResultSet rs = null;try {ps = conn.prepareStatement(sql);for (int i = 0; i < args.length; i++) {ps.setObject(i + 1, args[i]);}rs = ps.executeQuery();// 获取结果集的元数据 :ResultSetMetaDataResultSetMetaData rsmd = rs.getMetaData();// 通过ResultSetMetaData获取结果集中的列数int columnCount = rsmd.getColumnCount();// 创建集合对象ArrayList<T> list = new ArrayList<T>();while (rs.next()) {T t = clazz.newInstance();// 处理结果集一行数据中的每一个列:给t对象指定的属性赋值for (int i = 0; i < columnCount; i++) {// 获取列值Object columValue = rs.getObject(i + 1);// 获取每个列的列名// String columnName = rsmd.getColumnName(i + 1);String columnLabel = rsmd.getColumnLabel(i + 1);// 给t对象指定的columnName属性,赋值为columValue:通过反射Field field = clazz.getDeclaredField(columnLabel);field.setAccessible(true);field.set(t, columValue);}list.add(t);}return list;} catch (Exception e) {e.printStackTrace();} finally {JDBCUtils.closeResource(null, ps, rs);}return null;}//用于查询特殊值的通用的方法public <E> E getValue(Connection conn, String sql, Object... args) {PreparedStatement ps = null;ResultSet rs = null;try {ps = conn.prepareStatement(sql);for (int i = 0; i < args.length; i++) {ps.setObject(i + 1, args[i]);}rs = ps.executeQuery();if (rs.next()) {return (E) rs.getObject(1);}} catch (SQLException e) {e.printStackTrace();} finally {JDBCUtils.closeResource(null, ps, rs);}return null;}
}

CustomerDAO

package com.deserts.dao;import com.deserts.bean.Customer;import java.sql.Connection;
import java.sql.Date;
import java.util.List;/*** @ClassName CustomerDAO* @Description 此接口用于规范针对于customers表的常用操作* @Author deserts* @Date 2020/8/2 16:53*/
public interface CustomerDAO {/*** @Description 将customer对象添加到数据库中* @Date 2020/8/2 16:59* [connection, customer]* @return void**/void insert(Connection connection, Customer customer);/*** @Description // 针对指定的id,删除表中的一条记录* @Date 2020/8/2 17:00* [connection, id] * @return void**/void deleteById(Connection connection,int id);/*** @Description //针对内存中的customer对象,去修改数据表中指定的记录* @Date 2020/8/2 17:01* [connection, customer] * @return void**/void update(Connection connection,Customer customer);/*** @Description //查询表中指定id的customer对象* @Date 2020/8/2 17:01* [connection, id]* @return com.deserts.bean.Customer**/Customer getCustomerById(Connection connection,int id);/*** @Description //获取表中所有customer对象* @Date 2020/8/2 17:02* [connection]* @return java.util.List<com.deserts.bean.Customer>**/List<Customer> getAll(Connection connection);/*** @Description //返回表中customer对象的数目* @Date 2020/8/2 17:03* [connection]* @return java.lang.Long**/Long getCount(Connection connection);/*** @Description //获取最大生日* @Date 2020/8/2 17:04* [connection]* @return java.sql.Date**/Date getMaxBirth(Connection connection);
}

CustomerDAOImpl

package com.deserts.dao;import com.deserts.bean.Customer;import java.sql.Connection;
import java.sql.Date;
import java.util.List;/*** @ClassName CustomerDAOImpl* @Description CustomerDAO的实现类,用于操作customers表* @Author deserts* @Date 2020/8/2 17:05*/
public class CustomerDAOImpl extends BaseDAO<Customer> implements CustomerDAO{@Overridepublic void insert(Connection connection, Customer customer) {String sql = "insert into customers(name,email,birth) values(?,?,?)";update(connection,sql,customer.getName(),customer.getEmail(),customer.getBirth());}@Overridepublic void deleteById(Connection connection, int id) {String sql = "delete from customers where id = ?";update(connection,sql,id);}@Overridepublic void update(Connection connection, Customer customer) {String sql = "update customers set name = ?,email = ?, birth = ? where id = ?";update(connection,sql,customer.getName(),customer.getEmail(),customer.getBirth(),customer.getId());}@Overridepublic Customer getCustomerById(Connection connection, int id) {String sql = "select id,name,email,birth from customers where id = ?";return getInstance(connection, sql, id);}@Overridepublic List<Customer> getAll(Connection connection) {String sql = "select id,name,email,birth from customers";return getForList(connection,sql);}@Overridepublic Long getCount(Connection connection) {String sql = "select count(*) from customers";return getValue(connection,sql);}@Overridepublic Date getMaxBirth(Connection connection) {String sql = "select max(birth) from customers";return getValue(connection, sql);}
}

数据库连接池

druid数据库连接池(重要)

 public Connection getConnection() throws Exception {Properties properties = new Properties();InputStream is = ClassLoader.getSystemClassLoader().getResourceAsStream("druid.properties");properties.load(is);DataSource source = DruidDataSourceFactory.createDataSource(properties);Connection connection = source.getConnection();return connection;}

C3P0数据库连接池

 public Connection getConnection() throws SQLException {ComboPooledDataSource cpds = new ComboPooledDataSource("hellc3p0");Connection conn = cpds.getConnection();return conn;}

DBCP数据库连接池

public Connection getConnection() throws Exception {Properties properties = new Properties();FileInputStream is = new FileInputStream(new File("src\\dbcp.properties"));properties.load(is);DataSource source = BasicDataSourceFactory.createDataSource(properties);Connection connection = source.getConnection();return connection;}

注意点:若要将这三种方式添加到工具类中使用,应将线程池声明到静态方法外,并用静态代码块进行初始化

Apache-DBUtils实现CRUD操作

Apache-DBUtils简介

  • commons-dbutils 是 Apache 组织提供的一个开源 JDBC工具类库,它是对JDBC的简单封装,学习成本极低,并且使用dbutils能极大简化jdbc编码的工作量,同时也不会影响程序的性能。
  • API介绍:
    • org.apache.commons.dbutils.QueryRunner
    • org.apache.commons.dbutils.ResultSetHandler
    • 工具类:org.apache.commons.dbutils.DbUtils

DBUtils类关闭资源

增删改

以插入为例:

@Testpublic void insertTest(){Connection connection = null;try {QueryRunner runner = new QueryRunner();connection = JDBCUtils.getConnection();String sql = "insert into customers(name,email,birth)values(?,?,?)";int update = runner.update(connection, sql, "lala", "lala@126.com", "1995-01-01");System.out.println("插入了" + update + "条记录!");} catch (Exception e) {e.printStackTrace();} finally {DbUtils.closeQuietly(connection);}}

查询

ResultSetHandler接口及实现类

  • 该接口用于处理 java.sql.ResultSet,将数据按要求转换为另一种形式。

  • ResultSetHandler 接口提供了一个单独的方法:Object handle (java.sql.ResultSet .rs)。

  • 主要实现类:

    • ArrayHandler:把结果集中的第一行数据转成对象数组。

    • ArrayListHandler:把结果集中的每一行数据都转成一个数组,再存放到List中。

    • BeanHandler:将结果集中的第一行数据封装到一个对应的JavaBean实例中。

    • BeanListHandler:将结果集中的每一行数据都封装到一个对应的JavaBean实例中,存放到List里。

    • ColumnListHandler:将结果集中某一列的数据存放到List中。

    • KeyedHandler(name):将结果集中的每一行数据都封装到一个Map里,再把这些map再存到一个map

      里,其key为指定的key。

    • **MapHandler:**将结果集中的第一行数据封装到一个Map里,key是列名,value就是对应的值。

    • MapListHandler:将结果集中的每一行数据都封装到一个Map里,然后再存放到List。

    • ScalarHandler:查询单个值对象

查询单条记录

 @Testpublic void queryTest1(){Connection connection = null;try {QueryRunner runner = new QueryRunner();connection = JDBCUtils.getConnection();String sql = "select id,name,email,birth from customers where id = ?";BeanHandler<Customer> handler = new BeanHandler<>(Customer.class);Customer customer = runner.query(connection, sql, handler, 19);System.out.println(customer);} catch (Exception e) {e.printStackTrace();} finally {DbUtils.closeQuietly(connection);}}

查询多条记录

 @Testpublic void queryTest2(){Connection connection = null;try {QueryRunner runner = new QueryRunner();connection = JDBCUtils.getConnection();String sql = "select id,name,email,birth from customers where id < ?";BeanListHandler<Customer> handler = new BeanListHandler<>(Customer.class);List<Customer> list = runner.query(connection, sql, handler, 10);list.forEach(System.out::println);} catch (Exception e) {e.printStackTrace();} finally {DbUtils.closeQuietly(connection);}}

查询特殊值

@Testpublic void queryTest3(){Connection conn = null;try {QueryRunner runner = new QueryRunner();conn = JDBCUtils.getConnection();String sql = "select count(*) from customers";ScalarHandler handler = new ScalarHandler();long count = (long)runner.query(conn, sql, handler);System.out.println("总记录数:"+count);} catch (Exception e) {e.printStackTrace();} finally {DbUtils.closeQuietly(conn);}}

dler, 19);
System.out.println(customer);
} catch (Exception e) {
e.printStackTrace();
} finally {
DbUtils.closeQuietly(connection);
}
}


#### 查询多条记录```java@Testpublic void queryTest2(){Connection connection = null;try {QueryRunner runner = new QueryRunner();connection = JDBCUtils.getConnection();String sql = "select id,name,email,birth from customers where id < ?";BeanListHandler<Customer> handler = new BeanListHandler<>(Customer.class);List<Customer> list = runner.query(connection, sql, handler, 10);list.forEach(System.out::println);} catch (Exception e) {e.printStackTrace();} finally {DbUtils.closeQuietly(connection);}}

查询特殊值

@Testpublic void queryTest3(){Connection conn = null;try {QueryRunner runner = new QueryRunner();conn = JDBCUtils.getConnection();String sql = "select count(*) from customers";ScalarHandler handler = new ScalarHandler();long count = (long)runner.query(conn, sql, handler);System.out.println("总记录数:"+count);} catch (Exception e) {e.printStackTrace();} finally {DbUtils.closeQuietly(conn);}}

JDBC学习笔记(全)相关推荐

  1. JDBC 学习笔记(一)—— 基础知识 + 分页技术

    2019独角兽企业重金招聘Python工程师标准>>> 本文查阅方法:     1.查阅目录 -- 查阅本文目录,确定想要查阅的目录标题     2.快捷"查找" ...

  2. JDBC学习笔记03【JDBC事务管理、数据库连接池、JDBCTemplate】

    黑马程序员-JDBC文档(腾讯微云)JDBC笔记.pdf:https://share.weiyun.com/Kxy7LmRm JDBC学习笔记01[JDBC快速入门.JDBC各个类详解.JDBC之CR ...

  3. JDBC学习笔记02【ResultSet类详解、JDBC登录案例练习、PreparedStatement类详解】

    黑马程序员-JDBC文档(腾讯微云)JDBC笔记.pdf:https://share.weiyun.com/Kxy7LmRm JDBC学习笔记01[JDBC快速入门.JDBC各个类详解.JDBC之CR ...

  4. JDBC学习笔记01【JDBC快速入门、JDBC各个类详解、JDBC之CRUD练习】

    黑马程序员-JDBC文档(腾讯微云)JDBC笔记.pdf:https://share.weiyun.com/Kxy7LmRm JDBC学习笔记01[JDBC快速入门.JDBC各个类详解.JDBC之CR ...

  5. JDBC学习笔记——Java语言与数据库的鹊桥

    JDBC学习笔记--Java语言与数据库的鹊桥     JDBC(Java DataBase Connectivity):SUN公司提供的 一套操作数据库的标准规范,说白了就是用Java语言来操作数据 ...

  6. JDBC学习笔记(1)---B站尚硅谷宋红康

    JDBC学习笔记(1)-B站尚硅谷宋红康 JDBC学习笔记(2)-B站尚硅谷宋红康 文章目录 软件架构方式介绍 JavaWeb技术概览 第1章:JDBC概述 1.1 数据的持久化 1.2 Java中的 ...

  7. JDBC 学习笔记 day02 用 PreparedStatement 实现 CRUD 操作

    JDBC 学习笔记 day02 PreparedStatement 相比于 Statement 的好处 PreparedStatement 实现 增,删,改 操作 PreparedStatement ...

  8. JDBC学习笔记(六)

    JDBC学习笔记(六) Thinkersky[Email:yanghuangming@rongji.com] 关于婚姻与爱情,一组5至10岁的美国小孩给出了他们的答案.也许听起来傻傻的,但是谁能说小娃 ...

  9. JDBC学习笔记(6)——获取自动生成的主键值处理Blob数据库事务处理

    获取数据库自动生成的主键 [孤立的技术是没有价值的],我们这里只是为了了解具体的实现步骤:我们在插入数据的时候,经常会需要获取我们插入的这一行数据对应的主键值. 具体的代码实现: 1 /** 2 * ...

最新文章

  1. 利用Spring AOP与JAVA注解为系统增加日志功能
  2. linux环境下python的部署
  3. Python pip安装命令
  4. 【每周CV论文推荐】 初学目标检测必须要读的文章
  5. JS基础语法(05)-隐式数据类型转换
  6. mac下安装配置mongodb
  7. 解决Based on configured schedule, the given trigger ‘triggerGroupName_FREEZE_JOB.triggerName_FREEZE...
  8. LeetCode MySQL 1501. 可以放心投资的国家
  9. 了解 MongoDB 看这一篇就够了
  10. 让Fiddler能够检测到localhost的http数据
  11. hadoop之MapReduce的案例(多表关联)
  12. 算法导论第三版 21.2-3习题答案
  13. 网刻教程,无光驱装系统
  14. JAVA代码实现MD5加密算法
  15. Java 金额转换 阿拉伯数字金额转换成汉字大写金额
  16. 概率论的学习和整理11:伯努利试验的3种分布:0-1分支,几何分布, 二项分布
  17. [转载] 晓说——第1期:揭秘游戏规则奥斯卡走下“神坛“
  18. ads的项目下的文件全部消失不见了
  19. Excel中如何进行快速单位换算
  20. 小程序配置服务器域名不生效问题

热门文章

  1. 电梯调度 matlab,数学建模电梯调度问题.pdf
  2. MySQL判断字符串是否是数字
  3. java对象生成的时间_Java基础之一组有用的类——生成日期和时间(TryDateFormats)...
  4. 河北金融学院计算机与科学,河北金融学院计算机科学与技术专业2016年在河北理科高考录取最低分数线...
  5. 计算机三大科学理论是,近代科学最伟大的三大理论:进化论、量子论和计算论...
  6. java bcd码_java中BCD编码
  7. 博客园首页新随笔联系管理订阅订阅随笔- 610 文章- 0 评论- 83 阅读- 144万 Calendar时间获取天,周,月,季度,年度时间段
  8. Vue中mounted和created的区别
  9. vue + element-ui tab切换
  10. yum安装软件提示Another app is currently holding the yum lock