[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8M1ekrTA-1631929613313)(E:\DataBasesWork\PictureTool\JDBC编写步骤.jpg)]
1:Driver
接口实现类@Test
public void test1() throws SQLException {
Driver d=new com.mysql.jdbc.Driver();
String s="jdbc:mysql://localhost:6636/test";
/*
jdbc:mysql 协议
localhost ip地址
6636 端口号
test 数据库
*/
Properties p=new Properties();
//Properties类表示一组持久的属性。 Properties可以保存到流中或从流中加载。 属性列表中的每个键及其对应的值都是一个字符串。
p.setProperty("user","root");//用户名
p.setProperty("password","12345ssdlh");//密码
//将用户名1密码封装在Properties里面
Connection ct = d.connect(s, p);
System.out.println(ct);
}
//获取Driver的实现类对象【运用反射】
@Test
public void test2() throws Exception {
//如下程序不出现第三方的api,使得程序有更好的可移植性
Class<?> clazz = Class.forName("com.mysql.jdbc.Driver");
Driver o = (Driver) clazz.newInstance();
//提供要连接的数据库
String url="jdbc:mysql://localhost:6636/test";
//提供连接需要的用户名和密码
Properties info=new Properties();
info.setProperty("user","root");
info.setProperty("password","12345ssdlh");
Connection connect = o.connect(url, info);
System.out.println(connect);
}
//使用DriverManager
【类】 替换 Driver【接口】
@Test
public void test3()throws Exception{
//获取Driver实现类对象
Class<?> clazz = Class.forName("com.mysql.jdbc.Driver");
Driver o = (Driver) clazz.newInstance();
//注册驱动
DriverManager.registerDriver(o);
//获取连接
Connection root = DriverManager.getConnection("jdbc:mysql://localhost:6636/test", "root", "12345ssdlh");
System.out.println(root.toString());
}
//优化阶段3:可以只是加载驱动,不用手动注册驱动了
@Test
public void test5()throws Exception{
//获取Driver实现类对象
// Class.forName("com.mysql.jdbc.Driver");
//mysql-connector-java-5.1.7-bin.jar!/META-INF/services/java.sql.Driver:读该路径下的文件已经直接做Class.forName(xxx)了,不过只针对mysql,如果连接的是orance数据库则不能省略,故最好不要省略
Class.forName("com.mysql.jdbc.Driver");
// Driver o = (Driver) clazz.newInstance();
//注册驱动
// DriverManager.registerDriver(o);
//获取连接
Connection root = DriverManager.getConnection("jdbc:mysql://localhost:6636/test", "root", "12345ssdlh");
System.out.println(root.toString());
/*省略如上操作
在mysql的Driver实现类中,声明了如下的操作,
static {
try {
java.sql.DriverManager.registerDriver(new Driver());
} catch (SQLException E){
throw new RuntimeException("Can't register driver!");
}
}
*/
}
DriverManager.getConnection(库的地址,用户名,用户密码);//创建连接,返回值为Connection类型
@Test
public void test_final() throws Exception {
/*将数据库连接需要的4个配置信息声明在配置文件中,
通过读取配置文件的方式,获取连接
*/
//读取配置文件中的4个基本信息
InputStream is= ConnectionTest.class.getClassLoader().getResourceAsStream("JDBC.properties");
Properties p=new Properties();
p.load(is);
String user = p.getProperty("user");
String password = p.getProperty("password");
String url = p.getProperty("url");
String driverClass = p.getProperty("driverClass");
//加载驱动
Class.forName(driverClass);
//获取连接
Connection conn = DriverManager.getConnection("url", "user", "password");
System.out.println(conn);
}
配置文件:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mEHW0IzI-1631929613319)(E:\DataBasesWork\PictureTool\JDBC配置文件.jpg)]
此方法的好处
1、实现数据与代码的分离,实现了解耦
2、如果需要修改配置文件,可以避免程序重新打包
mysql
时区在cmd
中,输入mysql -u root -p
,再输入密码,进入mysql
命令行。之后
修改时区:set global time_zone='+8:00';
即可成功。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QRKWYoQP-1631929613320)(E:\DataBasesWork\PictureTool\数据库操作步骤.jpg)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vRIh2PFc-1631929613322)(E:\DataBasesWork\PictureTool\执行SQL.jpg)]
Ststement
在操作数据库时有弊端,故不再使用
PreparedStatement
执行sql
语句的函数:
返回值 | 函数名及其介绍 |
---|---|
boolean |
execute() 执行此 PreparedStatement 对象中的SQL 语句,这可能是任何类型的SQL 语句。 |
default long |
executeLargeUpdate() 执行在该SQL 语句PreparedStatement 对象,它必须是一个SQL 数据操纵语言(DML )语句,如INSERT , UPDATE 或DELETE ; 或不返回任何内容的SQL 语句,例如DDL 语句。 |
ResultSet |
executeQuery() 执行此 PreparedStatement 对象中的SQL 查询,并返回查询 PreparedStatement 的 ResultSet 对象。 |
int |
executeUpdate() 执行在该SQL 语句PreparedStatement 对象,它必须是一个SQL 数据操纵语言(DML )语句,如INSERT , UPDATE 或DELETE ; 或不返回任何内容的SQL 语句,例如DDL 语句。 |
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-m8bvoj54-1631929613326)(E:\DataBasesWork\PictureTool\JAVA与SQL数据类型转换.jpg)]
PreparedStatement
操作Properties
类表示一组持久的属性。 Properties
可以保存到流中或从流中加载。 属性列表中的每个键及其对应的值都是一个字符串。【Properties
是Hashtable
的子类】
Connection实例化对象.prepareStatement(sql语句);//创建一个 PreparedStatement对象,用于将参数化的SQL语句发送到数据库。返回值为prepareStatement类型
prepareStatement实例化对象.execute();//执行此 PreparedStatement对象中的SQL语句,这可能是任何类型的SQL语句。返回值为boolean类型
如果是查询操作的话返回true,如果是增删改操作的话返回false
@Test
public void test(){
/*
使用PreparedStatement来代替Statement进行对数据库的增删改查操作
*/
Connection conn = null;
PreparedStatement ps = null;
try {
//InputStream ra = ClassLoader.getSystemResourceAsStream("JDBC.properties");
//获取连接
InputStream ra = ClassLoader.getSystemClassLoader().getResourceAsStream("JDBC.properties");
Properties p=new Properties();
p.load(ra);
String user = p.getProperty("user");
String password = p.getProperty("password");
String url = p.getProperty("url");
String driverClass = p.getProperty("driverClass");
Class.forName(driverClass);
conn = DriverManager.getConnection(url, user, password);
System.out.println(conn);
//预编译sql语句,返回PreparedStatement的实例
String sql="insert into customers(name,email)values (?,?)";/*?起到占位符的作用*/
ps = conn.prepareStatement(sql);
//填充占位符
ps.setString(1,"张飞");
ps.setString(2,"null");
//执行sql语句
ps.execute();
} catch (Exception e) {
e.printStackTrace();
} finally {
//关闭资源
try {
if(ps!=null)
{
ps.close();
}
} catch (SQLException throwables) {
throwables.printStackTrace();
}
try {
if(conn!=null)
{
conn.close();
}
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
由于涉及到多个异常,资源的关闭,此时就不能使用throws抛出错误,要用try/catch;
***throws与try/catch的使用区别:***throws exception会将异常上抛,在循环中会中断循环;try catch将异常捕获,不影响下一条执行
【以删除为例】
@Test
public void test3() {
try {
String s="delete from customers where id=?";
test_tool(s,22);
} catch (Exception e) {
e.printStackTrace();
}
}
public void test_tool(String s,Object ...args) {
Connection conn=null;
PreparedStatement ps=null;
try {
conn = JDBC_utils.get_connection();
ps=conn.prepareStatement(s);
for(int i=0;i< args.length;i++){
ps.setObject(i+1,args[i]);
}
ps.execute();
} catch (Exception e) {
e.printStackTrace();
} finally {
JDBC_utils.Close_resource(conn,ps);}
}
【如果表名和`sql`语句关键字一样时,通过`jdbc`调用`sql`语句时表名要写成 `表名` ;不然会报错】
**情况:**类中的属性名和数据库中的列名刚好相同
prepareStatement实例化对象.executeQuery();//执行此 PreparedStatement对象中的SQL查询,并返回查询 PreparedStatement的 ResultSet对象。
ResultSet实例化对象.getMetaData();//获取结果集的元数据,通过元数据来对结果集进行深度解析,返回值类型为ResultSetMetaData
ResultSet实例化对象.getObject(下标数【mysql中下标从1开始】);//获取数据库中该下标指向列的数据;返回类型为Object
ResultSetMetaData实例化对象.getColumnCount();//获取结果集的列数,返回值为int
ResultSetMetaData实例化对象.getColumnName(下标数【mysql中下标从1开始】);//获取数据库中该下标指向列的列名;返回类型为String
@Test
public void test4(){
String sql="select id,name,email,birth from customers where id=?";
Customer customer = test4_Tool(sql, 5);
System.out.println(customer);
}
public Customer test4_Tool(String sql,Object...args){
//针对customers这个表的通用查询操作
Connection conn=null;
PreparedStatement ps=null;
ResultSet rs=null;
try {
conn=JDBC_utils.get_connection();
ps=conn.prepareStatement(sql);
for(int i=0;i<args.length;i++){
ps.setObject(i+1,args[i]);
}
ResultSet resultSet = ps.executeQuery();
//获取结果集的元数据
ResultSetMetaData rsmd = resultSet.getMetaData();
//通过结果集元数据【ResultSetMetaData】获取结果集的列数
int cc = rsmd.getColumnCount();
if (resultSet.next()){
Customer c=new Customer();
for (int i=0;i<cc;i++){
Object value = resultSet.getObject(i + 1);
//获取每个列的列名
String cn = rsmd.getColumnName(i + 1);
//通过反射设置属性
Class<?> clazz = Class.forName("com.study.util.Customer");
Field df = clazz.getDeclaredField(cn);
df.setAccessible(true);
//给c对象某个属性,赋值为value;
df.set(c,value);
}
return c;
}
} catch (Exception e) {
e.printStackTrace();
} finally {
JDBC_utils.Close_resource(conn,ps);
try {
if(rs!=null)
rs.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
return null;
}
ORM
编程思想(object relational mapping)一个数据对应一个java
类
表中的一条数据对应java
类的一个对象
表中的一个字段对应java
类的一个属性
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tWoUap5Q-1631929613327)(E:\DataBasesWork\PictureTool\Customer类.jpg)]
**情况:**类中的属性名和数据库中的列名不相同
ResultSetMetaData实例化对象.getColumnName(下标数【mysql中下标从1开始】);//不推荐使用
ResultSetMetaData实例化对象.getColumnLabel(下标数【mysql中下标从1开始】);//获取数据库中每个列的列名的别名,如果没起别名则获取数据库中该下标指向列的列名;返回类型为String
***例子:***对order表的通用查询
@Test
public void test2() {
//对order表的通用查询
String sql="select order_id as orderId,order_name as orderName,order_date as orderDate from `order` where order_id=?";
Order order = test2_tool(sql, 1);
System.out.println(order);
}
public Order test2_tool(String sql,Object...args) {
Connection conn = null;
PreparedStatement ps=null;
ResultSet rs=null;
try {
conn = Datebases_Tools.get_connection();
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();
if (rs.next()){
//获取列数
int count = rsmd.getColumnCount();
Order or=new Order();
for(int i=0;i<count;i++){
//获取每个列的列值(通过结果集)
Object object = rs.getObject(i + 1);
/*
获取数据库中每个列的列名(通过结果集的元数据)
String columnName = rsmd.getColumnName(i + 1);//不推荐使用
但是数据库中每列的列名与类中的属性名不一定相同
此时就需要获取类的别名,并且在编辑sql语句时
用类的属性名作为列的别名
*/
//获取数据库中每个列的列名的别名(通过结果集的元数据)
String columnLabel = rsmd.getColumnLabel(i + 1);
//通过反射,将对象指定名的属性赋值为指定的值
Class<?> clazz = Class.forName("com.study.class_tools.Order");
Field df = clazz.getDeclaredField(columnLabel);
df.setAccessible(true);
df.set(or,object);
}
return or;
}
} catch (Exception throwables) {
throwables.printStackTrace();
} finally {
Datebases_Tools.close_resource(conn,ps,rs);
}
return null;
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ljFRZKIe-1631929613328)(E:\DataBasesWork\PictureTool\order类.jpg)]
**情况:**针对不同的表的通用的查询操作,返回表中的一条记录
//下面是泛型方法的定义:
[public] [static] <T> 返回值类型 方法名(T 参数列表){函数体}
【根本作用:使得函数返回值类型为泛型T】
泛型方法能使方法独立于类而产生变化,以下是一个基本的指导原则:
无论何时,如果你能做到,你就该尽量使用泛型方法。也就是说,如果使用泛型方法将整个类泛型化,
那么就应该使用泛型方法。另外对于一个static的方法而已,无法访问泛型类型的参数。
所以如果static方法要使用泛型能力,就必须使其成为泛型方法。
@Test
public void test3(){
//对不同表的通用查询操作
String sql="select id,name,email from customers where id =?";
Customer customer = test3_tool(Customer.class, sql, 1);
System.out.println(customer);
sql="select order_id as orderId,order_name as orderName from `order` where order_id=?";
Order order = test3_tool(Order.class, sql, 2);
System.out.println(order);
}
public <T> T test3_tool(Class<T> clazz,String sql,Object...args){
//泛型方法
Connection conn =null;
PreparedStatement ps=null;
ResultSet rs=null;
try {
conn =Datebases_Tools.get_connection();
ps = conn.prepareStatement(sql);
for(int i=0;i<args.length;i++){
ps.setObject(i+1,args[i]);
}
rs = ps.executeQuery();
if(rs.next()){
T t = clazz.newInstance();
ResultSetMetaData rsmd = rs.getMetaData();
int columnCount = rsmd.getColumnCount();
for(int i=0;i<columnCount;i++){
String columnLabel = rsmd.getColumnLabel(i + 1);
Object value = rs.getObject(i + 1);
Field declaredField = clazz.getDeclaredField(columnLabel);
declaredField.setAccessible(true);
declaredField.set(t,value);
}
return t;
}
} catch (Exception throwables) {
throwables.printStackTrace();
} finally {
Datebases_Tools.close_resource(conn,ps,rs);
}
return null;}
**情况:**针对不同的表的通用的查询操作,返回表中的多条记录
forEach(Consumer super E> action);//对Iterable的每个元素执行给定的操作,直到所有元素都被处理或动作引发异常。
@Test
public void test4(){
String sql="select order_id as orderId,order_name as orderName,order_date as orderDate from `order` where order_id>?";
List<Order> orders =test4_test(Order.class, sql, 0);
// for(int i=0;i
// System.out.println(orders.get(i));
// }
orders.forEach(System.out::println);
//输出
sql ="select id,name,email from customers";
List<Customer> customers = test4_test(Customer.class, sql);
customers.forEach(System.out::println);
}
public <T> List<T> test4_test(Class<T> clazz,String sql, Object...args){
Connection conn=null;
PreparedStatement ps=null;
ResultSet rs=null;
List<T> lt=new ArrayList<T>();
try {
conn=Datebases_Tools.get_connection();
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();
while (rs.next()){
T t = clazz.newInstance();
int columnCount = rsmd.getColumnCount();
for(int i=0;i<columnCount;i++){
Object value = rs.getObject(i + 1);
String columnLabel = rsmd.getColumnLabel(i + 1);
Field declaredField = clazz.getDeclaredField(columnLabel);
declaredField.setAccessible(true);
declaredField.set(t,value);
}
lt.add(t);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
Datebases_Tools.close_resource(conn,ps,rs);
return lt;
}
}
PreparedStatement
补充知识点:SQL
语句的对象,解决SQL
注入问题原理;①、PreparedStatement
可操作Blob的数据,而Statement
做不到
②、PreparedStatement
可以实现更高效的批量操作
executeUpdate()
JDBC
操纵数据库实战**说明:**学生四六级考试查询系统
import com.study.Datebases_Tools;
import com.study.class_tools.ExamStudent;
import java.lang.reflect.Field;
import java.sql.*;
import java.util.Scanner;
public class CET_4 {
public static void main(String[] args) {
int i=1;
while(i>0){
Scanner in=new Scanner(System.in);
System.out.println("请输入要执行的操作:1、添加学生信息。2、查询学生信息。3、删除学生信息。4、退出系统");
int a=in.nextInt();
if(a==1){
String sql="insert into examstudent values(?,?,?,?,?,?,?)";
System.out.print("请输入FlowID:");
int folwid=in.nextInt();
System.out.print("请输入类型:");
int type=in.nextInt();
System.out.print("请输入身份证:");
String idcard=in.next();
System.out.print("请输入考号:");
String ExamCard=in.next();
System.out.print("请输入学生姓名:");
String StudentName=in.next();
System.out.print("请输入家庭住址:");
String location=in.next();
System.out.print("请输入年级:");
int grade=in.nextInt();
CET_4_Tool1(ExamStudent.class,a,sql,folwid,type,idcard,ExamCard,StudentName,location,grade);
}
else if(a==2){
String sql="select FlowID as flowid,`Type` as type,IDCard as ID,ExamCard as Exam,StudentName as name,Location as location,Grade as grade from examstudent where FlowID=?;";
System.out.println("请输入需查询信息的学生的FlowID");
int flowid=in.nextInt();
ExamStudent es = CET_4_Tool1(ExamStudent.class, a, sql, flowid);
if(es==null){
System.out.println("查无此人");
}
else {
System.out.println(es.toString());
}
}
else if(a==3){
System.out.println("请输入需删除信息的学生的FlowID");
String sql="delete from examstudent where FlowID=?";
int flowid=in.nextInt();
CET_4_Tool1(ExamStudent.class,a,sql,flowid);
}
else if(a==4){
break;
}
}
System.out.println("退出成功");
}
public static T CET_4_Tool1(Class clazz,int chance,String sql,Object...args){
Connection conn =null;
PreparedStatement ps =null;
ResultSet rs =null;
try {
conn = Datebases_Tools.get_connection();
ps = conn.prepareStatement(sql);
for(int i=0;i
用到的类:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hZTG8jvu-1631929613328)(E:\DataBasesWork\PictureTool\四六级系统实战.jpg)]
类型 | 大小(单位:字节) |
---|---|
TinyBlob | <=255字节 |
Blob | <=65K |
MediumBlob | <=16M |
LongBlob | <=4G |
.setBlob(int parameterIndex, InputStream inputStream)
将指定的参数设置为 InputStream对象。
Blob类型:MYSQL中,Blob是一个二进制大型对象,是一个可以存储大量数据的容器,它能容纳不同大小的数据
***
如果在指定了相关的Blob类型以后,还报错:xxx too large,那么在mysql的安装目录下,找到my.ini文件加上如下的配置参数:max_allowed_packet=16M。同时注意:修改了my.ini文件之后,需要重新启动mysql服务
@Test
public void testInsert()throws Exception{
InputStream is=new FileInputStream("E:\\软件包暂存\\wbb.jpg");
Connection conn = Datebases_Tools.get_connection();
String sql="update customers set photo=? where id=?";
PreparedStatement ps = conn.prepareStatement(sql);
ps.setBlob(1,is);
ps.setObject(2,22);
ps.execute();
Datebases_Tools.close_resource(conn,ps);
is.close();
}
Blob类型对象.getBinaryStream();//将此 Blob实例指定的 BLOB值作为流 Blob;返回值类型为InputStream
@Test
public void test_select_blob(){
Connection conn=null;
PreparedStatement ps=null;
ResultSet rs=null;
InputStream is=null;
FileOutputStream fos=null;
try {
conn=Datebases_Tools.get_connection();
String sql="select photo from customers where id=?";
ps = conn.prepareStatement(sql);
ps.setObject(1,16);
rs=ps.executeQuery();
if(rs.next()){
Blob photo = rs.getBlob(1);
is = photo.getBinaryStream();
fos=new FileOutputStream("E:\\Desktop\\朱颖.jpg");
byte[] b=new byte[1024];
int len;
while((len=is.read(b))!=-1){
fos.write(b,0,len);
}
}
} catch (Exception e) {
e.printStackTrace();
} finally {
Datebases_Tools.close_resource(conn,ps,rs);
try {
if(is!=null)
is.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
if(fos!=null)
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
初始
Connection类型实例.setAutoCommit(boolean t);//将此连接的自动提交模式设置为给定状态。
Connection类型实例.commit();// 使自上次提交/回滚以来所做的所有更改都将永久性,并释放此 Connection对象当前持有的任何数据库锁。
PreparedStatement实例对象.addBatch();//向这个PreparedStatement对象的一批命令添加一组参数。
PreparedStatement实例对象.executeBatch();//向数据库提交要运行的一批命令。如果所有命令都成功运行,则返回一个更新计数数组;
向数据库提交命令后,此方法将清除批中的所有命令;
通常是通过先使用addBatch()来缓存数据,将多条sql语句缓存起来,再通过executeBatch()方法一次性发给数据库,提高执行效率;
注意:
使用addBatch()缓存数据时要在循环中设置条件,当循环达到指定次数后执行executeBatch(),将缓存中的sql全部发给数据库,然后执行clearBatch()清楚缓存,否则数据过大是会出现OutOfMemory(内存不足)。
PreparedStatement实例对象.clearBatch();//
【javaAPI查不到】
@Test
public void test_batch_insert(){
//快速批量插入
Connection conn = null;
PreparedStatement ps = null;
try {
long start= System.currentTimeMillis();
conn = Datebases_Tools.get_connection();
//设置并不允许自动提交
conn.setAutoCommit(false);
String sql="insert into a1 set age=?";
ps = conn.prepareStatement(sql);
for(int i=0;i<66666;i++){
ps.setObject(1,"name_"+i);
//1、"攒”SQL
ps.addBatch();
if(i%1000==0||i==66665){
//2、执行batch
ps.executeBatch();
//3、清空batch
ps.clearBatch();
}
}
conn.commit();
long end= System.currentTimeMillis();
System.out.println((end-start)+"ms");
} catch (SQLException throwables) {
throwables.printStackTrace();
} finally {
Datebases_Tools.close_resource(conn,ps);
}
}
PreparedStatement
与Statement
对比(面试题)PreparedStatement
是statement的子接口
PreparedStatement
是预编译sql语句
1、Statement
存在拼串和SQL
注入问题
2、PreparedStatement
可对blob类型数据进行操作,Statement
则不能
(因为PreparedStatement
存在预编译这一操作)
3、在批量插入数据时PreparedStatement
更高效
(因为PreparedStatement
存在预编译这一操作:
**DBServer
会对预编译语句提供性能优化,因为预编译语句有可能被重复使用,所以语句在被DBServer
的编译器编译后的执行代码会被缓存下来,下次调用时只需要将相同的预编译语句就不需要编译,只要将参数直接传入编译过的代码中就会得到执行**)
一组逻辑操作单元,使数据从一种状态变换到另一种状态
保证所有事物都将作为一个工作单元来执行,即使出现了故障,都不能改变这种执行方式,当在一个事务中执行多个操作时,要么所有的事务都被提交(commit),那么这些修改就永久保存下来;要么数据库管理系统将放弃所做的修改,整个事务回滚(rollback)到最初状态。
数据一旦提交就不可回滚。
为确保数据库中数据的一致性,数据的操纵应当是离散的成组的逻辑单元:当它全部完成时,数据的一致性可以保持,而当这个单元中的一部分操作失败,整个事务应当全部视为错误,所有从起始点以后的操作应全部回退到开始状态。
1、DDL操作一旦执行,都会自动提交
(set autocommit=false
对DDL
操作失效)
2、DML默认情况下,一旦执行,就会自动提交
(可以通过set autocommit=false
的方式取消DML
操作的自动提交)
3、默认在关闭连接时,会自动提交数据
事例:AA给BB转账100(运用事务)
@Test
public void test1(){
Connection conn = null;
try {
conn = JDBC_utils.get_Connection();
//取消数据的自动提交
System.out.println(conn.getAutoCommit());
conn.setAutoCommit(false);
String sql1="update user_table set balance=balance-100 where user=?";
test1_tool(conn,sql1,"AA");
System.out.println(10/0);//模拟系统出现故障
String sql2="update user_table set balance=balance+100 where user=?";
test1_tool(conn,sql2,"BB");
System.out.println("转账成功");
//提交数据
conn.commit();
} catch (Exception e) {
e.printStackTrace();
//回滚数据
try {
conn.rollback();
System.out.println("转账失败");
} catch (SQLException e1) {
e1.printStackTrace();
}
} finally {
JDBC_utils.close_resources(conn,null);
}
}
public int test1_tool(Connection conn,String sql,Object...args){
PreparedStatement ps = null;
int i1 =0;
try {
ps = conn.prepareStatement(sql);
for(int i=0;i<args.length;i++){
ps.setObject(i+1,args[i]);
}
i1=ps.executeUpdate();
} catch (SQLException throwables) {
throwables.printStackTrace();
} finally {
JDBC_utils.close_resources(null,ps);
return i1;
}
}
1、原子性
原子性是指事务是一个不可分割的工作单位,事务中的操作要么都发生,要么都不发生
2、一致性
事务必须使数据库从一个一致性状态变换到另外一个一致性状态
3、隔离性
事务的隔离性是指一个事务的执行不能被其他事务干扰,即一个事务内部的操作及使用的数据对并发的其他事务是隔离的,并发执行的各个事务之间不能相互干扰
4、持久性
持久性是指一个事务一旦被提交,它对数据库中数据的改变就是永久性的,接下来的其他操作和数据库故障不应该对其有任何影响
保证事务都作为一个工作单元来执行,即使出了故障,都不能改变这种执行方式,当在一个事务执行多次操作时,要么所有的事务都提交(commit),这些修改就永久保存下来。要么数据库管理系统将放弃所做的修改,整个事务进行回滚(rollback)回到最初状态。
对于同时运行的多个事务,当这些事务访问数据库中相同的数据时,如果没有采取必要的隔离机制,就会导致各种并发问题:
>脏读:对于两个事务T1,T2;T1
读取了已经被T2
更新但还没有被提交的字段之后,若T2
回滚,T1
读取的内容就是临时且无效的
**>不可重复读:**对于两个事务T1,T2;T1
读取了一个字段,然后T2
更新了该字段之后,T1
再次读取了同一字段,值就不同了
>幻读:对于两个事务T1,T2;T1
从一个表中读取了一个字段,然后T2
在该表中插入了一些新的行,之后,如果T1
再次读取同一个表,就会多出几行
【详情见MYSQL
笔记】
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jMhZa6JS-1631929613329)(E:\DataBasesWork\PictureTool\数据库隔离级别.jpg)]
Connection实例对象.getTransactionIsolation()//获取当前连接的隔离级别,返回值为int类型,具体含义见下文
int TRANSACTION_NONE = 0;
int TRANSACTION_READ_UNCOMMITTED = 1;
int TRANSACTION_READ_COMMITTED = 2;
int TRANSACTION_REPEATABLE_READ = 4;
int TRANSACTION_SERIALIZABLE = 8;
Connection实例对象.setAutoCommit(false);//关闭当前连接的自动提交功能
Connection实例对象.getAutoCommit()//查看当前连接的自动提交功能是否打开
Connection实例对象.commit();//关闭当前连接的自动提交功能之后进行手动提交操作
Connection实例对象.rollback();//关闭当前连接的自动提交功能之后进行手动回滚操作
【两者刚好对立】
Connection实例对象.setTransactionIsolation(Connection.隔离级别||int level);//设置当前连接的隔离级别
所有隔离级别将在重启数据库服务时返回默认状态(包括在控制台更改的隔离级别)
DAO
及其实现类DAO:data(base) access object
(数据库访问对象)
封装了针对于数据库的通用操作
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MMyjcG4D-1631929613329)(E:\DataBasesWork\PictureTool\BAO模式思维图.jpg)]
Class实例对象.getGenericSuperclass();//返回值Type类型,表示此所表示的实体(类,接口,基本类型或void)的直接超类(即父类) 。
Type类型:Type是Java编程语言中所有类型的通用超级接口。 这些包括原始类型,参数化类型,数组类型,类型变量和原始类型。
ParameterizedType类型:ParameterizedType表示一个参数化类型,如Collection <String>,set<int>。 参数化类型在第一次通过反射方法需要时创建,当创建参数化类型p时,p实例化的泛型类型声明被解析,并且p的所有类型参数都是递归创建的。
ParameterizedType实例对象.getActualTypeArguments();//返回一个表示此类型的实际类型参数的Type数组对象。 (Type数组索引从0开始)
//具体类《Customer》
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QHclqsgS-1631929613330)(E:\DataBasesWork\PictureTool\Customer类.jpg)]
class BaseDAO
**//通用方法带泛型父类《BaseDAO
》:**封装了针对于数据表的通用的操作
import tools.JDBC_utils;
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;
//获取当前BaseDAO的子类继承的父类中的具体泛型(也就是本代码中创建子类时的"BaseDAO"中的"Customer")
public BaseDAO() {
//获取父类的泛型
Type gsc = this.getClass().getGenericSuperclass();
ParameterizedType pt= (ParameterizedType) gsc;
Type[] ata = pt.getActualTypeArguments();
clazz= (Class<T>) ata[0];
}
//查一个
public T select(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()){
ResultSetMetaData rsmd = rs.getMetaData();
T t=clazz.newInstance();
for(int i=0;i<rsmd.getColumnCount();i++){
String columnLabel = rsmd.getColumnLabel(i + 1);
Object value = rs.getObject(i + 1);
Field filed = clazz.getDeclaredField(columnLabel);
filed.setAccessible(true);
filed.set(t,value);
}
return t;
}
} catch (Exception e) {
e.printStackTrace();
} finally {
JDBC_utils.close_resources(null,ps,rs);
}
return null;
}
//查多个
public List<T> selects(Connection conn, String sql, Object...args){
PreparedStatement ps=null;
ResultSet rs=null;
List<T> t1=new ArrayList<>();
try {
ps=conn.prepareStatement(sql);
for(int i=0;i<args.length;i++){
ps.setObject(i+1,args[i]);
}
rs=ps.executeQuery();
while(rs.next()){
ResultSetMetaData rsmd = rs.getMetaData();
T t=clazz.newInstance();
for(int i=0;i<rsmd.getColumnCount();i++){
Object value = rs.getObject(i + 1);
String columnLabel = rsmd.getColumnLabel(i + 1);
Field field = clazz.getDeclaredField(columnLabel);
field.setAccessible(true);
field.set(t,value);
}
t1.add(t);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
JDBC_utils.close_resources(null,ps,rs);
return t1;
}
}
//增删改
public int update(Connection conn,String sql,Object...args){
PreparedStatement ps = null;
int i1 =0;
try {
ps = conn.prepareStatement(sql);
for(int i=0;i<args.length;i++){
ps.setObject(i+1,args[i]);
}
return ps.executeUpdate();
} catch (SQLException throwables) {
throwables.printStackTrace();
} finally {
JDBC_utils.close_resources(null,ps);
}
return 0;
}
//查询特殊值,比如“select count(*) from 表名”
public <T> T 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 (T) rs.getObject(1);
}
} catch (SQLException throwables) {
throwables.printStackTrace();
} finally {
JDBC_utils.close_resources(null,ps,rs);
}
return null;}
}
interface CustomerDAO
**//用于规定具体对象方法操作的接口《CustomerDAO
》:**此接口用于规范针对于Customers表的常用操作
import Class_tools.Customer;
import java.sql.Connection;
import java.sql.Date;
import java.util.List;
/*
此接口用于规范针对于Customers表的常用操作
*/
public interface CustomerDAO {
//将cust对象添加到数据库中
void insert(Connection conn, Customer cust);
//根据指定的id删除表中一条记录
void deleteById(Connection conn,int id);
//针对内存中的cust对象,去修改数据表中指定的记录
void update(Connection conn,Customer cust);
//针对指定的id查询得到对应的Customer对象
Customer getCustomerById(Connection conn,int id);
//查询表中的所有记录构成的集合
List getAll(Connection conn);
//返回数据表中数据的条目数
Long getCount(Connection conn);
//返回数据表中最大的生日
Date getMaxBirth(Connection conn);
}
class CustomerDAOImpl
**//具体对象方法实现类《CustomerDAOImpl
》:**用于实现接口中所规定的类
import interface_tools.CustomerDAO;
import test.DAO.BaseDAO;
import java.sql.Connection;
import java.sql.Date;
import java.util.List;
public class CustomerDAOImpl extends BaseDAO<Customer> implements CustomerDAO {
@Override
public void insert(Connection conn, Customer cust) {
String sql="insert into customers(id,name,email,birth) values(?,?,?,?)";
update(conn,sql,cust.getId(),cust.getName(),cust.getEmail(),cust.getBirth());
}
@Override
public void deleteById(Connection conn, int id) {
String sql="delete from customers where id=?";
update(conn,sql,id);
}
@Override
public void update(Connection conn, Customer cust) {
String sql="update customers set name=?,email=?,birth=? where id=?";
update(conn,sql,cust.getName(),cust.getEmail(),cust.getBirth(),cust.getId());
}
@Override
public Customer getCustomerById(Connection conn, int id) {
String sql="select id,`name`,email,birth from customers where id=?";
return select(conn,sql,id);
}
@Override
public List<Customer> getAll(Connection conn) {
String sql="select id,name,email,birth from customers";
return selects(conn,sql);
}
@Override
public Long getCount(Connection conn) {
String sql="select count(*) from customers";
return getValue(conn,sql);
}
@Override
public Date getMaxBirth(Connection conn) {
String sql="select max(birth) from customers";
return getValue(conn,sql);
}
}
class CustomerDAOImplTest
**//具体对象方法实现类的测试函数《CustomerDAOImplTest
》:**测试所写的实现类方法是否正确或测试整个DAO
代码是否正确
import Class_tools.Customer;
import Class_tools.CustomerDAOImpl;
import org.junit.Test;
import tools.JDBC_utils;
import java.sql.Connection;
import java.sql.Date;
import java.util.List;
public class CustomerDAOImplTest {
private CustomerDAOImpl cdit=new CustomerDAOImpl();
@Test
public void insert() {
Connection conn = null;
try {
conn = JDBC_utils.get_Connection();
Customer cust=new Customer(26,"包师语","@shiyu.com",new Date(89889654145L));
cdit.insert(conn,cust);
System.out.println("添加成功!");
} catch (Exception e) {
e.printStackTrace();
} finally {
JDBC_utils.close_resources(conn,null);
}
}
@Test
public void deleteById() {
Connection conn = null;
try {
conn = JDBC_utils.get_Connection();
cdit.deleteById(conn,6);
System.out.println(" 删除成功!");
} catch (Exception e) {
e.printStackTrace();
} finally {
JDBC_utils.close_resources(conn,null);
}
}
@Test
public void update() {
Connection conn = null;
try {
conn = JDBC_utils.get_Connection();
Customer cust=new Customer(26,"包师语","@shiyu.com",new Date(489889654145L));
cdit.update(conn,cust);
System.out.println(" 修改成功!");
} catch (Exception e) {
e.printStackTrace();
} finally {
JDBC_utils.close_resources(conn,null);
}
}
@Test
public void getCustomerById() {
Connection conn = null;
try {
conn = JDBC_utils.get_Connection();
Customer cust = cdit.getCustomerById(conn, 21);
System.out.println(cust);
System.out.println(" 查找成功!");
} catch (Exception e) {
e.printStackTrace();
} finally {
JDBC_utils.close_resources(conn,null);
}
}
@Test
public void getAll() {
Connection conn = null;
try {
conn = JDBC_utils.get_Connection();
List<Customer> all = cdit.getAll(conn);
for(int i=0;i<all.size();i++){
System.out.println(all.get(i));
}
System.out.println(" 查看成功!");
} catch (Exception e) {
e.printStackTrace();
} finally {
JDBC_utils.close_resources(conn,null);
}
}
@Test
public void getCount() {
Connection conn = null;
try {
conn = JDBC_utils.get_Connection();
Long count = cdit.getCount(conn);
System.out.println(count);
System.out.println(" 统计成功!");
} catch (Exception e) {
e.printStackTrace();
} finally {
JDBC_utils.close_resources(conn,null);
}
}
@Test
public void getMaxBirth() {
Connection conn = null;
try {
conn = JDBC_utils.get_Connection();
Date mb = cdit.getMaxBirth(conn);
System.out.println(mb);
System.out.println(" 成功!");
} catch (Exception e) {
e.printStackTrace();
} finally {
JDBC_utils.close_resources(conn,null);
}
}
}
DBCP
数据库连接池DBCP
是 Apache 软件基金组织下的开源连接池实现,该连接池依赖该组织下的另一个开源系统:Common-pool。如需使用该连接池实现,应在系统中增加如下两个 jar 文件:
Commons-dbcp.jar
:连接池的实现Commons-pool.jar
:连接池实现的依赖库conn.close()
; 但上面的代码并没有关闭数据库的物理连接,它仅仅把数据库连接释放,归还给了数据库连接池。属性 | 默认值 | 说明 |
---|---|---|
initialSize |
0 | 连接池启动时创建的初始化连接数量 |
maxActive |
8 | 连接池中可同时连接的最大的连接数 |
maxIdle |
8 | 连接池中最大的空闲的连接数,超过的空闲连接将被释放,如果设置为负数表示不限制 |
minIdle |
0 | 连接池中最小的空闲的连接数,低于这个数量会被创建新的连接。该参数越接近maxIdle ,性能越好,因为连接的创建和销毁,都是需要消耗资源的;但是不能太大。 |
maxWait |
无限制 | 最大等待时间,当没有可用连接时,连接池等待连接释放的最大时间,超过该时间限制会抛出异常,如果设置-1表示无限等待 |
poolPreparedStatements |
false | 开启池的Statement是否prepared |
maxOpenPreparedStatements |
无限制 | 开启池的prepared 后的同时最大连接数 |
minEvictableIdleTimeMillis |
连接池中连接,在时间段内一直空闲, 被逐出连接池的时间 | |
removeAbandonedTimeout |
300 | 超过时间限制,回收没有用(废弃)的连接 |
removeAbandoned |
false | 超过removeAbandonedTimeout 时间后,是否进 行没用连接(废弃)的回收 |
BasicDataSource:通过JavaBeans属性配置的javax.sql.DataSource的基本实现。 这不是结合commons-dbcp和commons-pool软件包的唯一方法,但是为基本需求提供了“一站式”解决方案。
BasicDataSource实例对象.setDriverClassName(String driverClassName);//设置jdbc驱动程序类名称。
BasicDataSource实例对象.setUrl(String url);//设置库地址
BasicDataSource实例对象.setUsername(String username);//设置用户名
BasicDataSource实例对象.setPassword(String password);//设置用户名密码
public static Connection getConnection3() throws Exception {
BasicDataSource source = new BasicDataSource();
source.setDriverClassName("com.mysql.jdbc.Driver");
source.setUrl("jdbc:mysql:///test");
source.setUsername("root");
source.setPassword("abc123");
//
source.setInitialSize(10);
Connection conn = source.getConnection();
return conn;
}
//使用dbcp数据库连接池的配置文件方式,获取数据库的连接:推荐
private static DataSource source = null;
static{
try {
Properties pros = new Properties();
InputStream is = DBCPTest.class.getClassLoader().getResourceAsStream("dbcp.properties");
pros.load(is);
//根据提供的BasicDataSourceFactory创建对应的DataSource对象
source = BasicDataSourceFactory.createDataSource(pros);
} catch (Exception e) {
e.printStackTrace();
}
}
public static Connection getConnection4() throws Exception {
Connection conn = source.getConnection();
return conn;
}
其中,src
下的配置文件为:【dbcp.properties
】
driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/test?rewriteBatchedStatements=true&useServerPrepStmts=false
username=root
password=abc123
initialSize=10
#...
Druid是阿里巴巴开源平台上一个数据库连接池实现,它结合了C3P0、DBCP、Proxool
等DB池的优点,同时加入了日志监控,可以很好的监控DB池连接和SQL
的执行情况,可以说是针对监控而生的DB连接池,可以说是目前最好的连接池之一。
BasicDataSourceFactory.createDataSource(Properties properties);//静态方法;根据给定的属性创建和配置BasicDataSource实例。 返回值为DataSource类型
DataSource:Interface ,一个连接到这个DataSource对象所代表的物理数据源的工厂。DriverManager设备的DriverManager ,DataSource对象是获取连接的首选方法。 实现DataSource接口的对象通常将基于Java“命名和目录(JNDI)API”的命名服务注册。
DataSource实例对象.getConnection();//无参
DataSource实例对象.getConnection(String username, String password);//有参
//**/尝试与此 DataSource 对象表示的数据源建立连接。 【通俗讲就是数据库连接池创建数据库连接】返回值类型为 Connection。
DruidDataSource
:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-L9yAsv3C-1631929613331)(E:\DataBasesWork\PictureTool\DruidDataSource回顾详解.jpg)]
@Test
public void getConnection3()throws Exception{
Properties p=new Properties();
InputStream is=ClassLoader.getSystemResourceAsStream("Druid.properties");
p.load(is);
DataSource ds = DruidDataSourceFactory.createDataSource(p);
Connection conn = ds.getConnection();
System.out.println(conn);
}
Druid.properties
:
#基本属性
url=jdbc:mysql://localhost:6636/test
username=root
password=12345ssdlh
driverClassName=com.mysql.cj.jdbc.Driver
#其他涉及数据库连接池管理的相关属性
initialSize=10#初始连接数
maxActive=10#最大连接数
配置 | 缺省 | 说明 |
---|---|---|
name | 配置这个属性的意义在于,如果存在多个数据源,监控的时候可以通过名字来区分开来。 如果没有配置,将会生成一个名字,格式是:”DataSource-” + System.identityHashCode(this) |
|
url |
连接数据库的url ,不同数据库不一样。例如:mysql : jdbc:mysql://10.20.153.104:3306/druid2 oracle : jdbc:oracle:thin:@10.20.149.85:1521:ocnauto |
|
username |
连接数据库的用户名 | |
password | 连接数据库的密码。如果你不希望密码直接写在配置文件中,可以使用ConfigFilter 。 |
|
driverClassName |
根据url 自动识别 这一项可配可不配,如果不配置druid会根据url 自动识别dbType ,然后选择相应的driverClassName (建议配置下) |
|
initialSize |
0 | 初始化时建立物理连接的个数。初始化发生在显示调用init 方法,或者第一次getConnection 时 |
maxActive |
8 | 最大连接池数量 |
maxIdle |
8 | 已经不再使用,配置了也没效果 |
minIdle |
最小连接池数量 | |
maxWait |
获取连接时最大等待时间,单位毫秒。配置了maxWait 之后,缺省启用公平锁,并发效率会有所下降,如果需要可以通过配置useUnfairLock 属性为true使用非公平锁。 |
|
poolPreparedStatements |
false | 是否缓存preparedStatement ,也就是PSCache。PSCache 对支持游标的数据库性能提升巨大,比如说oracle。在mysql 下建议关闭。 |
maxOpenPreparedStatements |
-1 | 要启用PSCache ,必须配置大于0,当大于0时,poolPreparedStatements 自动触发修改为true。在Druid中,不会存在Oracle下PSCache 占用内存过多的问题,可以把这个数值配置大一些,比如说100 |
validationQuery |
用来检测连接是否有效的sql ,要求是一个查询语句。如果validationQuery 为null,testOnBorrow、testOnReturn、testWhileIdle 都不会其作用。 |
|
testOnBorrow |
true | 申请连接时执行validationQuery 检测连接是否有效,做了这个配置会降低性能。 |
testOnReturn |
false | 归还连接时执行validationQuery 检测连接是否有效,做了这个配置会降低性能 |
testWhileIdle |
false | 建议配置为true,不影响性能,并且保证安全性。申请连接的时候检测,如果空闲时间大于timeBetweenEvictionRunsMillis ,执行validationQuery 检测连接是否有效。 |
timeBetweenEvictionRunsMillis |
有两个含义: 1)Destroy线程会检测连接的间隔时间2)testWhileIdle 的判断依据,详细看testWhileIdle 属性的说明 |
|
numTestsPerEvictionRun |
不再使用,一个DruidDataSource 只支持一个EvictionRun |
|
minEvictableIdleTimeMillis |
||
connectionInitSqls |
物理连接初始化的时候执行的sql |
|
exceptionSorter |
根据dbType 自动识别 当数据库抛出一些不可恢复的异常时,抛弃连接 |
|
filters | 属性类型是字符串,通过别名的方式配置扩展插件,常用的插件有: 监控统计用的filter:stat 日志用的filter:log4j 防御sql 注入的filter:wall |
|
proxyFilters |
类型是List,如果同时配置了filters和proxyFilters ,是组合关系,并非替换关系 |
Dbutils
工具类库实现增删改查操作commons-dbutils
是 Apache 组织提供的一个开源 JDBC
工具类库,它是对JDBC
的简单封装,学习成本极低,并且使用dbutils
能极大简化jdbc
编码的工作量,同时也不会影响程序的性能。
QueryRunner:使用可插拔策略执行SQL查询以处理ResultSet。 此类是线程安全的;该类简单化了SQL查询,它与ResultSetHandler组合在一起使用可以完成大部分的数据库操作,能够大大减少编码量
QueryRunner实例对象.update(Connection conn, String sql, Object... params);//用来执行一个更新操作(插入,删除,更改);返回值为更改了几条数据,int类型
->conn为数据库连接,这里可以依靠德鲁伊连接池创建链接
->sql为sql语句,占位符同样为?
->params为填充占位符的参数,个数不确定
样例:插入数据(删改以此类推)
@Test
public void test(){
//插入数据
Connection conn = null;
try {
QueryRunner qr = new QueryRunner();
conn = JDBC_utils.get_Connection2();
String sql="insert into customers(name,email,birth)values (?,?,?)";
int insertCount = qr.update(conn, sql, "球球", "[email protected]", "1998-2-5");
System.out.println("添加了"+insertCount+"条数据");
} catch (Exception e) {
e.printStackTrace();
} finally {
JDBC_utils.close_resources(conn,null);
}
}
ResultSetHandler:该接口用于处理 java.sql.ResultSet,将数据按要求转换为另一种形式。ResultSetHandler 接口提供了一个单独的方法:Object handle (java.sql.ResultSet .rs)。
QueryRunner实例对象.query(Connection conn, String sql,xxx, Object... params);//查询具体的数据,返回类型为ResultSetHandler的具体实现类的具体参数类T
->conn为数据库连接,这里可以依靠德鲁伊连接池创建链接
->sql为sql语句,占位符同样为?
->xxx:ResultSetHandler的具体实现类
->params为填充占位符的参数,个数不确定
ResultSetHandler接口的主要实现类:
- ArrayHandler:把结果集中的第一行数据转成对象数组。
- ArrayListHandler:把结果集中的每一行数据都转成一个数组,再存放到List中。
- BeanHandler:将结果集中的第一行数据封装到一个对应的JavaBean实例中。封装表中的一条数据
Class BeanHandler
- BeanListHandler:将结果集中的每一行数据都封装到一个对应的JavaBean实例中,存放到List里。
- ColumnListHandler:将结果集中某一列的数据存放到List中。
- KeyedHandler(name):将结果集中的每一行数据都封装到一个Map里,再把这些map再存到一个map里,其key为指定的key。
- MapHandler:将结果集中的第一行数据封装到一个Map里,key是列名,value就是对应的值。
- MapListHandler:将结果集中的每一行数据都封装到一个Map里,然后再存放到List
- ScalarHandler:查询单个值对象
样例:用BeanHander
查询数据
@Test
public void test1(){
//查询一条
/*
BeanHander:是ReasultSetHander接口的实现类,用于封装表中的一条记录
*/
Connection conn = null;
try {
System.out.println("返回一条记录");
QueryRunner qr=new QueryRunner();
conn = JDBC_utils.get_Connection2();
String sql="select id,name,email,birth from customers where id=?;";
BeanHandler<Customer> hander=new BeanHandler<>(Customer.class);
Customer cust = qr.query(conn, sql, hander, 21);
System.out.println(cust.toString());
/*
查询多条记录
BeanListHander:ResultSetHander接口的实现类
*/
System.out.println("返回多条记录");
String sql1="select id,name,email,birth from customers where id>?;";
BeanListHandler<Customer> blh=new BeanListHandler<>(Customer.class);
List<Customer> custs = qr.query(conn, sql1, blh, 21);
for(Customer c:custs){
System.out.println(c);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
JDBC_utils.close_resources(conn,null);
}
}
样例:用MapHander
查询数据
@Test
public void test2(){
//查询一条
/*
MapHander:是ResultSetHander接口的实现类,对应表中的一条记录
将字段及相应字段的值作为map中的key和value
*/
Connection conn = null;
try {
System.out.println("返回一条记录");
QueryRunner qr=new QueryRunner();
conn = JDBC_utils.get_Connection2();
String sql="select id,name,email,birth from customers where id=?;";
MapHandler handler=new MapHandler();
Map<String, Object> map = qr.query(conn, sql, handler, 22);
System.out.println(map);
/*
返回多条记录
MapListHander:ResultSetHander接口的实现类
*/
System.out.println("返回多条记录");
String sql1="select id,name,email,birth from customers where id>?;";
MapListHandler mlh=new MapListHandler();
List<Map<String, Object>> list = qr.query(conn, sql1, mlh, 21);
list.forEach(System.out::println);
} catch (Exception e) {
e.printStackTrace();
} finally {
JDBC_utils.close_resources(conn,null);
}
}
ScalarHandler:查询单个值对象(单个值)
QueryRunner实例对象.query(连接名,sql语句,ScalarHandler实例对象,[参数]); //查询特殊字段
@Test
public void test3(){
Connection conn = null;
try {
QueryRunner qr=new QueryRunner();
ScalarHandler sh = new ScalarHandler();
conn = JDBC_utils.get_Connection2();
String sql="select max(birth) from customers";
//返回最大生日
String sql1="select count(*) from customers";
//返回总条数
Object value = qr.query(conn, sql, sh);
Object value1 = qr.query(conn, sql1, sh);
System.out.println(value);
System.out.println(value1);
} catch (Exception e) {
e.printStackTrace();
} finally {
JDBC_utils.close_resources(conn,null);
}
}
ResultSetHander
实现类(自己写一个)
匿名实现类:与匿名内部类相似
例子(用到了匿名实现类):
@Test
public void test5()throws Exception{
/*
自定义ResultSetHander实现类(自己写个)
*/
Connection conn = JDBC_utils.get_Connection2();
QueryRunner qr=new QueryRunner();
String sql="select id,name,birth,email from customers where id=?";
//匿名实现类
ResultSetHandler<Customer> rshc=new ResultSetHandler<Customer>() {
@Override
public Customer handle(ResultSet rs) throws SQLException {
if(rs.next()){
Customer c=new Customer();
ResultSetMetaData rsmd = rs.getMetaData();
for(int i=0;i<rsmd.getColumnCount();i++){
Object value = rs.getObject(i + 1);
String columnLabel = rsmd.getColumnLabel(i+1);
try {
Field filed = Customer.class.getDeclaredField(columnLabel);
filed.setAccessible(true);
filed.set(c,value);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
return c;
}
else{
return null;
}
}
};
Customer query = qr.query(conn, sql, rshc, 23);
System.out.println(query);
}
匿名实现类
静态代码块