JDBC
前言:看懂项目—学习java反射! 还要学会maven基础!
去尚硅谷下载宋红康老师的资料学习。。
1. Driver接口-连接数据库(5种方式,最后一种最重要)
首先你要学会用maven创建模块。再次不做过多赘述了。
几点注意事项:
-
mysql和对应的驱动版本要一致(我的mysql8.0.29,驱动也是如此)
-
关于url后面 要不要加入代码注释的内容看情况,作用是(驱动和mysql的不一致导致有的参数要变,具体实践具体查去)
上代码:
package com.jdbc;
import com.mysql.cj.jdbc.Driver;
import com.mysql.cj.jdbc.integration.c3p0.MysqlConnectionTester;
import org.junit.Test;
import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.Properties;
public class Driverconnect {
//方式一:最普通的
@Test
public void testConnection1() throws SQLException {
Driver driver = new Driver();
String url = "jdbc:mysql://localhost:3306/jdbc";
Properties info = new Properties();
info.setProperty("user", "root");
info.setProperty("password", "123456");
Connection connect = driver.connect(url, info);
System.out.println(connect);
}
//方式二--进阶 用反射(方式一第一步还不够模块化)
@Test
public void testConnection2() throws ClassNotFoundException, InstantiationException, IllegalAccessException, SQLException {
//1. 获取Driver实现类对象,使用反射
Class<?> aClass = Class.forName("com.mysql.cj.jdbc.Driver");
Driver driver = (Driver) aClass.newInstance();
//?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
//2.连接数据库,用户名和密码
String url = "jdbc:mysql://localhost:3306/jdbc";
Properties info = new Properties();
info.setProperty("user", "root");
info.setProperty("password", "123456");
//3.获取连接
Connection connection = driver.connect(url, info);
System.out.println(connection);
}
//方式三:用DriverManager实现数据库的连接。体会获取连接必要的4个基本要素。
@Test
public void testConnection() throws ClassNotFoundException, InstantiationException, IllegalAccessException, SQLException {
//?useSSL=false&useUnicode=true&characterEncoding=utf-8&useLegacyDatetimeCode=false&serverTimezone=UTC
//1.四个要素
String url = "jdbc:mysql://localhost:3306/jdbc?";
String user = "root";
String password = "123456";
String driverName = "com.mysql.cj.jdbc.Driver";
//2.实例化Driver
Class<?> aClass = Class.forName(driverName);
Driver driver = (Driver) aClass.newInstance();
//3.注册Driver驱动
DriverManager.registerDriver(driver);
//4.获取连接
Connection connection = DriverManager.getConnection(url, user, password);
System.out.println(connection);
}
//方式四:-可省略版
@Test
public void connnection4() throws ClassNotFoundException, SQLException {
//1.需要的四个要素
String url = "jdbc:mysql://localhost:3306/jdbc";
String user = "root";
String password = "123456";
String driverClass = "com.mysql.cj.jdbc.Driver";//接口位置
//2.加载驱动
Class.forName(driverClass);
//3.获取连接
Connection connection = DriverManager.getConnection(url, user, password);
System.out.println(connection);
}
//方式五 --final最终版本 --加载配置文件的方法
@Test
public void finalconnection() throws IOException, ClassNotFoundException, SQLException {
//1. 加载配置文件
InputStream is = Driverconnect.class.getClassLoader().getResourceAsStream("properties");
// --Driverconnect 当前类
// * --Driverconnect.class.getClassLoader() 该加载器是系统加载器,--自定义的类都是系统加载器
// * --getResourceAsStream指定文件路径 默认是src下
Properties properties = new Properties();
properties.load(is);
//2.读取配置信息
String user = properties.getProperty("user");
String password = properties.getProperty("password");
String url = properties.getProperty("url");
String driverClass = properties.getProperty("driverClass");
//3.加载驱动
Class.forName(driverClass);
//4.获取连接
Connection connection = DriverManager.getConnection(url, user, password);
System.out.println(connection);
}
}
//************其中final版本的配置文件有讲究的
方式五为最终的版本:通过读取配置文件来(+一些省略写法)完成jdbc的连接
这里需要注意的是:配置文件properties要放在maven工作区的resource下(不然总是无法识别到配置文件的信息)
下面为配置文件的内容:配置文件就是一个简单的file文件,一定要放在resource下面啊!
user=root
password=123456
url=jdbc:mysql://localhost:3306/jdbc
driverClass=com.mysql.cj.jdbc.Driver
# 配置文件不能有空格,会有歧义
运行效果图片:
学习遇到的问题
- @Test单元格报错:首先你要创建一个maven项目,然后再里面加入junit的驱动
具体的方法参考我之前的maven学习 - 我的mysql为8.0.29 使用对应的驱动也应该是8.0.29
- 报错:java.sql.SQLException: The server time zone value ‘Öйú±ê׼ʱ¼ä’ is unrecognized or represents more than one time zone.
解决方法:这个有点忘记了,解决方法可能是:1.导包的问题,用最新的import com.mysql.cj.jdbc.Driver; 2.驱动和mysql版本两者的版本要对应
- 又遇到问题了
解决方法:很多因素!
参考了这个大佬的思路,首先保证驱动和mysql版本的一致;其次是端口之类的问题(我个人是这两个问题,吧3306和3360搞错了tnnd)
2. PreparedStatement实现增删改查
首先一提Statement的弊端:
- 存在拼串操作
- 窜在sql 注入问题(网上查一下就懂了)
所以引入PreparedStatement,先预编译sql语句,涉及到占位符?
(这里的问号不是问号的意思,他是一种符号表示的是占位符!)
2.1 常规到通用 — 增删改
常规的方法:以增加为例(向数据库增加一条数据)
上代码:
package com.statement;
import com.jdbc.Driverconnect;
import org.junit.Test;
import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Properties;
public class PreparedStatementOpration {
/*
* 插入一段数据
* */
@Test
public void testInsert() throws IOException, ClassNotFoundException, SQLException, ParseException {
//1. 加载配置文件
InputStream is = Driverconnect.class.getClassLoader().getResourceAsStream("properties");
Properties properties = new Properties();
properties.load(is);
//2.读取配置信息
String user = properties.getProperty("user");
String password = properties.getProperty("password");
String url = properties.getProperty("url");
String driverClass = properties.getProperty("driverClass");
//3.加载驱动
Class.forName(driverClass);
//4.获取连接
Connection connection = DriverManager.getConnection(url, user, password);
System.out.println(connection);
//5.预编译sql语句
String sql = "insert into customers(name,email,birth)values(?,?,?)";//?是占位符的意思
PreparedStatement ps = connection.prepareStatement(sql);
//6.填充占位符
ps.setString(1, "文大牛");
ps.setString(2, "wts@666.com");
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
Date date = simpleDateFormat.parse("2001-2-28");
ps.setDate(3, new java.sql.Date(date.getTime()));
//7.执行
ps.execute();
//8.关闭
ps.close();
connection.close();
}
}
效果如下:
由此引出通用代码:
首先是固定的连接数据库和关闭操作的部分
上代码:
package com.statement;
/*连接固定代码*/
import com.jdbc.Driverconnect;
import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;
public class JDBCUtils {
/*连接部分*/
public static Connection getConnection() throws IOException, ClassNotFoundException, SQLException {
//1. 加载配置文件
InputStream is = Driverconnect.class.getClassLoader().getResourceAsStream("properties");
Properties properties = new Properties();
properties.load(is);
//2.读取配置信息
String user = properties.getProperty("user");
String password = properties.getProperty("password");
String url = properties.getProperty("url");
String driverClass = properties.getProperty("driverClass");
//3.加载驱动
Class.forName(driverClass);
//4.获取连接
Connection connection = DriverManager.getConnection(url, user, password);
return connection;
}
//*关闭流
public static void closeResource(Connection conn, Statement ps) throws SQLException {
if (conn != null) {
conn.close();
}
if (ps != null) {
ps.close();
}
}
}
总结上述:上面的代码是一个泛性的提取,连接数据库和关闭资源是重复的代码,所以把他们全都提取出来单独写成一个类。(后续关于资源关闭可能用到代码的重载)
其次是通用代码:
解释一下通用代码(在另一个主类中写的),通过JDBCUtils获取连接,然后实用preparedstatement的方式,先预编译sql语句(此处的sql是参数传进去的,后续自己定义);另外填充占用符的代码 ,sql占位符的个数就是数组的长度。
public void update(String sql, Object... args) throws SQLException, IOException, ClassNotFoundException {
//!!!需要注意的是:sql里面占位符? == 可变参数数组args[]长度相同
//1.获取连接
Connection connection = JDBCUtils.getConnection();
//2.预编译sql语句
PreparedStatement ps = connection.prepareStatement(sql);
//3.填充占位符
for (int i = 0; i < args.length; i++) {
ps.setObject(i + 1, args[i]);
}
//4.执行
ps.execute();
//5.关闭
JDBCUtils.closeResource(connection, ps);
}
测试:
@Test //--尝试通用操作--删除
public void deleteTest() throws SQLException, IOException, ClassNotFoundException {
String sql = "delete from customers where id = ?";
update(sql,18);
}
–效果如下
2.2 查询 — 通用
针对某个特定表格的通用查询。
所谓通用指的是查询字段的任意多少
通用 — 查询 — 针对Customers表
查询比较特殊,最终的结果要显示出来。 所以不是void,选择的是Customer类。
上代码:
1.ORM编程思想:
Customer类:
package com.statement;
import java.sql.Date;
public class Customer {
private int id;
private String name;
private String email;
private Date birth;
public Customer() {
}
public Customer(int id, String name, String email, Date birth) {
this.id = id;
this.name = name;
this.email = email;
this.birth = birth;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public Date getBirth() {
return birth;
}
public void setBirth(Date birth) {
this.birth = birth;
}
@Override
public String toString() {
return "Customer{" +
"id=" + id +
", name='" + name + '\'' +
", email='" + email + '\'' +
", birth=" + birth +
'}';
}
}
2.JDBCUtils — 关闭资源,方法重载
package com.statement;
/*连接固定代码*/
import com.jdbc.Driverconnect;
import java.io.IOException;
import java.io.InputStream;
import java.sql.*;
import java.util.Properties;
public class JDBCUtils {
/*连接部分*/
public static Connection getConnection() throws IOException, ClassNotFoundException, SQLException {
//1. 加载配置文件
InputStream is = Driverconnect.class.getClassLoader().getResourceAsStream("properties");
Properties properties = new Properties();
properties.load(is);
//2.读取配置信息
String user = properties.getProperty("user");
String password = properties.getProperty("password");
String url = properties.getProperty("url");
String driverClass = properties.getProperty("driverClass");
//3.加载驱动
Class.forName(driverClass);
//4.获取连接
Connection connection = DriverManager.getConnection(url, user, password);
return connection;
}
//*关闭资源
public static void closeResource(Connection conn, Statement ps) throws SQLException {
if (conn != null) {
conn.close();
}
if (ps != null) {
ps.close();
}
}
//关闭资源--方法重载
public static void closeResource(Connection conn, Statement ps, ResultSet rs) throws SQLException {
if (conn != null) {
conn.close();
}
if (ps != null) {
ps.close();
}
if (rs != null) {
rs.close();
}
}
}
3.查询通用—针对表格Customers
//--查询通用
public Customer queryForCustomers(String sql, Object... args) throws SQLException, IOException, ClassNotFoundException, NoSuchFieldException, IllegalAccessException {
//1.获取链接
Connection connection = JDBCUtils.getConnection();
//2.设置占位符
PreparedStatement ps = connection.prepareStatement(sql);
for (int i = 0; i < args.length; i++) {
ps.setObject(i + 1, args[i]);
}
//3.执行,并返回结果集
ResultSet rs = ps.executeQuery();
//4.处理结果集
//获取结果集的元数据
ResultSetMetaData rsmd = rs.getMetaData();
//获取列数
int columnCount = rsmd.getColumnCount();
if (rs.next()) {//查一条数据--查询一行数据(很多数据就用while) --- .next()有点像迭代器,
Customer customer = new Customer(); //ORM
/*ORM编程思想: ----将表中的数据封装成一个对象
* 一个表对应 一个java类
* 表中的一条记录对应 java类的一个对象
* 表中的一个字段对应 java类中的一个属性*/
for (int i = 0; i < columnCount; i++) {
Object value = rs.getObject(i + 1);//得到列的值 任然是从1开始的!
//获取结果集的列名
String columnName = rsmd.getColumnName(i + 1);
//反射-- 给cust对象 指定的columnName属性,赋值value:通过反射
Field declaredField = Customer.class.getDeclaredField(columnName);
declaredField.setAccessible(true);
declaredField.set(customer, value);
}
return customer;
}
//资源的关闭--方法重载
JDBCUtils.closeResource(connection, ps, rs);
return null;
}
4.测试+效果:
@Test //查询通用测试
public void testQuery() throws SQLException, IOException, NoSuchFieldException, ClassNotFoundException, IllegalAccessException {
String sql = "select id, name, email, birth from customers where id = ?";
Customer customer = queryForCustomers(sql, 21);
System.out.println(customer);
}
通用 — 查询 — 针对Order表
- 需要注意的是,order作为一个敏感词汇(对于sql来说),如何处理?
- order_id,order_name,order_date用的别称orderId,orderName,orderDate如何处理?
上代码:
1.Order类(ORM编程思维):需要注意的是这里的属性已更换别名
package com.statement;
import java.sql.Date;
/**
* 针对order表格的查询
*/
public class Order {
private int orderId;
private String orderName;
private Date orderDate;
public Order() {
}
public Order(int orderId, String orderName, Date orderDate) {
this.orderId = orderId;
this.orderName = orderName;
this.orderDate = orderDate;
}
public int getOrderId() {
return orderId;
}
public void setOrderId(int orderId) {
this.orderId = orderId;
}
public String getOrderName() {
return orderName;
}
public void setOrderName(String orderName) {
this.orderName = orderName;
}
public Date getOrderDate() {
return orderDate;
}
public void setOrderDate(Date orderDate) {
this.orderDate = orderDate;
}
@Override
public String toString() {
return "Order{" +
"orderId=" + orderId +
", orderName='" + orderName + '\'' +
", orderDate=" + orderDate +
'}';
}
}
2.order表的通用查询 + 验证
/**
* 针对order表格的通用查询
*/
public Order queryForOrder(String sql, Object... args) throws SQLException, IOException, ClassNotFoundException, NoSuchFieldException, IllegalAccessException {
//1.获取连接
Connection connection = JDBCUtils.getConnection();
//2.设置占位符,预编译sql语句
PreparedStatement ps = connection.prepareStatement(sql);
for (int i = 0; i < args.length; i++) {
ps.setObject(i + 1, args[i]); //第i+1个占位符(从1开始计)--对应args[i](从0开始计数)
}
//3.执行
ResultSet rs = ps.executeQuery();
//4.处理结果集
//获取元数据
ResultSetMetaData rsmd = rs.getMetaData();
//获取列数
int columnCount = rsmd.getColumnCount();
if (rs.next()) {
Order order = new Order();
for (int i = 0; i < columnCount; i++) {
Object value = rs.getObject(i + 1);//获取列的值,(从1开始计
//获取列的别名 ---- 这里区别于获取表的列名,而是获取表的别名。。
String columnLabel = rsmd.getColumnLabel(i + 1);
//通过反射(动态加载)是实现:给order对象中的(别名)属性,赋值value
Field declaredField = Order.class.getDeclaredField(columnLabel);
declaredField.setAccessible(true);
declaredField.set(order, value);
}
return order;
}
return null;
}
@Test //测试order表的通用查询
public void testQueryForOrder() throws SQLException, IOException, NoSuchFieldException, ClassNotFoundException, IllegalAccessException {
String sql = "select order_id orderId , order_name orderName, order_date orderDate from `order` where order_id = ?";//注意这里的sql语句中order用``符号框出,避免敏感词
Order order = queryForOrder(sql, 2);
System.out.println(order);
}
效果:
小结
总结和对比:
针对某一个特定表格的通用查询。
- 对于某个表而言,不知道要查询几个字段,所以用到了
rs.getMetaDate()
获取字段数 - 用类对象的方式呈现查询的结果,通过反射动态加载
- 用
rsdm.getColumnLabel(i+1)
更靠谱,应对类的属性名和表的字段名不一致的问题(order表) - 反射
通用 — 泛型 模板
通过泛型搞定任意一个表的通用查询
上代码:
/**
* 泛型 通用查询
*/
public <T> T queryForAnyone(Class<T> clazz, String sql, Object... args) throws SQLException, IOException, ClassNotFoundException, IllegalAccessException, NoSuchFieldException, InstantiationException {
//1.获取连接
Connection connection = JDBCUtils.getConnection();
//2.设置占位符,预编译sql语句
PreparedStatement ps = connection.prepareStatement(sql);
for (int i = 0; i < args.length; i++) {
ps.setObject(i + 1, args[i]); //第i+1个占位符(从1开始计)--对应args[i](从0开始计数)
}
//3.执行
ResultSet rs = ps.executeQuery();
//4.处理结果集
//获取元数据
ResultSetMetaData rsmd = rs.getMetaData();
//获取列数
int columnCount = rsmd.getColumnCount();
if (rs.next()) {
//创建对象
T t = clazz.newInstance();
for (int i = 0; i < columnCount; i++) {
Object value = rs.getObject(i + 1);//获取列的值,(从1开始计
//获取列的别名
String columnLabel = rsmd.getColumnLabel(i + 1);
//通过反射(动态加载)是实现:给order对象中的(别名)属性,赋值value
Field declaredField = clazz.getDeclaredField(columnLabel);
declaredField.setAccessible(true);
declaredField.set(t, value);
}
return t;
}
return null;
}
@Test //测试上述代码 -- 泛型
public void testQueryForAnyone() throws SQLException, IOException, NoSuchFieldException, ClassNotFoundException, IllegalAccessException, InstantiationException {
String sql1 = "select name , email , birth from customers where id = ?";
Object result1 = queryForAnyone(Customer.class, sql1, 1);
System.out.println(result1);
String sql2 = "select order_id orderId, order_name orderName, order_date orderDate from `order` where order_id = ?";
Order result2 = queryForAnyone(Order.class, sql2, 1);
System.out.println(result2);
}
效果:
通用–泛型查询–多条数据
if改成while,用list查收
上代码:
/**
* 通用查询 --- 多条数据 --- 泛型
*/
public <T> List<T> getQueryList(Class<T> clazz, String sql, Object... args) throws SQLException, IOException, ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchFieldException {
//1.获取连接
Connection connection = JDBCUtils.getConnection();
//2.设置占位符,预编译sql语句
PreparedStatement ps = connection.prepareStatement(sql);
for (int i = 0; i < args.length; i++) {
ps.setObject(i + 1, args[i]); //第i+1个占位符(从1开始计)--对应args[i](从0开始计数)
}
//3.执行
ResultSet rs = ps.executeQuery();
//4.处理结果集
//获取元数据
ResultSetMetaData rsmd = rs.getMetaData();
//获取列数
int columnCount = rsmd.getColumnCount();
//创建list集合
ArrayList<T> list = new ArrayList<>();
while (rs.next()) {
//创建对象
T t = clazz.newInstance();
for (int i = 0; i < columnCount; i++) {
Object value = rs.getObject(i + 1);//获取列的值,(从1开始计
//获取列的别名
String columnLabel = rsmd.getColumnLabel(i + 1);
//通过反射(动态加载)是实现:给order对象中的(别名)属性,赋值value
Field declaredField = clazz.getDeclaredField(columnLabel);
declaredField.setAccessible(true);
declaredField.set(t, value);
}
list.add(t);
}
return list;
}
@Test //-猜测是上述代码, --- 泛型查询多条数据
public void testGetQueryList() throws SQLException, IOException, NoSuchFieldException, ClassNotFoundException, InstantiationException, IllegalAccessException {
String sql1 = "select name, email, birth from customers where id < ?";
List<Customer> result1 = getQueryList(Customer.class, sql1, 4);
result1.forEach(System.out::println);//浪木塔表达式--说实话有点忘记了淦!只要记住是列表一行行输出就行。。
String sql2 = "select order_id orderId, order_name orderName, order_date orderDate from `order` where order_id < ?";
List<Order> result2 = getQueryList(Order.class, sql2, 4);
result2.forEach(System.out::println);
}
效果:
课后练习:
1.向customers表格插入一条数据(键输入)
上代码:
//通用的-增删改-操作 ---适合任何一张表格
public int update(String sql, Object... args) throws SQLException, IOException, ClassNotFoundException {
//!!!需要注意的是:sql里面占位符? == 可变参数数组args[]长度相同
//1.获取连接
Connection connection = JDBCUtils.getConnection();
//2.预编译sql语句
PreparedStatement ps = connection.prepareStatement(sql);
//3.填充占位符
for (int i = 0; i < args.length; i++) {
ps.setObject(i + 1, args[i]);
}
//4.执行
// ps.execute()
int n = ps.executeUpdate(); //这里的n表示n条数据有改动,0则没有变化
//5.关闭
JDBCUtils.closeResource(connection, ps);
return n;
}
/**
* 插入一条数据---表customers中
*/
@Test //----练习1 插入数据
public void test1_AddOnetoCustomers() throws SQLException, IOException, ClassNotFoundException {
Scanner sc = new Scanner(System.in);
System.out.print("输入name:");
String name = sc.nextLine();
System.out.print("输入email:");
String email = sc.nextLine();
System.out.println("输入birth:");
String birth = sc.nextLine();
String sql1 = "insert into customers (name, email, birth) values(?,?,?)";
int i = update(sql1, name, email, birth);
if (i!=0){
System.out.println("添加成功!");
}else {
System.out.println("添加失败!");
}
}
过程问题: 关于idea中,@Test中的键盘键输入导致的一直阻塞的问题
参考 IDEA单元测试一直转圈,阻塞,控制台无法键入数据
解决方法:
效果:
2.添加四六级成绩
表格examstudent(FlowID设置为主键,not null,自增)
上代码:
/**
* 练习2 --- 添加四六级成绩
*/
@Test
public void test2_AddGrade() throws SQLException, IOException, ClassNotFoundException {
Scanner sc = new Scanner(System.in);
System.out.print("四级/六级:");
int Type = sc.nextInt();
System.out.print("学号:");
String IDCard = sc.next();
System.out.print("准考证号:");
String ExamCard = sc.next();
System.out.print("姓名:");
String StudentName = sc.next();
System.out.print("地区:");
String Location = sc.next();
System.out.print("分数:");
int Grade = sc.nextInt();
String sql = "insert into examstudent(type,idcard,examcard,studentname,location,grade) values(?,?,?,?,?,?)";
int i = update(sql, Type, IDCard, ExamCard, StudentName, Location, Grade);
if (i != 0) {
System.out.println("添加成功!");
} else {
System.out.println("添加失败!");
}
}
效果:
3.输入学号/准考证号,查询学生信息
上代码:
Student类:
package com.statement;
public class Student {
private int FlowID;
private int Type;
private String IDCard;
private String ExamCard;
private String StudentName;
private String Location;
private int Grade;
public Student() {
}
public Student(int flowID, int type, String IDCard, String examCard, String studentName, String location, int grade) {
FlowID = flowID;
Type = type;
this.IDCard = IDCard;
ExamCard = examCard;
StudentName = studentName;
Location = location;
Grade = grade;
}
public int getFlowID() {
return FlowID;
}
public void setFlowID(int flowID) {
FlowID = flowID;
}
public int getType() {
return Type;
}
public void setType(int type) {
Type = type;
}
public String getIDCard() {
return IDCard;
}
public void setIDCard(String IDCard) {
this.IDCard = IDCard;
}
public String getExamCard() {
return ExamCard;
}
public void setExamCard(String examCard) {
ExamCard = examCard;
}
public String getStudentName() {
return StudentName;
}
public void setStudentName(String studentName) {
StudentName = studentName;
}
public String getLocation() {
return Location;
}
public void setLocation(String location) {
Location = location;
}
public int getGrade() {
return Grade;
}
public void setGrade(int grade) {
Grade = grade;
}
@Override
public String toString() {
return "\n------------\n" +
"\n Type=" + Type +
"\n IDCard='" + IDCard +
"\n ExamCard='" + ExamCard +
"\n StudentName='" + StudentName +
"\n Location='" + Location +
"\n Grade=" + Grade;
}
}
查询操作:
/**
* 练习3 ----根据IDCard(学号) 或者 ExamCard(准考号) 查询
*/
@Test
public void test3_QueryForStudent() throws SQLException, IOException, NoSuchFieldException, ClassNotFoundException, InstantiationException, IllegalAccessException {
System.out.println("a:学号查询");
System.out.println("b:准考证号查询");
System.out.print("(a/b):");
Scanner sc = new Scanner(System.in);
String k = sc.next();
if ("a".equals(k)) {
System.out.print("输入学号:");
String IDCard = sc.next();
String sql = "select FlowID, Type, IDCard, ExamCard, StudentName, Location, Grade from examstudent where IDCard = ?";
List<Student> queryList = getQueryList(Student.class, sql, IDCard);
queryList.forEach(System.out::println);
} else if ("b".equals(k)) {
//不高兴写了,淦!
} else {
System.out.println("输入错误!");
}
}
//**-查询多条数据 - - - - 通用
public <T> List<T> getQueryList(Class<T> clazz, String sql, Object... args) throws SQLException, IOException, ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchFieldException {
//1.获取连接
Connection connection = JDBCUtils.getConnection();
//2.设置占位符,预编译sql语句
PreparedStatement ps = connection.prepareStatement(sql);
for (int i = 0; i < args.length; i++) {
ps.setObject(i + 1, args[i]); //第i+1个占位符(从1开始计)--对应args[i](从0开始计数)
}
//3.执行
ResultSet rs = ps.executeQuery();
//4.处理结果集
//获取元数据
ResultSetMetaData rsmd = rs.getMetaData();
//获取列数
int columnCount = rsmd.getColumnCount();
//创建list集合
ArrayList<T> list = new ArrayList<>();
while (rs.next()) {
//创建对象
T t = clazz.newInstance();
for (int i = 0; i < columnCount; i++) {
Object value = rs.getObject(i + 1);//获取列的值,(从1开始计
//获取列的别名
String columnLabel = rsmd.getColumnLabel(i + 1);
//通过反射(动态加载)是实现:给order对象中的(别名)属性,赋值value
Field declaredField = clazz.getDeclaredField(columnLabel);
declaredField.setAccessible(true);
declaredField.set(t, value);
}
list.add(t);
}
return list;
}
效果:
4.删除 ---- 根据IDCard
上代码:
/**
* 练习3:根据IDCard(学号),删除数据
*/
@Test
public void test3_Delete() throws SQLException, IOException, ClassNotFoundException {
System.out.println("输入需要删除的学号(IDCard):");
Scanner sc = new Scanner(System.in);
String IDCard = sc.next();
String sql = "delete from examstudent where IDCard = ?";
int i = update(sql, IDCard);
if (i!=0){
System.out.println("删除成功。");
}else {
System.out.println("查无此人,删除失败!");
}
}
update代码 上述有,不多赘述。
效果:
小结:
- 7.13复习中,JDBCUtils中没有写static
- JDBCUtils_re.class.getClassLoader().getResourceAsStream(“properties”) 忘记写了
2.3 Blob大型数据类型
大型数据就没有通用的方法了,最基本的插入来。
1.添加图片 — 大型数据的添加
上代码:
/**
* blob大型数据类型 ---添加图片
*/
@Test
public void BlobAddImage() throws SQLException, IOException, ClassNotFoundException {
//1.获取连接
Connection connection = JDBCUtils.getConnection();
//2.预编译 + 填占位符
String sql = "insert into customers(name,email,birth,photo) value (?,?,?,?)";
PreparedStatement ps = connection.prepareStatement(sql);
ps.setObject(1, "文二牛");
ps.setObject(2, "wenerniu@666.com");
ps.setDate(3, null);
FileInputStream fis = new FileInputStream("G:\\1_Program\\java_数据结构\\代码\\JDBC\\src\\test\\java\\com\\statement\\wen.png");
ps.setObject(4, fis);
//3.执行
ps.execute();
//4.
fis.close();
JDBCUtils.closeResource(connection, ps);
}
效果:使用blob添加图片到数据库
2.查询图片 — 从数据库中下载图片
上代码:
/**
* blob --- 下载图片到本地 --查询
* 哥么他妈这字节流是一点也记不住啊。认真复习这里字节流
*/
@Test
public void BlobDownLoad() throws SQLException, IOException, ClassNotFoundException {
Connection connection = JDBCUtils.getConnection();
String sql = "select id, name, email, birth, photo from customers where id = ?";
PreparedStatement ps = connection.prepareStatement(sql);
ps.setInt(1, 16);
ResultSet rs = ps.executeQuery();
//4.处理结果集
if (rs.next()) {
int id = rs.getInt("id");
String name = rs.getString("name");
String email = rs.getString("email");
Date birth = rs.getDate("birth");
Customer customer = new Customer(id, name, email, (java.sql.Date) birth);
System.out.println(customer);
//读取blob类型的字段
Blob photo = rs.getBlob("photo");
InputStream is = photo.getBinaryStream();//核心
OutputStream os = new FileOutputStream("G:\\1_Program\\java_数据结构\\代码\\JDBC\\src\\test\\java\\com\\statement\\zhuyin.jpg");
byte[] buffer = new byte[1024];
int len = 0;
while ((len = is.read(buffer))!= -1){
os.write(buffer,0,len);
}
//关闭
JDBCUtils.closeResource(connection,ps,rs);
is.close();
os.close();
}
}
效果:
3.批量插入
PreparedStatement好就好在会预编译sql语句
一步一步优化代码:
高效批量插入
数据库提供一个goods表格
CREATE TABLE goods( id INT PRIMARY KEY AUTO_INCREMENT, NAME VARCHAR(20) );
实现层次一:使用Statement
Connection conn = JDBCUtils.getConnection();
Statement st = conn.createStatement();
for (int i = 1; i <= 20000; i++) {
String sql = "insert into goods(name) values('name_' + " + i + ")";
st.executeUpdate(sql);
}
实现层次二:使用PreparedStatement
long start = System.currentTimeMillis();
Connection conn = JDBCUtils.getConnection();
String sql = "insert into goods(name)values(?)";
PreparedStatement ps = conn.prepareStatement(sql);
for (int i = 1; i <= 20000; i++) {
ps.setString(1, "name_" + i);
ps.executeUpdate();
}
long end = System.currentTimeMillis();
System.out.println("花费的时间为:" + (end - start));
JDBCUtils.close.......
}
ok分析一二:
- PreparedStatement存在预编译
- 二,存在缓存的。相比之下,一,每次都要执行前都要运行sql语句;然后,二,有缓存,就可以少用点资源 (大白话分析,淦)
现在起飞
实现层次三:
/**
*
* 修改1: 使用 addBatch() / executeBatch() / clearBatch() *
* 修改2:mysql服务器默认是关闭批处理的,我们需要通过一个参数,让mysql开启批处理的支持。?rewriteBatchedStatements=true 写在配置文件的url后面 *
* 修改3:使用更新的mysql 驱动:mysql-connector-java-5.1.37-bin.jar ---这是老师的驱动 我不是用的这个!
* 最后设置:不自动提交数据,全部执行完之后再提交数据
*/
@Test
public void AddBigData() throws SQLException, IOException, ClassNotFoundException {
long start = System.currentTimeMillis();
Connection connection = JDBCUtils.getConnection();
//设置不自动提交
connection.setAutoCommit(false);
String sql = "insert into goods(name) values(?)";
PreparedStatement ps = connection.prepareStatement(sql);
for (int i = 0; i <= 1000000; i++) {
ps.setObject(1, "name" + i);
//1.赞”sql“
ps.addBatch();
if (i % 1000 == 0) {
//2.执行
ps.executeBatch();
//3.清空
ps.clearBatch();
}
}
//提交数据
connection.commit();
long end = System.currentTimeMillis();
System.out.println("耗时:" + (end - start));
JDBCUtils.closeResource(connection,ps);
}
效果:
上述总结和补充
首先Statement – vs – PreparedStatement:
-
Statement就是来传送sql语句到数据库进行操作的。
但,存在两个弊端:繁琐+sql注入问题。 -
PreparedStatement是Statement子接口。 PreparedStatement 用到的占位符 ,预编译。
补:(会对预编译语句提供性能优化。因为预编译语句有可能被重复调用,所以语句在被DBServer的编译器编译后的执行代码被缓存下来,那么下次调用时只要是相同的预编译语句就不需要编译,只要将参 数直接传入编译过的语句执行代码中就会得到执行。)
22.7.13
花了一上午时间复习这里的基础,有几点问题:
对于增删改的理解进一步加深了这很好。
- JDBCUtils的static不知道要加
- JDBCUtils获取构造器老是忘记
- 另外对于流的字节数组写入的方式不太熟悉(啧)
- 对于批量插入的循环有误解(是重复操作多次,而不是占位符有多个)
- 另外对于Blob大型数据的查询下载,中间有一部核心的操作很重要,不熟悉。
自此,就像是三九北风熬了一碗鲫鱼汤,出家和尚瞄了一眼小娇娘。
说不出的欢喜和难熬。