文章目录
1 PreparedStatement实现
1.1PreparedStatement实现数据库增删改(通用模版)
1.连接数据库,加载数据库配置文件
将数据库连接需要的四个基本信息写入配置文件,通过加载配置文件的方式实现连接
配置文件最好放在src下,写入新建的file中
优点:1.实现了数据与代码的分离,实现了解耦,换数据库等仅需要改配置文件
2.要修改配置信息时,不需要对原有的文件重新打包
user=root
password=此处写你自己的数据登录密码
url=jdbc:mysql://localhost:3306/将要操作的数据库名
driverClass=com.mysql.jdbc.Driver
2.新建包和类—工具类
- 因为每次获取连接和关闭连接都是必须要做的,所以我们可以将这两部分代码封装起来,写入一个工具类中
- 这里工具类命名为JDBCUtils.java
- 这里代码通用,可以不用改,若不是mysql数据库仅需要修改上一步中的配置文件即可
package jdbcstu.util;
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.sql.Statement;
import java.util.Properties;
/**alt+shift+j 创建文档注释
* @Description 因为每次获取连接和关闭连接都是必须要做的,所以我们可以将这两部分代码封装起来
* 这个是自己创建的工具类的包。
* @version
* @date 2021年3月29日 下午8:52:27
*/
public class JDBCUtils {
/**
* 打/**+Enter
* @Description 获取数据库连接
* @version
* @date 2021年3月29日 下午9:10:00
* @return
* @throws Exception
*/
//静态方法,返回的是一个连接,方法名为getConnection
public static Connection getConnection() throws Exception {
//1.读取配置文件中四个基本信息;类.class.方法即类的加载器中系统加载器.方法(文件名)
InputStream is = ClassLoader.getSystemClassLoader().getResourceAsStream("jdbc.properties");//ctrl+1生成流
Properties pros = new Properties();
pros.load(is);//通过pros加载文件
String user=pros.getProperty("user");//通过这个来读取
String password=pros.getProperty("password");
String url=pros.getProperty("url");
String driverClass=pros.getProperty("driverClass");
//2.加载驱动
Class.forName(driverClass);
//3.获取连接
Connection conn = DriverManager.getConnection(url, user, password);
return conn;
}
/**
* 关闭连接和关闭statement操作
*/
public static void closeResource(Connection conn,Statement ps) {//PreparedStatement,这里写成Statement大一点,PreparedStatement也能放
/**
* 关闭连接和关闭statement操作
*/
try {
if(ps!=null)//避免空指针问题,因为对象没有创建报异常,finally这里进行关闭操作,从而出现空指针的调用,因此加以判断,避免空指针问题
ps.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}//关闭执行实例
try {
if(conn!=null)//避免空指针问题,因为获取连接的时候报异常,对象没拿到却仍然在finally这里进行关闭操作,因此加以判断,避免空指针问题
conn.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}//关闭连接,(关闭了资源则可以不用再抛出异常,对上面的大块进行统一try catch,再还要对两个关闭进行try catch)
}
/**
*
* @Description关闭资源操作(连接、Statement、结果集)
* @version
* @date 2021年3月31日 下午8:49:18
* @param conn
* @param ps
* @param rs
*/
public static void closeResource(Connection conn,Statement ps,ResultSet rs) {//多一个参数构成方法的重载,需要导入sql下的包(面向接口编程不出现其他第三方API)
try {
if(ps!=null)//避免空指针问题,因为对象没有创建报异常,finally这里进行关闭操作,从而出现空指针的调用,因此加以判断,避免空指针问题
ps.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}//关闭执行实例
try {
if(conn!=null)//避免空指针问题,因为获取连接的时候报异常,对象没拿到却仍然在finally这里进行关闭操作,因此加以判断,避免空指针问题
conn.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}//关闭连接,(关闭了资源则可以不用再抛出异常,对上面的大块进行统一try catch,再还要对两个关闭进行try catch)
try {
if(rs!=null)//避免空指针问题,因为获取连接的时候报异常,对象没拿到却仍然在finally这里进行关闭操作,因此加以判断,避免空指针问题
rs.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
3.增删改操作的通用方法
- 通用的增删改操作,适用与同一数据库中的所有表的增删改操作
- 增删改中主要区别是sql语句处和占位符不同,可以将这些不同的变化之处放到参数上来体现
- 占位符不确定个数采用可变形参,obj类型
- 这里代码也可直接用,不用改
package jdbcstu.preparedstatement.crud
import java.io.InputStream;
import java.sql.Connection;
import java.sql.Date;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;
//import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Properties;
import org.junit.Test;
import jdbcstu.connection.ConnectionTest;
import jdbcstu.util.JDBCUtils;
//通用的增删改操作,适用与同一数据库中的所有表的增删改操作
public class PreparedStatementUpdateTest {
public void update(String sql,Object...args) {//sql中占位符的个数与可变形参的长度相同
Connection conn = null;
PreparedStatement ps = null;
try {
//1.获取数据库的连接
conn = JDBCUtils.getConnection();
//2.预编译sql语句返回PreparedStatement的实例
ps = conn.prepareStatement(sql);
//3.填充占位符
for(int i=0;i<args.length;i++) {
ps.setObject(i+1, args[i]);//第一位下标是从1开始的,而第二位是数组是从0开始的;小心参数声明错误
}
//4.执行
ps.execute();
// } catch (SQLException e) {
// // TODO Auto-generated catch block
// e.printStackTrace();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally {
//5.资源的关闭
JDBCUtils.closeResource(conn, ps);
}
}
}
e.g 使用增删改的通用方法例
使用方式如下
这里需要写你自己具体的sql语句
package jdbcstu.preparedstatement.crud
import java.io.InputStream;
import java.sql.Connection;
import java.sql.Date;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;
//import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Properties;
import org.junit.Test;
import jdbcstu.connection.ConnectionTest;
import jdbcstu.util.JDBCUtils;
public class PreparedStatementUpdateTest {
@Test
public void testCommonUpdate() {//测试通用方法
//测试1
// String sql="delete from customers where id=?";
// update(sql,3);
//测试2
//String sql="update order set order_name=? where order_id";//这里的表名order与java关键字冲突,会报sql语句错误异常,可用` `将order扩起来
String sql="update `order` set order_name=? where order_id=?";
update(sql,"DD","2");
}
}
1.2 使用preparedstatement实现通用查询操作
jdbcstu.bean.Customer文件如下
package jdbcstu.bean;
/*
* ORM编程思想(object relational mapping对象关系映射)
* 一个数据表对应一个java类
* 表中的一条记录对应java类的一个对象
* 表中的一个字段对应java类的一个属性
*
*/
import java.sql.Date;
public class Customer {
private int id;
private String name;
private String email;
private Date birth;
//alt+shift+s,r,o,c生成相应的构造器
//快捷键后选择Generate Constructor using Fields——选择Deselect All——选择OK(空参构造器)
public Customer() {
super();
}
//快捷键后选择Generate Constructor using Fields——Ok(带参构造器)
public Customer(int id, String name, String email, Date birth) {
super();
this.id = id;
this.name = name;
this.email = email;
this.birth = birth;
}
//快捷键后选择Generate Getters and Setters——Select All——OK(get、set方法)
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;
}
//快捷键后选择Generate toString()——OK(toString方法)
@Override
public String toString() {
return "Customer [id=" + id + ", name=" + name + ", email=" + email + ", birth=" + birth + "]";
}
}
}
jdbcstu.bean.Order文件如下
package jdbcstu.bean;
import java.sql.Date;
public class Order {
private int orderId;
private String orderName;
private Date orderDate;
//生成构造器
public Order() {
super();
}
public Order(int orderId, String orderName, Date orderDate) {
super();
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 + "]";
}
}
主要查询操作表如下
针对不同表可查询多个记录通用的查询方式;
查询结果封装在对象中(每一条记录封装在一个对象中),并将最终多个(多个记录)对象封装在集合或数组中。
package jdbcstu.preparedstatement.crud;
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;
import java.util.List;
import org.junit.Test;
import jdbcstu.bean.Customer;
import jdbcstu.bean.Order;
import jdbcstu.util.JDBCUtils;
/**
*
* @Description 使用PrepareStatement实现针对于不同表的通用的查询操作
* 在这里与之前的区别是要造的类的对象不确定了,通过反射泛型来做替换即可
* @author Dell
* @version
* @date 2021年4月2日 下午8:38:45
*/
//多条记录即多个对象以集合或者数组的方式封装
public <T> List<T> getForList(Class<T> clazz,String sql, Object... args){//ctrl+shift+O选择util下的list
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
conn = JDBCUtils.getConnection();
ps = conn.prepareStatement(sql);
for (int i = 0; i < args.length; i++) {
ps.setObject(i + 1, args[i]);
}
rs = ps.executeQuery();
// 获取结果集的元数据(修饰现有数据的数据)
ResultSetMetaData rsmd = rs.getMetaData();
int columnCount = rsmd.getColumnCount();
/**
* 造一个集合将对象放到集合当中,然后返回集合
*/
//创建集合对象
ArrayList<T> list = new ArrayList<T>();//Ctrl+shift+O引入包
while(rs.next()) {//查询多条记录,这里将if换成while
// 如下述所言这里造对象
// Customer cust = new Customer();对于通用查询来说应该利用反射来得到应该造哪个类的对象
T t=clazz.newInstance();
/**
* 现在是查一条数据,若是查多条可以写成while(数据库表中的行) 获取结果集列数,根据结果集列数来判断需要读取多少个字段值
* 列数封装在rs的元数据中,在if之前使用getMetaData()方法获取结果集的元数据 通过ResultSetMetaData获取结果集的列数
*/
// 处理结果集一行数据中的每一个列:给t对象指定属性赋值
for (int i = 0; i < columnCount; i++) {// 根据列数(数据库表中的列),循环读取属性字段值
// 获取列值
Object columValue = rs.getObject(i + 1);
/**
* 将数据封装到一个对象当中,对象需要给属性们附上所查到的值;构造对象(两种方式,这里推荐方式二)
* 方式一:在构造器中将属性直接填进去(不推荐,因为对应同参数个数的构造器不一定有)
* 方式二:先用空参构造器造一个对象,根据查询属性字段通过Set方法将值设置进去(推荐)
* 对象造一次故不能写到for里,最好写到if里,for外;不写到if里因为可能存在没有结果集却造了个对象的情况
*/
/**
* 现在需要给cust这个对象指定的某一个属性名赋值为value 需要获取结果集当中的列名,将这个列名对应的同名的属性赋值为相应的value
*/
// 获取每个列的列名
String columnLabel = rsmd.getColumnLabel(i+1);// 获取i+1这列
// 将cust这个对象叫columnName这个名的属性赋值为columValue这个值,通过反射
/**
* Customer类中找叫columnName的属性,把那个属性对应cust对象的值赋值为columValue 即调用运行时类的指定属性用户值的操作
*/
//Field field = Customer.class.getDeclaredField(columnName);// 先将叫这个名的属性拿到
Field field = clazz.getDeclaredField(columnLabel);// 先将叫这个名的属性拿到
field.setAccessible(true);// 拿到的这个属性可能是私有的,将它变得能访问
field.set(t, columValue);// 将这个属性名的值设置给当前的cust,赋值为columValue
}
//return t;现在返回的是集合了
list.add(t);//将对象加入集合
}
return list;//整个while循环结束后,在这里返回集合
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
JDBCUtils.closeResource(conn, ps, rs);
// 如果没附上值,则返回null
}
return null;
// 再去掉throws用try catch以及用finally,赋值为null
}
e.g 使用通用查询方法例
package jdbcstu.preparedstatement.crud;
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;
import java.util.List;
import org.junit.Test;
import jdbcstu.bean.Customer;
import jdbcstu.bean.Order;
import jdbustu.util.JDBCUtils;
/**
*
* @Description 使用PrepareStatement实现针对于不同表的通用的查询操作
* 在这里与之前的区别是要造的类的对象不确定了,通过反射泛型来做替换即可
* @author Dell
* @version
* @date 2021年4月2日 下午8:38:45
*/
public class PrepareStatementQueryTest {
@Test
//测试方法
public void testGetForList() {
String sql="select id,name,email from customers where id<?";
List<Customer> list = getForList(Customer.class,sql,12);
list.forEach(System.out::println);//拉姆达表达式
String sql1="select order_id orderId,order_name orderName from `order`";
List<Order> orderList = getForList(Order.class, sql1);
orderList.forEach(System.out::println);
}
小结
-
首先,填写数据库配置文件**(可直接用模版)**
-
编写工具类,工具类中实现连接和关闭连接**(可直接用模版)**
- 实现连接方法
- 读取配置文件中四个基本信息;用类.class.方法即类的加载器中系统加载器.方法(文件名)
- 加载驱动
- 获取连接
- 实现关闭方法
- 关闭执行实例(需要判断是否非空)
- 关闭连接(需要判断是否非空)
- 实现连接方法
-
编写增删改操作通用方法**(可直接用模版)**
-
获取数据库的连接
-
预编译sql语句返回PreparedStatement的实例
-
填充占位符
-
执行
-
资源的关闭
-
-
编写针对不同表可查询多个记录通用的查询方法**(可用模版)**
-
编写String的sql语句,并调用增删改(查)通用方法
1.3 非模板无通用型方式(PreparedStatement)
以下内容可不看,仅是对以上模版来历的演变过程
1.数据配置文件连接方式的来历
这里描述了,从第一种数据库连接方式一步步优化迭代到最终版配置文件连接方式的思路与代码
package jdbcstu.connection;
import java.io.InputStream
import java.sql.Connection;
import java.sql.Driver;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.Properties;
import org.junit.Test;
//数据库连接类
/*--------------方式一-------------------*/
public class ConnectionTest {
@Test
//方式一
public void testConnection1() throws SQLException//自动暂时抛出异常
{
Driver driver=new com.mysql.jdbc.Driver();//获取drive的实现类对象;驱动对象,需要导入mysql连接 jar包驱动;将导入包中所拥有的实现类写到这里
String url="jdbc:mysql://localhost:3306/test";//自动添加的变量,这里的url值是文档里面固定的,除了最后的test是数据库名(可改)
Properties info=new Properties();//自动添加的变量,先创建Properties对象,是String的键值对型,用将 用户名和密码封装进去
info.setProperty("user", "root");//一般相同,固定的是root用户
info.setProperty("password", "admin");//第二个值写数据库登录密码,每个人可能不同
Connection conn=driver.connect(url, info);//返回的是连接对象
System.out.println(conn);//打印得到的连接对象,出来一串数,证明连接正常
}
/*----------------方式二-----------------*/
//方式二:对方式一的迭代。方式一具有第三方API(drive那里);没有良好的移植性
//利用反射动态获取
@Test
public void testConnection2() throws Exception {
//1.获取Driver实现类对象,使用反射
//Class<?> clazz = Class.forName("com.mysql.jdbc.Driver");//反射的源头括号里是具体实现类的路径;此处cur+1得到class的实例
Class clazz = Class.forName("com.mysql.jdbc.Driver");//去掉尖括号的泛型
Driver driver=(Driver) clazz.newInstance();//要成功调用这个方法必须保证有空参构造器且权限够;返回的是object所以需要强转
//也就是用这两行替代方法一的第一行
//剩下与上面相同,先提供要连接的数据库
//2.提供要连接的数据库
String url="jdbc:mysql://localhost:3306/test";
//3.提供要连接的用户名和密码
Properties info=new Properties();
info.setProperty("user", "root");
info.setProperty("password", "admin");
//4.获取连接
Connection conn=driver.connect(url, info);
System.out.println(conn);
}
/*---------------方式三------------------*/
//方式三:使用DriverManager替换drive,驱动的管理者sun公司提供的类
@Test
public void testConnection3() 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="admin";
//注册驱动
DriverManager.deregisterDriver(driver);
//3.获取连接
Connection conn = DriverManager.getConnection(url, user, password);
System.out.println(conn);
}
/*---------------方式四------------------*/
//方式四:优化.获取Driver实现类对象部分;可以仅加载驱动,不用显示的注册驱动
@Test
public void testConnection4() throws Exception {
//1.提供另外三个连接的基本信息
String url="jdbc:mysql://localhost:3306/test";
String user="root";
String password="admin";
//2.获取Driver实现类对象;加载Driver
Class.forName("com.mysql.jdbc.Driver");//去掉尖括号的泛型,这行代码其实带有注册驱动部分
//可以省略如下操作;因为在mysql的Driver实现类中已经声明了如下操作
// Driver driver=(Driver) clazz.newInstance();
// //注册驱动
// DriverManager.deregisterDriver(driver);
//3.获取连接
Connection conn = DriverManager.getConnection(url, user, password);
System.out.println(conn);
}
/*---------------方式五:最终版------------------*/
//方式五:最终版,将数据库连接需要的四个基本信息写入配置文件,通过加载配置文件的方式实现连接
//配置文件最好放在src下,新建file
//优点:1.实现了数据与代码的分离,实现了解耦,换数据库等仅需要改配置文件
//2.要修改配置信息时,不需要对原有的文件重新打包
@Test
public void testConnection5() throws Exception {
//1.读取配置文件中四个基本信息;类.class.方法即类的加载器中系统加载器.方法(文件名)
InputStream is = ConnectionTest.class.getClassLoader().getResourceAsStream("jdbc.properties");//ctrl+1生成流
Properties pros = new Properties();
pros.load(is);//通过pros加载文件
String user=pros.getProperty("user");//通过这个来读取
String password=pros.getProperty("password");
String url=pros.getProperty("url");
String driverClass=pros.getProperty("driverClass");
//2.加载驱动
Class.forName(driverClass);
//3.获取连接
Connection conn = DriverManager.getConnection(url, user, password);
System.out.println(conn);
}
}
2.没有如上工具类和通用方法只有数据配置文件时的描写方法
这里不是通用模版
这里的代码描述了,如果没有工具类和增删改通用方法只有配置文件时该怎么对数据库进行操作
package jdbcstu.preparedstatement.crud
import java.io.InputStream;
import java.sql.Connection;
import java.sql.Date;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;
//import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Properties;
import org.junit.Test;
import jdbcstu.connection.ConnectionTest;
import jdbcstu.util.JDBCUtils;
public class PreparedStatementUpdateTest {
//向customers表中添加一条记录全过程
@Test
public void testInsert(){
Connection conn=null;;
PreparedStatement ps=null;
try {
//1.读取配置文件中四个基本信息;类.class.方法即类的加载器中系统加载器.方法(文件名);获取类的加载器
InputStream is = ClassLoader.getSystemClassLoader().getResourceAsStream("jdbc.properties");//ctrl+1生成流
Properties pros = new Properties();
pros.load(is);//通过pros加载文件
String user=pros.getProperty("user");//通过这个来读取
String password=pros.getProperty("password");
String url=pros.getProperty("url");
String driverClass=pros.getProperty("driverClass");
//2.加载驱动
Class.forName(driverClass);
//3.获取连接
conn = DriverManager.getConnection(url, user, password);
//4.获取preparedstatement的实例(预编译sql语句,返回preparedstatement的实例)
String sql="insert into customers(name,email,birth)values(?,?,?)";//
ps = conn.prepareStatement(sql);
//5.填充占位符,特例和数据库交互的index是从1开始的。
ps.setString(1, "哪吒");
ps.setString(2, "nezha@gmail.com");
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");//格式化,小写的m是时间
java.util.Date date= sdf.parse("1000-01-01");//解析,从字符串中解析出日期;这里是util下的date
ps.setDate(3, new Date(date.getTime()));//将util下的date转化为sql下的date
//6.执行sql操作
ps.execute();
} catch (Exception e) {
e.printStackTrace();
}finally {
//7.资源的关闭(分别对两个关闭再进行try catch操作)
try {
if(ps!=null)//避免空指针问题,因为对象没有创建报异常,finally这里进行关闭操作,从而出现空指针的调用,因此加以判断,避免空指针问题
ps.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}//关闭执行实例
try {
if(conn!=null)//避免空指针问题,因为获取连接的时候报异常,对象没拿到却仍然在finally这里进行关闭操作,因此加以判断,避免空指针问题
conn.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}//关闭连接,(关闭了资源则可以不用再抛出异常,对上面的大块进行统一try catch,再还要对两个关闭进行try catch)
}
}
}
3.有数据库配置文件和工具类仅没有增删改通用方法的写法
这里不是通用模版
这里的代码描述了,如果有数据库配置文件和工具类仅没有增删改通用方法时该怎么对数据库进行操作
package jdbcstu.preparedstatement.crud
import java.io.InputStream;
import java.sql.Connection;
import java.sql.Date;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;
//import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Properties;
import org.junit.Test;
import jdbcstu.connection.ConnectionTest;
import jdbcstu.util.JDBCUtils;
public class PreparedStatementUpdateTest {
//修改customers表的一条记录
@Test
public void testUpdate(){
Connection conn = null;
PreparedStatement ps = null;
try {
//1.获取数据库的连接
conn = JDBCUtils.getConnection();
//2.预编译sql语句返回PreparedStatement的实例
String sql="update customers set name=? where id=?";
ps = conn.prepareStatement(sql);
//3.填充占位符
ps.setObject(1, "莫扎特");//下标仍然从1开始
ps.setObject(2, 18);
//4.执行
ps.execute();
// } catch (SQLException e) { //去掉这个小范围的try catch;仅保留一个
// e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}finally {
//5.资源的关闭
JDBCUtils.closeResource(conn, ps);
}
//写完后去掉抛异常,改成try catch。将第一步到第四步全部try catch(选中右键,surround with,选中try catch)
//去掉第一个小范围的try catch;仅保留一个
//再将第五步写入finally中
}
}
4. 不通用的查询
①.针对于Customer表的查询
package jdbcstu.preparedstatement.crud;
import java.lang.reflect.Field;
import java.sql.Connection;
import java.sql.Date;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import org.junit.Test;
import jdbcstu.bean.Customer;
import jdbcstu.util.JDBCUtils;
/**
*
* @Description 针对于Customers表的查询操作
* @author Dell
* @version
* @date 2021年3月31日 下午7:45:39
*/
public class CustomerForQuery {
//测试针对customers表的通用查询方法
@Test
public void testQueryForCustomers() {
String sql="select id,name,birth,email from customers where id=?";
Customer customer = queryForCustomers(sql,13);//查询id是13的
System.out.println(customer);
sql="select name,birth,email from customers where name=?";
Customer customer1 = queryForCustomers(sql,"周杰伦");
System.out.println(customer1);
}
/**
*
* @Description 针对customers表的通用查询操作(不同查询字段都可以用)
* @author Dell
* @version
* @throws Exception
* @date 2021年3月31日 下午9:06:57
*/
public Customer queryForCustomers(String sql,Object...args) {//返回Customer对象
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
conn = JDBCUtils.getConnection();
ps = conn.prepareStatement(sql);
for(int i=0;i<args.length;i++) {
ps.setObject(i+1, args[i]);
}
rs = ps.executeQuery();
//获取结果集的元数据(修饰现有数据的数据)
ResultSetMetaData rsmd = rs.getMetaData();
int columnCount = rsmd.getColumnCount();
if(rs.next()) {
//如下述所言这里造对象
Customer cust=new Customer();
/**
* 现在是查一条数据,若是查多条可以写成while(数据库表中的行)
* 获取结果集列数,根据结果集列数来判断需要读取多少个字段值
* 列数封装在rs的元数据中,在if之前使用getMetaData()方法获取结果集的元数据
* 通过ResultSetMetaData获取结果集的列数
*/
//处理结果集一行数据中的每一个列
for(int i=0;i<columnCount;i++) {//根据列数(数据库表中的列),循环读取属性字段值
//获取列值
Object columValue = rs.getObject(i+1);
/**
* 将数据封装到一个对象当中,对象需要给属性们附上所查到的值;构造对象(两种方式,这里推荐方式二)
* 方式一:在构造器中将属性直接填进去(不推荐,因为对应同参数个数的构造器不一定有)
* 方式二:先用空参构造器造一个对象,根据查询属性字段通过Set方法将值设置进去(推荐)
* 对象造一次故不能写到for里,最好写到if里,for外;不写到if里因为可能存在没有结果集却造了个对象的情况
*/
/**
* 现在需要给cust这个对象指定的某一个属性名赋值为value
* 需要获取结果集当中的列名,将这个列名对应的同名的属性赋值为相应的value
*/
//获取每个列的列名
String columnName = rsmd.getColumnName(i+1);//获取i+1这列
//将cust这个对象叫columnName这个名的属性赋值为columValue这个值,通过反射
/**
* Customer类中找叫columnName的属性,把那个属性对应cust对象的值赋值为columValue
* 即调用运行时类的指定属性用户值的操作
*/
Field field = Customer.class.getDeclaredField(columnName);//先将叫这个名的属性拿到
field.setAccessible(true);//拿到的这个属性可能是私有的,将它变得能访问
field.set(cust, columValue);//将这个属性名的值设置给当前的cust,赋值为columValue
}
return cust;
}
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally {
JDBCUtils.closeResource(conn, ps, rs);
//如果没附上值,则返回null
}
return null;
//再去掉throws用try catch以及用finally,赋值为null
}
//一般查询步骤(以查询Customers表为例,以id查询信息)
//测试
@Test
public void testQuery1(){
Connection conn = null;
PreparedStatement ps = null;
ResultSet resultSet = null;
try {
conn = JDBCUtils.getConnection();
String sql = "select id,name,email,birth from customers where id=?";
ps = conn.prepareStatement(sql);
ps.setObject(1, 1);//填充第一个占位符,查id是1的
//执行并返回结果集;调用另一个执行方法
resultSet = ps.executeQuery();
//处理结果集;resultSet.next()1.判断结果集下一条是否有数据,如果有数据返回true并指针下移;没数据则返回false则指针不下移直接结束
if(resultSet.next()) {
//获取当前这条数据的各个字段值
int id = resultSet.getInt(1);//1表示查询到结果集中的第一个字段
String name = resultSet.getString(2);
String email = resultSet.getString(3);
Date birth = resultSet.getDate(4);
//方式1:
System.out.println("id="+id+",name="+name+",email="+email+",birth="+birth);
//方式2:将数据封装到数组中,数据要统一只能是object类型
Object[] date=new Object[] {id,name,email,birth};
//方式3:将信息封装到一个类的对象中,新建一个包为bean包
Customer customer = new Customer(id, name, email, birth);
//Alt+/可以补全代码
System.out.println(customer);//打印相当于调用它的toSring方法
}
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally {
//如果没查到就不处理了
//关闭资源resultSet也需要关闭
JDBCUtils.closeResource(conn, ps, resultSet);
//去掉throws,用try catch,去掉小范围catch,finally关闭资源部分
//conn、ps、resultSet变量赋空值(并不是一上来就赋空值)
}
}
}
②.针对于Order表的查询
package com.atguigu3.preparedstatement.crud;
import java.lang.reflect.Field;
import java.sql.Connection;
import java.sql.Date;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import org.junit.Test;
import jdbcstu.bean.Order;
import jdbdstu.util.JDBCUtils;
/**
*
* @Description 针对于Order表的通用查询操作
* @author Dell
* @version
* @date 2021年4月1日 下午6:57:17
*/
//做查询需要返回相应的对象,所以再到bean下创建一个Order类
public class OrderForQuery {
/*
* 针对于表的字段名与类的属性名不相同的情况
* 1.必须声明sql时,使用类的属性名来命名字段的别名
* 2.使用ResultSetMetaDate时,需要使用getColumnLabel()来替换getColumnName()
* 获取列的别名。
* 说明:如果sql中没有给字段起别名,getColumnLabel()获取的就是列名
*/
@Test
public void testOrderForQuery(){
/**String sql="select order_id,order_name,order_date from `order` where order_id=?";
* 不能这么写,这时表中属性名即结果集中属性名与类对象属性名并不一致;反射时会出现异常
* 无法将对象指定名的属性columnName,赋值为指定值columeValue
* 但可以通过写sql语句时给查询出的属性写同类对象属性名相同的别名,这时结果集中属性名就与其类对象属性名一致了
* 同时代码中获取列名时就不要用getColumnname()方法,这个获取的是表的列名,而不是结果集的列名
*
*/
//类的别名一定要拿类的属性名去替代
String sql="select order_id orderId,order_name orderName,order_date orderDate from `order` where order_id=?";
Order order = orderForQuery(sql,1);
System.out.println(order);
}
/**
*
* @Description 针对Order表的通用查询
* @author Dell
* @version
* @throws Exception
* @date 2021年4月1日 下午7:18:32
*/
public Order orderForQuery(String sql,Object...args) {
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
conn = JDBCUtils.getConnection();
ps = conn.prepareStatement(sql);
for(int i=0;i<args.length;i++) {
ps.setObject(i+1, args[i]);
}
//执行获取结果集
rs = ps.executeQuery();
//获取结果集的元数据
ResultSetMetaData rsmd = rs.getMetaData();
//获取列数
int columnCount = rsmd.getColumnCount();
if(rs.next()) {
Order order=new Order();
for(int i=0;i<columnCount;i++) {
//获取每个列的列值:通过结果集ResultSet
Object columeValue = rs.getObject(i+1);
/**
* 获取每个列的列名通过结果集元数据ResultSetMetaData
* String columnName = rsmd.getColumnName(i+1);不推荐使用
*/
//应该是获取列的别名:getColumnLabel()
String columnLabel = rsmd.getColumnLabel(i+1);
//通过反射,将对象指定名的属性columnName,赋值为指定值columeValue
Field field = Order.class.getDeclaredField(columnLabel);
field.setAccessible(true);
field.set(order, columeValue);
}
return order;
}
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally {
JDBCUtils.closeResource(conn, ps, rs);
}
return null;
}
//测试
@Test
public void testQuery1(){
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
conn = JDBCUtils.getConnection();
String sql="select order_id,order_name,order_date from `order` where order_id=?";
ps = conn.prepareStatement(sql);
ps.setObject(1, 1);
rs = ps.executeQuery();
if(rs.next()) {
int id=(int) rs.getObject(1);
String name = (String) rs.getObject(2);
Date date = (Date) rs.getObject(3);
Order order=new Order(id, name, date);
System.out.println(order);
}
} catch (Exception e) {
e.printStackTrace();
}finally {
JDBCUtils.closeResource(conn, ps, rs);
}
}
}
③.针对于不同表但仅返回一条记录的查询
package jdbcstu.preparedstatement.crud;
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;
import java.util.List;
import org.junit.Test;
import jdbcstu.bean.Customer;
import jdbcstu.bean.Order;
import jdbcstu.util.JDBCUtils;
/**
*
* @Description 使用PrepareStatement实现针对于不同表的通用的查询操作
* 在这里与之前的区别是要造的类的对象不确定了,通过反射泛型来做替换即可
* @author Dell
* @version
* @date 2021年4月2日 下午8:38:45
*/
public class PrepareStatementQueryTest {
/**
*
* @Description 针对于不同表通用的查询操作,获取一个对象,返回表中的一条记录
* @author Dell
* @version
* @date 2021年4月2日 下午9:06:59
* @param <T>
* @param clazz
* @param sql
* @param args
* @return
*/
public <T>T getInstance(Class<T> clazz,String sql, Object... args) {// 获取一个对象实例,泛型方法T是对应的运行时类。返回值类型是T,T是一个参数
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
conn = JDBCUtils.getConnection();
ps = conn.prepareStatement(sql);
for (int i = 0; i < args.length; i++) {
ps.setObject(i + 1, args[i]);
}
rs = ps.executeQuery();
// 获取结果集的元数据(修饰现有数据的数据)
ResultSetMetaData rsmd = rs.getMetaData();
int columnCount = rsmd.getColumnCount();
if (rs.next()) {
// 如下述所言这里造对象
// Customer cust = new Customer();对于通用查询来说应该利用反射来得到应该造哪个类的对象
T t=clazz.newInstance();
/**
* 现在是查一条数据,若是查多条可以写成while(数据库表中的行) 获取结果集列数,根据结果集列数来判断需要读取多少个字段值
* 列数封装在rs的元数据中,在if之前使用getMetaData()方法获取结果集的元数据 通过ResultSetMetaData获取结果集的列数
*/
// 处理结果集一行数据中的每一个列
for (int i = 0; i < columnCount; i++) {// 根据列数(数据库表中的列),循环读取属性字段值
// 获取列值
Object columValue = rs.getObject(i + 1);
/**
* 将数据封装到一个对象当中,对象需要给属性们附上所查到的值;构造对象(两种方式,这里推荐方式二)
* 方式一:在构造器中将属性直接填进去(不推荐,因为对应同参数个数的构造器不一定有)
* 方式二:先用空参构造器造一个对象,根据查询属性字段通过Set方法将值设置进去(推荐)
* 对象造一次故不能写到for里,最好写到if里,for外;不写到if里因为可能存在没有结果集却造了个对象的情况
*/
/**
* 现在需要给cust这个对象指定的某一个属性名赋值为value 需要获取结果集当中的列名,将这个列名对应的同名的属性赋值为相应的value
*/
// 获取每个列的列名
String columnLabel = rsmd.getColumnLabel(i+1);// 获取i+1这列
// 将cust这个对象叫columnName这个名的属性赋值为columValue这个值,通过反射
/**
* Customer类中找叫columnName的属性,把那个属性对应cust对象的值赋值为columValue 即调用运行时类的指定属性用户值的操作
*/
//Field field = Customer.class.getDeclaredField(columnName);// 先将叫这个名的属性拿到
Field field = clazz.getDeclaredField(columnLabel);// 先将叫这个名的属性拿到
field.setAccessible(true);// 拿到的这个属性可能是私有的,将它变得能访问
field.set(t, columValue);// 将这个属性名的值设置给当前的cust,赋值为columValue
}
return t;
}
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
JDBCUtils.closeResource(conn, ps, rs);
// 如果没附上值,则返回null
}
return null;
// 再去掉throws用try catch以及用finally,赋值为null
}
}
2 当使用Statement实现(不推荐)
这里使用Statement进行数据库操作,模拟实现用户登录功能
以下代码是ststementTest类
package jdbcstu.statement.crud;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;
import java.util.Scanner;
import org.junit.Test;
public class StatementTest {
// 使用Statement的弊端:需要拼写sql语句,并且存在SQL注入的问题
@Test
public void testLogin() {
Scanner scan = new Scanner(System.in);
System.out.print("用户名:");
String userName = scan.nextLine();
System.out.print("密 码:");
String password = scan.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 = '" + userName + "' AND PASSWORD = '" + password
+ "'";
User user = get(sql, User.class);
if (user != null) {
System.out.println("登陆成功!");
} else {
System.out.println("用户名或密码错误!");
}
}
// 使用Statement实现对数据表的查询操作
public <T> T get(String sql, Class<T> clazz) {
T t = null;
Connection conn = null;
Statement st = null;
ResultSet rs = null;
try {
// 1.加载配置文件
InputStream is = StatementTest.class.getClassLoader().getResourceAsStream("jdbc.properties");
Properties pros = new Properties();
pros.load(is);
// 2.读取配置信息
String user = pros.getProperty("user");
String password = pros.getProperty("password");
String url = pros.getProperty("url");
String driverClass = pros.getProperty("driverClass");
// 3.加载驱动
Class.forName(driverClass);
// 4.获取连接
conn = DriverManager.getConnection(url, user, password);
st = conn.createStatement();
rs = st.executeQuery(sql);
// 获取结果集的元数据
ResultSetMetaData rsmd = rs.getMetaData();
// 获取结果集的列数
int columnCount = rsmd.getColumnCount();
if (rs.next()) {
t = clazz.newInstance();
for (int i = 0; i < columnCount; i++) {
// //1. 获取列的名称
// String columnName = rsmd.getColumnName(i+1);
// 1. 获取列的别名
String columnName = rsmd.getColumnLabel(i + 1);
// 2. 根据列名获取对应数据表中的数据
Object columnVal = rs.getObject(columnName);
// 3. 将数据表中得到的数据,封装进对象
Field field = clazz.getDeclaredField(columnName);
field.setAccessible(true);
field.set(t, columnVal);
}
return t;
}
} catch (Exception e) {
e.printStackTrace();
} finally {
// 关闭资源
if (rs != null) {
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (st != null) {
try {
st.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
return null;
}
}
以下代码是User类
package jdbcstu.statement.crud;
public class User {
private String user;
private String password;
public User() {
}
public User(String user, String password) {
super();
this.user = user;
this.password = password;
}
@Override
public String toString() {
return "User [user=" + user + ", password=" + password + "]";
}
public String getUser() {
return user;
}
public void setUser(String user) {
this.user = user;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
3 使用PreparedStatement代替Statement的优点测试
package jdbcstu.statement.crud;
import java.lang.reflect.Field;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.util.Scanner;
import org.junit.Test;
import jdbcstu.util.JDBCUtils;
/**
*
* @Description 演示使用PreparedStatement替换Statement,解决sql注入问题
* @author Dell
* @version
* @date 2021年4月2日 下午9:43:13
* 除了解决Statement的拼串、sql问题之外,PreparedStatement还有哪些好处呢?
* 1.PreparedStatement操作Blob的数据(图文件),因为它有占位符是可以传流的,而Statement做不到
* 2.PreparedStatement可以实现更高效的批量操作
*/
public class PreparedStatementTest {
@Test
public void testLogin() {
Scanner scan = new Scanner(System.in);
System.out.print("用户名:");
String user= scan.nextLine();
System.out.print("密 码:");
String password = scan.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 =? and password=?";
User returnUser = getInstance(User.class,sql,user,password);
if (returnUser != null) {
System.out.println("登陆成功!");
} else {
System.out.println("用户名或密码错误!");
}
}
public <T>T getInstance(Class<T> clazz,String sql, Object... args) {// 获取一个对象实例,泛型方法T是对应的运行时类。返回值类型是T,T是一个参数
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
conn = JDBCUtils.getConnection();
ps = conn.prepareStatement(sql);
for (int i = 0; i < args.length; i++) {
ps.setObject(i + 1, args[i]);
}
rs = ps.executeQuery();
// 获取结果集的元数据(修饰现有数据的数据)
ResultSetMetaData rsmd = rs.getMetaData();
int columnCount = rsmd.getColumnCount();
if (rs.next()) {
// 如下述所言这里造对象
// Customer cust = new Customer();对于通用查询来说应该利用反射来得到应该造哪个类的对象
T t=clazz.newInstance();
/**
* 现在是查一条数据,若是查多条可以写成while(数据库表中的行) 获取结果集列数,根据结果集列数来判断需要读取多少个字段值
* 列数封装在rs的元数据中,在if之前使用getMetaData()方法获取结果集的元数据 通过ResultSetMetaData获取结果集的列数
*/
// 处理结果集一行数据中的每一个列
for (int i = 0; i < columnCount; i++) {// 根据列数(数据库表中的列),循环读取属性字段值
// 获取列值
Object columValue = rs.getObject(i + 1);
/**
* 将数据封装到一个对象当中,对象需要给属性们附上所查到的值;构造对象(两种方式,这里推荐方式二)
* 方式一:在构造器中将属性直接填进去(不推荐,因为对应同参数个数的构造器不一定有)
* 方式二:先用空参构造器造一个对象,根据查询属性字段通过Set方法将值设置进去(推荐)
* 对象造一次故不能写到for里,最好写到if里,for外;不写到if里因为可能存在没有结果集却造了个对象的情况
*/
/**
* 现在需要给cust这个对象指定的某一个属性名赋值为value 需要获取结果集当中的列名,将这个列名对应的同名的属性赋值为相应的value
*/
// 获取每个列的列名
String columnLabel = rsmd.getColumnLabel(i+1);// 获取i+1这列
// 将cust这个对象叫columnName这个名的属性赋值为columValue这个值,通过反射
/**
* Customer类中找叫columnName的属性,把那个属性对应cust对象的值赋值为columValue 即调用运行时类的指定属性用户值的操作
*/
//Field field = Customer.class.getDeclaredField(columnName);// 先将叫这个名的属性拿到
Field field = clazz.getDeclaredField(columnLabel);// 先将叫这个名的属性拿到
field.setAccessible(true);// 拿到的这个属性可能是私有的,将它变得能访问
field.set(t, columValue);// 将这个属性名的值设置给当前的cust,赋值为columValue
}
return t;
}
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
JDBCUtils.closeResource(conn, ps, rs);
// 如果没附上值,则返回null
}
return null;
// 再去掉throws用try catch以及用finally,赋值为null
}
}
练习
练习一
package jdbcstu.exercise;
import java.lang.reflect.Field;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.util.Scanner;
import org.junit.Test;
import jdbcstu.util.JDBCUtils;
public class Exer1Test {
@Test
public void testInsert() {
Scanner scanner = new Scanner(System.in);
System.out.print("请输入用户名");
String name = scanner.next();
System.out.print("请输入邮箱");
String email = scanner.next();
System.out.print("请输入生日");
String birthday = scanner.next();
String sql="insert into customers(name,email,birth)values(?,?,?)";
int insertCount = update(sql,name,email,birthday);//生日必须以xxxx-xx-xx格式来写
if(insertCount>=0) {
System.out.println("添加成功");
}else {
System.out.println("添加失败");
}
}
//通用的增删改操作
public int update(String sql,Object...args) {//sql中占位符的个数与可变形参的长度相同
Connection conn = null;
PreparedStatement ps = null;
try {
//1.获取数据库的连接
conn = JDBCUtils.getConnection();
//2.预编译sql语句返回PreparedStatement的实例
ps = conn.prepareStatement(sql);
//3.填充占位符
for(int i=0;i<args.length;i++) {
ps.setObject(i+1, args[i]);//第一位下标是从1开始的,而第二位是数组是从0开始的;小心参数声明错误
}
//4.执行
/*
* ps.execute();
* 这个方法返回boolean类型,当执行查询操作有返回结果返回true,当为增删改没有返回结果操作则返回false
*/
//方式一:
// return ps.execute();
//方式二:
return ps.executeUpdate();//前面已经传过sql这里不用传,这个返回值为int型(当前执行操作所影响的行数),
//为了有是否执行成功有返回结果
// } catch (SQLException e) {
// // TODO Auto-generated catch block
// e.printStackTrace();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally {
//5.资源的关闭
JDBCUtils.closeResource(conn, ps);
}
return 0;//不成功
}
//修改customers表的一条记录
@Test
public void testUpdate(){
Connection conn = null;
PreparedStatement ps = null;
try {
//1.获取数据库的连接
conn = JDBCUtils.getConnection();
//2.预编译sql语句返回PreparedStatement的实例
String sql="update customers set name=? where id=?";
ps = conn.prepareStatement(sql);
//3.填充占位符
ps.setObject(1, "莫扎特");//下标仍然从1开始
ps.setObject(2, 18);
//4.执行
ps.execute();
// } catch (SQLException e) { //去掉这个小范围的try catch;仅保留一个
// e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}finally {
//5.资源的关闭
JDBCUtils.closeResource(conn, ps);
}
//写完后去掉抛异常,改成try catch。将第一步到第四步全部try catch(选中右键,surround with,选中try catch)
//去掉第一个小范围的try catch;仅保留一个
//再将第五步写入finally中
}
//问题2:根据身份证号或者准考证号查询学生成绩信息
@Test
public void queryWithIDCardOrExamCard() {
Scanner scanner = new Scanner(System.in);
System.out.println("请选择您要输入的类型");
System.out.println("a:准考证号");
System.out.println("b:身份证号");
String selection = scanner.next();
//if(selection.equalsIgnoreCase("a")) {//可忽略大小写的判断,selection可能是个null会空指针异常
if("a".equalsIgnoreCase(selection)) {//最好反过来写,这样可以避免空指针异常,顶多报false
System.out.println("请输入准考证号");
String examCard = scanner.next();
String sql="select FlowID flowID,Type type,IDCard,ExamCard examCard,StudentName name,Location location,Grade grade from examstudent where examCard=?";
Student student = getInstance(Student.class,sql,examCard);
if(student!=null) {
System.out.println(student);
}else {
System.out.println("输入准考证号有误");
}
}else if("b".equalsIgnoreCase(selection)) {
System.out.println("请输入身份证号");
String IDCard = scanner.next();
String sql="select FlowID flowID,Type type,IDCard,ExamCard examCard,StudentName name,Location location,Grade grade from examstudent where IDCard=?";
Student student = getInstance(Student.class,sql,IDCard);
if(student!=null) {
System.out.println(student);
}else {
System.out.println("输入身份证号有误");
}
}else {
System.out.println("您的输入有误,请重新进入程序");
}
}
//仅返回一个对象的通用查询
public <T>T getInstance(Class<T> clazz,String sql, Object... args) {// 获取一个对象实例,泛型方法T是对应的运行时类。返回值类型是T,T是一个参数
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
conn = JDBCUtils.getConnection();
ps = conn.prepareStatement(sql);
for (int i = 0; i < args.length; i++) {
ps.setObject(i + 1, args[i]);
}
rs = ps.executeQuery();
// 获取结果集的元数据(修饰现有数据的数据)
ResultSetMetaData rsmd = rs.getMetaData();
int columnCount = rsmd.getColumnCount();
if (rs.next()) {
// 如下述所言这里造对象
// Customer cust = new Customer();对于通用查询来说应该利用反射来得到应该造哪个类的对象
T t=clazz.newInstance();
/**
* 现在是查一条数据,若是查多条可以写成while(数据库表中的行) 获取结果集列数,根据结果集列数来判断需要读取多少个字段值
* 列数封装在rs的元数据中,在if之前使用getMetaData()方法获取结果集的元数据 通过ResultSetMetaData获取结果集的列数
*/
// 处理结果集一行数据中的每一个列
for (int i = 0; i < columnCount; i++) {// 根据列数(数据库表中的列),循环读取属性字段值
// 获取列值
Object columValue = rs.getObject(i + 1);
/**
* 将数据封装到一个对象当中,对象需要给属性们附上所查到的值;构造对象(两种方式,这里推荐方式二)
* 方式一:在构造器中将属性直接填进去(不推荐,因为对应同参数个数的构造器不一定有)
* 方式二:先用空参构造器造一个对象,根据查询属性字段通过Set方法将值设置进去(推荐)
* 对象造一次故不能写到for里,最好写到if里,for外;不写到if里因为可能存在没有结果集却造了个对象的情况
*/
/**
* 现在需要给cust这个对象指定的某一个属性名赋值为value 需要获取结果集当中的列名,将这个列名对应的同名的属性赋值为相应的value
*/
// 获取每个列的列名
String columnLabel = rsmd.getColumnLabel(i+1);// 获取i+1这列
// 将cust这个对象叫columnName这个名的属性赋值为columValue这个值,通过反射
/**
* Customer类中找叫columnName的属性,把那个属性对应cust对象的值赋值为columValue 即调用运行时类的指定属性用户值的操作
*/
//Field field = Customer.class.getDeclaredField(columnName);// 先将叫这个名的属性拿到
Field field = clazz.getDeclaredField(columnLabel);// 先将叫这个名的属性拿到
field.setAccessible(true);// 拿到的这个属性可能是私有的,将它变得能访问
field.set(t, columValue);// 将这个属性名的值设置给当前的cust,赋值为columValue
}
return t;
}
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
JDBCUtils.closeResource(conn, ps, rs);
// 如果没附上值,则返回null
}
return null;
// 再去掉throws用try catch以及用finally,赋值为null
}
//问题3:删除指定学生信息
@Test
public void testDeleteByExamCard() {
System.out.println("请输入学生的考号");
Scanner scanner = new Scanner(System.in);
String examCard=scanner.next();
//查询指定准考证号的学生
String sql="select FlowID flowID,Type type,IDCard,ExamCard examCard,StudentName name,Location location,Grade grade from examstudent where examCard=?";
Student student = getInstance(Student.class,sql,examCard);
if(student==null) {
System.out.println("查无此人,请重新输入");
}else {
String sql1="delete from examstudent where examCard=?";
int deleteCount = update(sql1,examCard);
if(deleteCount>0) {
System.out.println("删除成功!");
}
}
}
//优化以后的操作
@Test
public void testDeleteByExamCard1() {
System.out.println("请输入学生的考号");
Scanner scanner = new Scanner(System.in);
String examCard=scanner.next();
String sql="delete from examstudent where examCard=?";
int deleteCount = update(sql,examCard);
if(deleteCount>0) {
System.out.println("删除成功!");
}else {
System.out.println("查无此人,请重新输入");
}
}
}
练习二
package jdbcstu.exercise;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.util.Scanner;
import org.junit.Test;
import jdbcstu.util.JDBCUtils;
//课后练习2
public class Exer2Test {
//问题1:向examstudet表中添加一条记录
/*
* Type,IDCard,ExamCard,StudentName,Location,Grade
*/
@Test
public void testInsert() {
Scanner scanner = new Scanner(System.in);
System.out.print("四级/六级:");
int type = scanner.nextInt();
System.out.print("身份证号:");
String IDCard = scanner.next();
System.out.print("准考证号:");
String examCard = scanner.next();
System.out.print("学生姓名:");
String studentName = scanner.next();
System.out.print("所在位置:");
String location = scanner.next();
System.out.print("成绩:");
int grade = scanner.nextInt();
String sql="insert into examstudent(type,IDCard,examCard,studentName,location,grade)values(?,?,?,?,?,?)";
int insertCount = update(sql,type,IDCard,examCard,studentName,location,grade);
if(insertCount>0) {
System.out.println("添加成功");
}else {
System.out.println("添加失败");
}
}
//通用的增删改操作
public int update(String sql,Object...args) {//sql中占位符的个数与可变形参的长度相同
Connection conn = null;
PreparedStatement ps = null;
try {
//1.获取数据库的连接
conn = JDBCUtils.getConnection();
//2.预编译sql语句返回PreparedStatement的实例
ps = conn.prepareStatement(sql);
//3.填充占位符
for(int i=0;i<args.length;i++) {
ps.setObject(i+1, args[i]);//第一位下标是从1开始的,而第二位是数组是从0开始的;小心参数声明错误
}
//4.执行
/*
* ps.execute();
* 这个方法返回boolean类型,当执行查询操作有返回结果返回true,当为增删改没有返回结果操作则返回false
*/
//方式一:
// return ps.execute();
//方式二:
return ps.executeUpdate();//前面已经传过sql这里不用传,这个返回值为int型(当前执行操作所影响的行数),
//为了有是否执行成功有返回结果
// } catch (SQLException e) {
// // TODO Auto-generated catch block
// e.printStackTrace();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally {
//5.资源的关闭
JDBCUtils.closeResource(conn, ps);
}
return 0;//不成功
}
//修改customers表的一条记录
@Test
public void testUpdate(){
Connection conn = null;
PreparedStatement ps = null;
try {
//1.获取数据库的连接
conn = JDBCUtils.getConnection();
//2.预编译sql语句返回PreparedStatement的实例
String sql="update customers set name=? where id=?";
ps = conn.prepareStatement(sql);
//3.填充占位符
ps.setObject(1, "莫扎特");//下标仍然从1开始
ps.setObject(2, 18);
//4.执行
ps.execute();
// } catch (SQLException e) { //去掉这个小范围的try catch;仅保留一个
// e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}finally {
//5.资源的关闭
JDBCUtils.closeResource(conn, ps);
}
//写完后去掉抛异常,改成try catch。将第一步到第四步全部try catch(选中右键,surround with,选中try catch)
//去掉第一个小范围的try catch;仅保留一个
//再将第五步写入finally中
}
}
//练习中所牵扯到的student类
package jdbcstu.exercise;
public class Student {
//给类提供相关属性
private int flowID;//流水号
private int type;//考试类型
private String IDCard;
private String examCard;
private String name;
private String location;
private int grade;
//alt+shift+s生成构造器
public Student() {
super();
}
public Student(int flowID, int type, String iDCard, String examCard, String name, String location, int grade) {
super();
this.flowID = flowID;
this.type = type;
IDCard = iDCard;
this.examCard = examCard;
this.name = name;
this.location = location;
this.grade = grade;
}
public int getFlowID() {
return flowID;
}
// public void setFlowID(int flowID) {
// this.flowID = flowID;
// } 出于封装性考虑,不建议修改flowID,因此不勾选flowID的set方法较好
public int getType() {
return type;
}
public void setType(int type) {
this.type = type;
}
public String getIDCard() {
return IDCard;
}
public void setIDCard(String iDCard) {
IDCard = iDCard;
}
public String getExamCard() {
return examCard;
}
public void setExamCard(String examCard) {
this.examCard = examCard;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getLocation() {
return location;
}
public void setLocation(String location) {
this.location = location;
}
public int getGrade() {
return grade;
}
public void setGrade(int grade) {
this.grade = grade;
}
@Override
public String toString() {
System.out.println("==========查询结果==============");
return info();
}
private String info() {
return "流水号:"+flowID+"\n四级/六级:"+type+"\n身份证号:"+IDCard+"\n准考证号:"+examCard+
"\n学生姓名:"+name+"\n区域:"+location+"\n成绩:"+grade;
}
}
4. prepareStatement操作Blob数据类型
package jdbcstu.blob;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.sql.Blob;
import java.sql.Connection;
import java.sql.Date;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import org.junit.Test;
import jdbcstu.bean.Customer;
import jdbcstu.util.JDBCUtils;
/**
*
* @Description 测试使用PreparedStatement操作Blob类型的数据
* @author Dell
* @version
* @date 2021年4月3日 下午8:49:03
*/
public class BlobTest {
//向数据表Customer表中插入Blob类型的字段
@Test
public void testInsert() throws Exception {
Connection conn = JDBCUtils.getConnection();
String sql="insert into customers(name,email,birth,photo)values(?,?,?,?)";
PreparedStatement ps = conn.prepareStatement(sql);
ps.setObject(1, "李雷");
ps.setObject(2, "lei@qq.com");
ps.setObject(3, "1998-09-08");
FileInputStream is=new FileInputStream(new File("sea.png"));//该文件大于2M需要在mysql的ini属性文件中添加字段设置一下
//找my.ini文件加上如下的配置参数: **max_allowed_packet=16M**。同时注意:修改了my.ini文件之后,需要重新启动mysql服务。
ps.setBlob(4, is);
ps.execute();
JDBCUtils.closeResource(conn, ps);
}
//查询数据表Customers中的Blob字段
@Test
public void testQuery() {
Connection conn=null;
PreparedStatement ps=null;
InputStream is=null;
FileOutputStream fos=null;
ResultSet rs=null;
try {
conn = JDBCUtils.getConnection();
String sql="select id,name,email,birth,photo from customers where id=?";
ps = conn.prepareStatement(sql);
ps.setInt(1, 22);
rs = ps.executeQuery();
if(rs.next()) {
//方式一
// int id = rs.getInt(1);
// String name = rs.getString(2);
// String email = rs.getString(3);
// Date birth = rs.getDate(4);
//方式二:这种更好
int id = rs.getInt("id");//按列别名找
String name = rs.getString("name");
String email = rs.getString("email");
Date birth = rs.getDate("birth");
Customer cust = new Customer(id, name, email, birth);//封装到对象中
System.out.println(cust);
//将blob类型的字段下载下来,以文件方式保存在本地
Blob photo = rs.getBlob("photo");
is = photo.getBinaryStream();
fos=new FileOutputStream("2.jpg");
byte[] buffer=new byte[1024];
int len;
while((len=is.read(buffer))!=-1){
fos.write(buffer,0,len);
}
}
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally {
try {
if(is!=null)
is.close();
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
try {
if(fos!=null)
fos.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
JDBCUtils.closeResource(conn, ps, rs);
}
}
5.批量插入数据
package jdbcstu.blob;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import org.junit.Test;
import jdbcstu.util.JDBCUtils;
/*
* 使用PreparedStatement实现批量数据的操作
* update、delete本身就具有批量操作的效果
* 此时的批量操作主要指的是批量插入。使用PreparedStatement如何实现更高效的批量插入?
*
* 题目:向goods表中插入20000条数据
* CREATE TABLE goods(
* id INT PRIMARY KEY AUTO_INCREMENT,
* NAME VARCHAR(25)
* );
*/
/*
* 方式一:使用statement
* Connection conn=JDBCUtils.getConnection();
* Statement st=conn.createStatement();
* for(i=1;i<=20000;i++){
* String sql="insert into goods(name)values('name_"+i+"');"
* st.excute(sql);
* }
*
*/
public class InsertTest {
//批量插入的方式二:使用PreparedStatement
//占内存小,但要不停的与数据库进行交互,效率低
@Test
public void testInsert1() {
Connection conn = null;
PreparedStatement ps = null;
try {
long start = System.currentTimeMillis();//查看时间
conn = JDBCUtils.getConnection();
String sql="insert into goods(name)values(?)";
ps = conn.prepareStatement(sql);
for(int i=0;i<20000;i++) {
ps.setObject(1, "name_"+i);
ps.execute();
}
long end = System.currentTimeMillis();//查看所用时间
System.out.println("花费时间为"+(end-start));
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally {
JDBCUtils.closeResource(conn, ps);
}
}
/*
* 批量插入的方式三:
* 1.addBatch()、executeBatch()、clearBatch0()
* 2.mysql服务器默认是关闭批处理的,我们需要通过一个参数,让mysql开启批处理的支持。
* ?rewriteBatchedStatements=true 写在自己写的配置文件的url后面
* 3.使用更新的mysql 驱动:mysql-connector-java-5.1.37-bin.jar先remove调原先的,再buildpath
*/
@Test
public void testInsert2() {
Connection conn = null;
PreparedStatement ps = null;
try {
long start = System.currentTimeMillis();//查看时间
conn = JDBCUtils.getConnection();
String sql="insert into goods(name)values(?)";
ps = conn.prepareStatement(sql);
for(int i=1;i<=1000000;i++) {
ps.setObject(1, "name_"+i);
//1."攒"sql
ps.addBatch();
if(i%500==0) {
//2.执行batch(每500次执行一下)
ps.executeBatch();
//3.清空batch
ps.clearBatch();
}
//if(i==1999)除不尽的让它单独执行一次
}
long end = System.currentTimeMillis();//查看所用时间
System.out.println("花费时间为"+(end-start));
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally {
JDBCUtils.closeResource(conn, ps);
}
}
/*
* 批量插入的方式四:设置不允许自动提交数据
*/
@Test
public void testInsert3() {
Connection conn = null;
PreparedStatement ps = null;
try {
long start = System.currentTimeMillis();//查看时间
conn = JDBCUtils.getConnection();
//设置不允许自动提交数据
conn.setAutoCommit(false);
String sql="insert into goods(name)values(?)";
ps = conn.prepareStatement(sql);
for(int i=1;i<=1000000;i++) {
ps.setObject(1, "name_"+i);
//1."攒"sql
ps.addBatch();
if(i%500==0) {
//2.执行batch(每500次执行一下)
ps.executeBatch();
//3.清空batch
ps.clearBatch();
}
//if(i==1999)除不尽的让它单独执行一次
}
//都缓存起来,再这里统一的提交数据
conn.commit();
long end = System.currentTimeMillis();//查看所用时间
System.out.println("花费时间为"+(end-start));
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally {
JDBCUtils.closeResource(conn, ps);
}
}
}
/**
* SELECT count(*)#查询个数
FROM goods;
truncate table goods;
#清空表数据
*/
附: 进行以上操作测试的数据库表
1.Customers表
2.Order表
3.user_table表
4.examstudent表