JDBC学习笔记(全)
概述
数据持久化
- 持久化(persistence):把数据保存到可掉电式存储设备中以供之后使用。大多数情况下,特别是企业级应用,数据持久化意味着将内存中的数据保存到硬盘上加以”固化”,而持久化的实现过程大多通过各种
关系数据库
来完成。 - 持久化的主要应用是将内存中的数据存储在关系型数据库中,当然也可以存储在磁盘文件、XML数据文件中。
java中的数据存储技术
- JDBC直接访问数据库
- JDO技术
- 第三方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 驱动程序需要实现的接口。这个接口是提供给数据库厂商使用的,不同数据库厂商提供不同的实现
步骤
- 获取Driver实现类对象
- 提供要连接的数据库
- 提供连接需要的用户名和密码
- 获取连接
下面将通过五种方式逐步进阶
方式一
@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学习笔记(全)相关推荐
- JDBC 学习笔记(一)—— 基础知识 + 分页技术
2019独角兽企业重金招聘Python工程师标准>>> 本文查阅方法: 1.查阅目录 -- 查阅本文目录,确定想要查阅的目录标题 2.快捷"查找" ...
- JDBC学习笔记03【JDBC事务管理、数据库连接池、JDBCTemplate】
黑马程序员-JDBC文档(腾讯微云)JDBC笔记.pdf:https://share.weiyun.com/Kxy7LmRm JDBC学习笔记01[JDBC快速入门.JDBC各个类详解.JDBC之CR ...
- JDBC学习笔记02【ResultSet类详解、JDBC登录案例练习、PreparedStatement类详解】
黑马程序员-JDBC文档(腾讯微云)JDBC笔记.pdf:https://share.weiyun.com/Kxy7LmRm JDBC学习笔记01[JDBC快速入门.JDBC各个类详解.JDBC之CR ...
- JDBC学习笔记01【JDBC快速入门、JDBC各个类详解、JDBC之CRUD练习】
黑马程序员-JDBC文档(腾讯微云)JDBC笔记.pdf:https://share.weiyun.com/Kxy7LmRm JDBC学习笔记01[JDBC快速入门.JDBC各个类详解.JDBC之CR ...
- JDBC学习笔记——Java语言与数据库的鹊桥
JDBC学习笔记--Java语言与数据库的鹊桥 JDBC(Java DataBase Connectivity):SUN公司提供的 一套操作数据库的标准规范,说白了就是用Java语言来操作数据 ...
- JDBC学习笔记(1)---B站尚硅谷宋红康
JDBC学习笔记(1)-B站尚硅谷宋红康 JDBC学习笔记(2)-B站尚硅谷宋红康 文章目录 软件架构方式介绍 JavaWeb技术概览 第1章:JDBC概述 1.1 数据的持久化 1.2 Java中的 ...
- JDBC 学习笔记 day02 用 PreparedStatement 实现 CRUD 操作
JDBC 学习笔记 day02 PreparedStatement 相比于 Statement 的好处 PreparedStatement 实现 增,删,改 操作 PreparedStatement ...
- JDBC学习笔记(六)
JDBC学习笔记(六) Thinkersky[Email:yanghuangming@rongji.com] 关于婚姻与爱情,一组5至10岁的美国小孩给出了他们的答案.也许听起来傻傻的,但是谁能说小娃 ...
- JDBC学习笔记(6)——获取自动生成的主键值处理Blob数据库事务处理
获取数据库自动生成的主键 [孤立的技术是没有价值的],我们这里只是为了了解具体的实现步骤:我们在插入数据的时候,经常会需要获取我们插入的这一行数据对应的主键值. 具体的代码实现: 1 /** 2 * ...
最新文章
- 利用Spring AOP与JAVA注解为系统增加日志功能
- linux环境下python的部署
- Python pip安装命令
- 【每周CV论文推荐】 初学目标检测必须要读的文章
- JS基础语法(05)-隐式数据类型转换
- mac下安装配置mongodb
- 解决Based on configured schedule, the given trigger ‘triggerGroupName_FREEZE_JOB.triggerName_FREEZE...
- LeetCode MySQL 1501. 可以放心投资的国家
- 了解 MongoDB 看这一篇就够了
- 让Fiddler能够检测到localhost的http数据
- hadoop之MapReduce的案例(多表关联)
- 算法导论第三版 21.2-3习题答案
- 网刻教程,无光驱装系统
- JAVA代码实现MD5加密算法
- Java 金额转换 阿拉伯数字金额转换成汉字大写金额
- 概率论的学习和整理11:伯努利试验的3种分布:0-1分支,几何分布, 二项分布
- [转载] 晓说——第1期:揭秘游戏规则奥斯卡走下“神坛“
- ads的项目下的文件全部消失不见了
- Excel中如何进行快速单位换算
- 小程序配置服务器域名不生效问题
热门文章
- 电梯调度 matlab,数学建模电梯调度问题.pdf
- MySQL判断字符串是否是数字
- java对象生成的时间_Java基础之一组有用的类——生成日期和时间(TryDateFormats)...
- 河北金融学院计算机与科学,河北金融学院计算机科学与技术专业2016年在河北理科高考录取最低分数线...
- 计算机三大科学理论是,近代科学最伟大的三大理论:进化论、量子论和计算论...
- java bcd码_java中BCD编码
- 博客园首页新随笔联系管理订阅订阅随笔- 610 文章- 0 评论- 83 阅读- 144万 Calendar时间获取天,周,月,季度,年度时间段
- Vue中mounted和created的区别
- vue + element-ui tab切换
- yum安装软件提示Another app is currently holding the yum lock