Jdbc入门
Jdbc本质(一套接口)
1.jdbc 是什么?
Java DataBase Connectivity(Java语言连接数据库)
2.jdbc的本质是什么?
jdbc是sun 公司指定的一套
接口
。 接口都有调用者和实现者。
面向接口调用、面向接口写实现类,这都属于面向接口编程。
接i都有调用者和实现者。 面向接口调用、面向接口写实现类,这都属于面向接口编程。
思考:为什么sun公司要制定一套jdbc接口呢?
因为每一个数据库的实现原理都不一样。
Oracle有自己的实现原理。
MySql也有自己的实现原理。
MysqlServer也有自己的实现原理。
。。
每一个数据库都有自己独特的实现原理
3. JDBC体系结构
JDBC接口(API)包括两个层次︰
1.面向应用的API : JavaAPI,抽象接口,供应用程序开发人员使用(连接数据库,执行SQL语句,获得结果)。
2.面向数据库的API: Java Driver API,供开发商开发数据库驱动程序用。
JDBC是sun公司提供一套用于数据库操作的接口,java程序员只需要面向这套接口编程即可。
不同的数据库厂商,需要针对这套接口,提供不同实现。不同的实现的集合,即为不同数据库的驱动。
4.JDBC编程六步(需要背会)
第一步:注册驱动(作用:告诉Java程序,即将要连接的是哪个品牌的数据库)
第二步:获取连接(表示Java的进程和数据库进程之间的通道打开了,这属于进程之间的通信,重量级的,使用完之后一定要)第三步:获取数据库操作对象(专门执行sql语句的对象)
第四步:执行sql语句(DQLDML…)
第五步:处理查询结果集(只有当第四步执行的是select语句的时候,才有这第五步处理查询结果集。)
第六步:释放资源(使用完资源之后一定要关闭资源。Java和数据库属于进程间的通信,开启之后一定要关闭。)
5.获取数据库连接的方法
方法一:
@Test
public void testConnection1() throws SQLException{
Driver driver=new com.mysql.cj.jdbc.Driver();
//url:http://localhost:8080/gmall/keyboard.jpg
//jdbc:mysql协议
//localhost:ip地址
//3306:默认mysql的端口号
//mybatis:mybatis的数据库
//注册驱动:告诉Java程序,即将要连接的是哪个品牌的数据库
String url="jdbc:mysql://localhost:3306/mybatis";
//获取连接:表示java进程和数据库进程之间的通道打开了,这属于进程之间的通信
//将用户名和密码封装在Properties中
Properties info = new Properties();
info.setProperty("user","root");
info.setProperty("password","");
Connection conn=driver.connect(url,info);
System.out.println(conn);
}
方法二:
//方式二:对方式一的迭代
@Test
public void testConnection2() throws ClassNotFoundException, IllegalAccessException, InstantiationException, SQLException {
//获取Driver实现类对象,使用反射
Class<?> aClass = Class.forName("com.mysql.cj.jdbc.Driver");
Driver driver=(Driver)aClass.newInstance();
String url="jdbc:mysql://localhost:3306/mybatis";
Properties info = new Properties();
info.setProperty("user","root");
info.setProperty("password","");
Connection conn=driver.connect(url,info);
System.out.println(conn);
}
//方式三:使用DriverManage替换Driver
@Test
public void testConnection() throws ClassNotFoundException, IllegalAccessException, InstantiationException, SQLException {
//1.获取Driver的实现类对象
Class<?> clazz = Class.forName("com.mysql.cj.jdbc.Driver");
Driver driver=(Driver)clazz.newInstance();
//2.提供另外三个连接的基本信息
String url="jdbc:mysql://localhost:3306/mybatis";
String user="root";
String password="";
//注册驱动
DriverManager.registerDriver(driver);
//获取连接
Connection connection = DriverManager.getConnection(url, user, password);
System.out.println(connection);
}
//方法四:可以只是加载驱动,不用显示的注册驱动
@Test
public void testConnection4() throws ClassNotFoundException, IllegalAccessException, InstantiationException, SQLException {
//2.提供另外三个连接的基本信息
String url="jdbc:mysql://localhost:3306/mybatis";
String user="root";
String password="";
//1.获取Driver的实现类对象
Class.forName("com.mysql.cj.jdbc.Driver");
// Driver driver=(Driver)clazz.newInstance();
//注册驱动
// DriverManager.registerDriver(driver);
//为什么可以省略上述操作呢?
/*
* 在mysql的Driver实现类中,声明了如下操作
* static {
try {
DriverManager.registerDriver(new Driver());
} catch (SQLException var1) {
throw new RuntimeException("Can't register driver!");
}
}
*
*
* */
//获取连接
Connection connection = DriverManager.getConnection(url, user, password);
System.out.println(connection);
}
JAVA 笔记 ClassLoader.getResourceAsStream() 与 Class.getResourceAsStream()的区别?
复习反射的三种 类加载器
https://www.cnblogs.com/yjl49/archive/2012/08/08/2628502.html
Properties类总结https://blog.csdn.net/yelang0/article/details/76449165
方式五:(复习)
/方式五:将数据库连接需要的四个基本信息声明在配置文件中,通过读取配置文件的方式,获取连接
@Test
public void testConnection5() throws ClassNotFoundException, IllegalAccessException, InstantiationException, SQLException, IOException {
//1.读取配置文件的四个基本信息
InputStream is = ConnectionTest.class.getClassLoader().getResourceAsStream("jdbc.properties");
Properties pros = new Properties();
pros.load(is);
String user=pros.getProperty("user");
String password=pros.getProperty("password");
String url=pros.getProperty("url");
String driver=pros.getProperty("driver");
//2.加载驱动
Class.forName(driver);
//3.获取连接
Connection connection = DriverManager.getConnection(url, user, password);
System.out.println(connection);
}
如果出现空指针异常,有可能是properties的存放位置不对。
ser=root
password=
url=jdbc:mysql://localhost:3306/mybatis?characterEncoding=UTF-8
driver=com.mysql.cj.jdbc.Driver
导入driver驱动
注意:每新建一个moudle建立数据库连接的时候都要执行这三步
5.1精简版(复习)
1.获取数据库连接核心代码两步:
1.注册驱动 注意Class.forName().newInstance()的使用
//注册驱动
DriverManager.registerDriver(driver);
2.获取连接
//获取连接
Connection connection = DriverManager.getConnection(url, user, pwd);
if(connection!=null){
System.out.println("连接成功");
}
3.运行
import org.junit.Test;
import java.sql.Connection;
import java.sql.Driver;
import java.sql.DriverManager;
import java.sql.SQLException;
/**
* @description:使用DriverManager获取数据库连接
* @author: Ada
* @time: 2022/3/14
*/
public class MyConnectionTest {
@Test
public void test() throws Exception {
//com.mysql.cj.jdbc.Driver driver = new com.mysql.cj.jdbc.Driver();
Driver driver = (Driver)Class.forName("com.mysql.cj.jdbc.Driver").newInstance();
//注册驱动
DriverManager.registerDriver(driver);
String url="jdbc:mysql://localhost:3306/mybatis";
String user="root";
String pwd="";
//获取连接
Connection connection = DriverManager.getConnection(url, user, pwd);
if(connection!=null){
System.out.println("连接成功");
}
}
}
6.使用Statement的弊端
需要拼写sql语句,并且存在SQL注入的问题
三个判断,最后while 1=1是true.
//如歌避免sql注入,使用PrepareStatement(从Statement扩展而来)取代Statement
7.使用PrepareStatement实现增删改查
import org.junit.Test;
import java.io.IOException;
import java.io.InputStream;
import java.nio.channels.Channel;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.Properties;
/*
*
* 使用PreparedStatement来替换Statement,实现对数据表的增删改查操作
*
* */
public class PreparedStatementUpdateTest {
@Test
public void testConnection5() throws Exception {
PreparedStatement ps= null;
Connection conn=null;
try {
//1.读取配置文件的四个基本信息
InputStream is = ConnectionTest.class.getClassLoader().getResourceAsStream("jdbc.properties");
Properties pros = new Properties();
pros.load(is);
String user=pros.getProperty("user");
String password=pros.getProperty("password");
String url=pros.getProperty("url");
String driver=pros.getProperty("driver");
//2.加载驱动
Class.forName(driver);
//3.获取连接
conn = DriverManager.getConnection(url, user, password);
//4.预编译sql语句,返回PreparedStatement实例
String sql="insert into user(id,name,pwd)values(?,?,?)";
ps = conn.prepareStatement(sql);
//5.填充占位符
ps.setInt(1,4);
ps.setString(2,"王红");
ps.setString(3,"345678");
//6.执行sql
ps.execute();
} catch (Exception e) {
e.printStackTrace();
} finally{
//7.资源的关闭
try {
if(ps!=null)
ps.close();
} catch (SQLException e) {
e.printStackTrace();
}
try {
if(conn!=null)
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
补充知识点:idea设置类注释和方法注释
https://blog.csdn.net/weixin_39911998/article/details/114114030?
易错点:
1.如果出现乱码,在properties属性文件中的url后面添加
?characterEncoding=UTF-8设置一下字符集
实现通用的增删改查操作
为什么有些String需要加双引号而有些不需要
双引号表示字符串
比如
定义 int a(string s)
调用时可以 a(“asdas”)
“asdas”就表示一个字符串
当然也可以
string str=“asdasd”
a(str)这样使用
import org.junit.Test;
import java.io.IOException;
import java.io.InputStream;
import java.nio.channels.Channel;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.Properties;
/*
*
* 使用PreparedStatement来替换Statement,实现对数据表的增删改查操作
*
* */
public class PreparedStatementUpdateTest {
//修改customers表的一条记录
@Test
public void testUpdate() {
PreparedStatement ps= null;
Connection conn=null;
//1.获取数据库的连接
try {
conn = JDBCutils.getConnection();
//2.预编译sql语句,返回PreparedStatement的实例
String sql ="update user set name = ? where id = ? ";
ps = conn.prepareStatement(sql);
//3.填充占位符
ps.setString(1,"莫扎特");
ps.setInt(2,2);
//4.执行
ps.execute();
//5.资源的关闭
JDBCutils.closeResource(ps,conn);
} catch (Exception e) {
e.printStackTrace();
}finally {
try {
if(conn!=null)
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
try {
if(ps!=null)
ps.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
@Test
public void testConnection5() throws Exception {
PreparedStatement ps= null;
Connection conn=null;
try {
//1.读取配置文件的四个基本信息
InputStream is = ConnectionTest.class.getClassLoader().getResourceAsStream("jdbc.properties");
Properties pros = new Properties();
pros.load(is);
String user=pros.getProperty("user");
String password=pros.getProperty("password");
String url=pros.getProperty("url");
String driver=pros.getProperty("driver");
//2.加载驱动
Class.forName(driver);
//3.获取连接
conn = DriverManager.getConnection(url, user, password);
//4.预编译sql语句,返回PreparedStatement实例
String sql="insert into user(id,name,pwd)values(?,?,?)";
ps = conn.prepareStatement(sql);
//5.填充占位符
ps.setInt(1,4);
ps.setString(2,"王红");
ps.setString(3,"345678");
//6.执行sql
ps.execute();
} catch (Exception e) {
e.printStackTrace();
} finally{
//7.资源的关闭
try {
if(ps!=null)
ps.close();
} catch (SQLException e) {
e.printStackTrace();
}
try {
if(conn!=null)
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
实现通用的增删改查
public class PreparedStatementUpdateTest {
@Test
public void testCommonUpdate(){
String sql="delete from user where id=?";
update(sql,4);
}
//通用的增删改查操作
public void update(String sql,Object ...args){
Connection conn=null;
PreparedStatement ps=null;
//1.获取数据库连接
try {
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]);
}
//4.执行
ps.execute();
} catch (Exception e) {
e.printStackTrace();
} finally {
//关闭资源
JDBCutils.closeResource(ps,conn);
}
}
Java与SQL对应数据类型转换表
7.1查
getDeclaredFiled 仅能获取类本身的属性成员(包括私有、共有、保护) getField 仅能获取类(及其父类可以自己测试)
public属性成员
import com.sun.org.apache.regexp.internal.RE;
import javafx.beans.binding.ObjectBinding;
import org.junit.Test;
import java.io.ObjectInputStream;
import java.lang.reflect.Field;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
/**
* @description:z针对user表的查询操作
* @author: Ada
* @time: 2022/3/13
*/
public class userQuery {
/*
*
* **
* @description:针对user表的通用查询操作
* @author: Ada
* @time: 2022/3/13
*/
@Test
public void testQueryForCustomers() throws Exception {
String sql="select id,name,pwd from user where id=?";
Customer customer=queryForCustomers(sql,3);
System.out.println(customer);
}
public Customer queryForCustomers(String sql, Object...args) throws Exception {
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
ResultSetMetaData rsmd = rs.getMetaData();
//通过ResultSetMeteData获取结果集中的列数
int columnCount = rsmd.getColumnCount();
if(rs.next()){
Customer cust = new Customer();
for(int i=0;i<columnCount;i++){
Object columnvalue=rs.getObject(i+1);
//获取每个列的列名
String columnName=rsmd.getColumnName(i+1);
//给cust对象指定的columnName属性,赋值columValue,通过反射
Field field=Customer.class.getDeclaredField(columnName);
field.setAccessible(true);
field.set(cust,columnvalue);
}
return cust;
}
} catch (Exception e) {
e.printStackTrace();
} finally {
JDBCutils.closeResource(ps,conn,rs);
}
return null;
}
@Test
public void testQuery1() {
Connection conn = null;
PreparedStatement ps = null;
ResultSet resultSet = null;
try {
//1.获取数据库连接
conn = JDBCutils.getConnection();
//2.预编译sql语句,获取PreparedStatement对象
String sql="select id,name,pwd from user where id =?";
ps = conn.prepareStatement(sql);
ps.setObject(1,2);
//执行,并返回结果集
resultSet = ps.executeQuery();
//处理结果集
if(resultSet.next()){//.next()方法判断结果集的下一条是否有数据,如果有数据返回true并指针下移,如果无返回false;
//获取当前这条数据的各个字段值
int id = resultSet.getInt(1);
String name = resultSet.getString(2);
String pwd = resultSet.getString(3);
//方式一:
// System.out.println("id =" + id +",name = " + name + "pwd =" + pwd );
//方式二:
// Object[] data= new Object[]{id,name,pwd};
//方式三:将数据封装为一个对象(推荐)
Customer customer = new Customer(id, name, pwd);
System.out.println(customer);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
//关闭资源
JDBCutils.closeResource(ps,conn,resultSet);
}
}
}
7.2 查常见问题
针对表的字段名与类的属性名不相同的情况;
1.必须声明sql时,使用类的属性名来命名字段的别名。
2.使用ResultSetMetaData时,需要使用getColumnLabel() 来替换getColumnName()来获取别名。 说明:如果sql中没有给字段取别名,getColumnLabel()获取的就是列名。
有时候数据库表的结构会发生变化,出现数据不对应的问题。
可以通过起别名的方式解决
但还是会报错?
为什么?
改为获取列的别名。
7.3图解查询操作的流程
7.4用泛型实现任意表的查询
泛型复习
泛型方法声明
1.只能查询一条数据
public <T2> void printArray(T2[] arr){
for (int i = 0; i < arr.length; i++) {
System.out.println(arr[i]);
} }
import org.junit.Test;
import java.lang.reflect.Field;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
/**
* @description:使用PreparedStatement实现针对不同的通用的查询操作
* @author: Ada
* @time: 2022/3/14
*/
public class PreparedStatementQueryTest {
@Test
public void testGetInstance() throws Exception {
String sql="select id,name,pwd from user where id=?";
User user=getInstance(User.class,sql,2);
System.out.println(user);
}
public <T> T getInstance(Class<T> clazz,String sql, Object...args) throws Exception {
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
ResultSetMetaData rsmd = rs.getMetaData();
//通过ResultSetMeteData获取结果集中的列数
int columnCount = rsmd.getColumnCount();
if(rs.next()){
T t = clazz.newInstance();
for(int i=0;i<columnCount;i++){
Object columnvalue=rs.getObject(i+1);
//获取每个列的列名
String columnName=rsmd.getColumnName(i+1);
//给cust对象指定的columnName属性,赋值columValue,通过反射
Field field=clazz.getDeclaredField(columnName);
field.setAccessible(true);
field.set(t,columnvalue);
}
return t;
}
} catch (Exception e) {
e.printStackTrace();
} finally {
JDBCutils.closeResource(ps,conn);
}
return null;
}
}
2.可以查询多条数据
import org.junit.Test;
import java.lang.reflect.Field;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
/**
* @description:使用PreparedStatement实现针对不同的通用的查询操作
* @author: Ada
* @time: 2022/3/14
*/
public class PreparedStatementQueryTest {
@Test
public void testGetInstance() throws Exception {
String sql="select id,name,pwd from user where id < ?";
List<User> list = getForList(User.class, sql, 4);
Iterator<User> iterator = list.iterator();
while(iterator.hasNext()){
System.out.println(iterator.next());
}
}
public <T> List<T> getForList(Class<T> clazz, String sql, Object...args) throws Exception {
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
ResultSetMetaData rsmd = rs.getMetaData();
//通过ResultSetMeteData获取结果集中的列数
int columnCount = rsmd.getColumnCount();
//创建集合对象
ArrayList<T> list = new ArrayList<T>();
while (rs.next()){
T t = clazz.newInstance();
for(int i=0;i<columnCount;i++){
Object columnvalue=rs.getObject(i+1);
//获取每个列的列名
String columnName=rsmd.getColumnName(i+1);
//给cust对象指定的columnName属性,赋值columValue,通过反射
Field field=clazz.getDeclaredField(columnName);
field.setAccessible(true);
field.set(t,columnvalue);
}
list.add(t);
}
return list;
} catch (Exception e) {
e.printStackTrace();
} finally {
JDBCutils.closeResource(ps,conn);
}
return null;
}
}
7.5使用PreparedStatement解决SQL注入问题
@Test
public void testLogin() throws Exception {
Scanner scanner = new Scanner(System.in);
System.out.println("请输入用户名:");
String user= scanner.nextLine();
System.out.println("请输入密码:");
String password = scanner.nextLine();
//SELECT name,password from user where user ='1' or AND password = '=1 or '1' ='1'
String sql="SELECT name,password from user where user =? and password =? ";
User returnUser = getInstance(User.class, sql, user, password);
if(returnUser !=null){
System.out.println("登录成功");
}else {
System.out.println("用户名不存在或者密码错误");
}
使用PreparedStatement输入这句话就会显示登录失败,
成功防止sql注入。
7.6Statement和PreparedStatement的区别
除了解决Statement的拼串,sql问题之外,PreparedStatement还有那些好处呢?
1.PreparedStatement操作Blob的数据,而Statement做不到
2.PreparesStatemet可以实现更高校的批量操作。
8.向数据表中插入Blob类型数据
数据库表设计的有Blob类型的字段
import org.junit.Test;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
/**
* @description:
* @author: Ada
* @time: 2022/3/15
*/
public class BlobTest {
//向数据customer中插入Blob类型的字段
@Test
public void testInsert() throws SQLException, FileNotFoundException {
Connection conn= JDBCutils.getConnection();
System.out.println(conn);
String sql="insert into customers(name,email,birth,photo) values(?,?,?,?)";
PreparedStatement ps = conn.prepareStatement(sql);
ps.setObject(1,"章子怡");
ps.setObject(2,"zhang@qq.com");
ps.setObject(3,"1992-09-08");
FileInputStream is = new FileInputStream(new File("src/main/resources/A.jpg"));
ps.setBlob(4,is);
ps.execute();
JDBCutils.closeResource(ps,conn);
}
}
8.1从数据表中读取Blob类型的数据
import org.junit.Test;
import java.io.*;
import java.sql.*;
/**
* @description:
* @author: Ada
* @time: 2022/3/15
*/
public class BlobTest {
//向数据customer中插入Blob类型的字段
@Test
public void testInsert() throws SQLException, FileNotFoundException {
Connection conn= JDBCutils.getConnection();
System.out.println(conn);
String sql="insert into customers(name,email,birth,photo) values(?,?,?,?)";
PreparedStatement ps = conn.prepareStatement(sql);
ps.setObject(1,"章子怡");
ps.setObject(2,"zhang@qq.com");
ps.setObject(3,"1992-09-08");
FileInputStream is = new FileInputStream(new File("src/main/resources/A.jpg"));
ps.setBlob(4,is);
ps.execute();
JDBCutils.closeResource(ps,conn);
}
//查询数据表customer中Blob的字段
@Test
public void testQuery() throws Exception {
Connection conn = BJDBCutils.getConnection();
String sql="select id, name,email,birth,photo from customers where id=?";
PreparedStatement ps = conn.prepareStatement(sql);
ps.setInt(1,16);
ResultSet rs = ps.executeQuery();
if(rs.next()){
// 方式一:
//
// int anInt = rs.getInt(1);
// String string = rs.getString(2);
// String string1 = rs.getString(3);
// Date date = rs.getDate(4);
// Blob blob = rs.getBlob(5);
//方式二:
int id = rs.getInt("id");
String name = rs.getString("name");
String email= rs.getString("email");
Date birth = rs.getDate("birth");
Customers cust = new Customers(id, name, email, birth);
//将Blob类型的字段下载下来,以文件的方式保存到本地
Blob photo = rs.getBlob("photo");
InputStream is= photo.getBinaryStream();
FileOutputStream fos = new FileOutputStream("src/main/resources/zhangyuhao.jpg");
System.out.println(cust);
byte[] buffer = new byte[1024];
int len;
while((len=is.read(buffer))!=-1){
fos.write(buffer,0,len);
}
is.close();
fos.close();
BJDBCutils.closeResource(ps,conn,rs);
}
}
}
9.批量插入操作
9.1用PreparementStatement实现批量插入操作
import org.junit.Test;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
/**
* @description:使用PreparesStatement实现批量数据的操作
* @author: Ada
* @time: 2022/3/15
*
*
*
* update ,delete本身就具有批量操作的效果
* 此时的批量操作,主要指的是批量插入,使用PreparedStatement如何实现更高效的批量插入?
*
*
* 方式一:S使用Statement
* Connection conn=BBJDBCutils.getConnection();
* Statement st=conn.createStatement();
* for(int i=1;i<=2000;i++){
* String sql="insert into goods(name)values('name_"+i+"")";
* st.execute(sql);
* }
*
*/
public class InsertTest {
//用PreparedStatement实现批量操作
@Test
public void testInsert1() {
Connection conn=null;
PreparedStatement ps=null;
try {
long start = System.currentTimeMillis();
conn = BJDBCutils.getConnection();
String sql="insert into goods(name)values(?)";
ps = conn.prepareStatement(sql);
for(int i=1;i<=20000;i++){
ps.setString(1,"name_"+i);
ps.execute();
}
long end=System.currentTimeMillis();
System.out.println("花费时间为:"+(end-start));
} catch (SQLException e) {
e.printStackTrace();
} finally {
BJDBCutils.closeResource(ps,conn);
}
}
}
用truncate清空数据库
9.2用Batch提高效率
批量插入方式二:
//用addBathch(),excuteBatch(),clearBatch()实现批量插入提升效率
//mysql服务器默认是关闭批处理的,我们需要通过一个参数,让mysql开启批处理的支持。
// * ?rewriteBatchedStatements=true 写在配置文件的url后面
@Test
public void insert1(){
Connection conn=null;
PreparedStatement ps=null;
try {
long start = System.currentTimeMillis();
conn = BJDBCutils.getConnection();
String sql="insert into goods(name)values(?)";
ps = conn.prepareStatement(sql);
for(int i=1;i<=20000;i++){
ps.setString(1,"name_"+i);
//1.赞sql
ps.addBatch();
if(i%500==0){
//2.执行batch
ps.executeBatch();
//3.清空batch
ps.clearBatch();
}
}
long end=System.currentTimeMillis();
System.out.println("Batch花费时间为:"+(end-start));
} catch (SQLException e) {
e.printStackTrace();
} finally {
BJDBCutils.closeResource(ps,conn);
}
}
9.3setAutoCommit提高效率(最高效)
@Test
public void insert1(){
Connection conn=null;
PreparedStatement ps=null;
try {
long start = System.currentTimeMillis();
conn = BJDBCutils.getConnection();
conn.setAutoCommit(false);
String sql="insert into goods(name)values(?)";
ps = conn.prepareStatement(sql);
for(int i=1;i<=20000;i++){
ps.setString(1,"name_"+i);
//1.赞sql
ps.addBatch();
if(i%500==0){
//2.执行batch
ps.executeBatch();
//3.清空batch
ps.clearBatch();
}
}
conn.commit();
long end=System.currentTimeMillis();
System.out.println("Batch花费时间为:"+(end-start));
} catch (SQLException e) {
e.printStackTrace();
} finally {
BJDBCutils.closeResource(ps,conn);
}
}
数据库事物的引入
1.事务处理(事务操作):保证所有事务都作为一个工作单元来执行,即使出现了故障,都不能改变这种执行方式。当在一个事务中执行多个操作时,要么所有的事务都被提交(commit),那么这些修改就永久地保存下来;要么数据库管理系统将放弃所作的所有修改,整个事务回滚(rollback)**到最初状态。
2.数据一旦提交,就不可回滚。(回滚是回滚到最近一个提交之前)
3.
DDL操作一旦执行,都会自动提交
DML默认情况下一旦执行,就会自动提交
我们可以通过set autocomit = false/0 的方式取消DML的自动提交
默认在关闭连接时,会自动地提交数据
总和应该是2000现在是1900.
1.考虑事务以后的代码实现
//考虑数据库事务后的转账操作
@Test
public void testUpdateWithTx(){
Connection conn=null;
try {
conn= BJDBCutils.getConnection();
System.out.println(conn.getAutoCommit());//true默认情况下是true
//1.取消数据的自动提交
conn.setAutoCommit(false);
String sql1="update user_table set balance = balance-100 where user =?";
update1(conn,sql1,"AA");
System.out.println(10/0);
String sql2="update user_table set balance = balance+100 where user =?";
update1(conn,sql2,"BB");
System.out.println("转账成功");
//2.提交数据
conn.commit();
} catch (Exception e) {
e.printStackTrace();
//3.回滚数据
try {
conn.rollback();
System.out.println("转账失败");
} catch (SQLException e1) {
e1.printStackTrace();
}
} finally {
BJDBCutils.closeResource(null,conn);
}
}
public int update1(Connection conn, String sql,Object ...args){
PreparedStatement ps=null;
//1.获取数据库连接
try {
//2.预编译sql语句,返回PreparedStatement的实例
ps = conn.prepareStatement(sql);
//3.填充占位符
for(int i=0;i<args.length;i++){
ps.setObject(i+1,args[i]);
}
//4.执行
return ps.executeUpdate();
} catch (Exception e) {
e.printStackTrace();
} finally {
//关闭资源
BJDBCutils.closeResource(ps,null);
}
return 0;
}
2.包的使用
Dao及实现类
package com.ada.dao;
import com.ada.util.BJDBCutils;
import java.lang.reflect.Field;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;
/**
* @description:
* @author: Ada
* @time: 2022/3/16
* 封装了针对于数据表的通用的操作
*/
public class BaseDao {
//通用的增删改
public int update1(Connection conn, String sql, Object ...args){
PreparedStatement ps=null;
//1.获取数据库连接
try {
//2.预编译sql语句,返回PreparedStatement的实例
ps = conn.prepareStatement(sql);
//3.填充占位符
for(int i=0;i<args.length;i++){
ps.setObject(i+1,args[i]);
}
//4.执行
return ps.executeUpdate();
} catch (Exception e) {
e.printStackTrace();
} finally {
//关闭资源
BJDBCutils.closeResource(ps,null);
}
return 0;
}
//通用的查询操作,用于返回数据表的一条记录(version2.0,考虑上事务)
public <T> T getInstance(Connection conn,Class<T> clazz, 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();
//获取结果集的元数据:ResultSetMetaData
ResultSetMetaData rsmd = rs.getMetaData();
//通过ResultSetMeteData获取结果集中的列数
int columnCount = rsmd.getColumnCount();
//创建集合对象
ArrayList<T> list = new ArrayList<T>();
if(rs.next()){
T t = clazz.newInstance();
for(int i=0;i<columnCount;i++){
Object columnvalue=rs.getObject(i+1);
//获取每个列的列名
String columnName=rsmd.getColumnName(i+1);
//给cust对象指定的columnName属性,赋值columValue,通过反射
Field field=clazz.getDeclaredField(columnName);
field.setAccessible(true);
field.set(t,columnvalue);
}
return t;
}
} catch (Exception e) {
e.printStackTrace();
} finally {
BJDBCutils.closeResource(ps,null);
}
return null;
}
//返回多个对象的
public <T> List<T> getForList(Connection conn, Class<T> clazz, 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();
//获取结果集的元数据:ResultSetMetaData
ResultSetMetaData rsmd = rs.getMetaData();
//通过ResultSetMeteData获取结果集中的列数
int columnCount = rsmd.getColumnCount();
//创建集合对象
ArrayList<T> list = new ArrayList<T>();
while (rs.next()){
T t = clazz.newInstance();
for(int i=0;i<columnCount;i++){
Object columnvalue=rs.getObject(i+1);
//获取每个列的列名
String columnName=rsmd.getColumnName(i+1);
//给cust对象指定的columnName属性,赋值columValue,通过反射
Field field=clazz.getDeclaredField(columnName);
field.setAccessible(true);
field.set(t,columnvalue);
}
list.add(t);
}
return list;
} catch (Exception e) {
e.printStackTrace();
} finally {
BJDBCutils.closeResource(ps,null);
}
return null;
}
//用于查询特殊值的方法
public <E> E getValue(Connection conn,String sql,Object...args){
PreparedStatement ps=null;
ResultSet rs=null;
try {
ps = conn.prepareStatement(sql);
for(int i=0;i<args.length;i++){
ps.setObject(i+1,args[i]);
}
rs = ps.executeQuery();
if(rs.next()){
return (E)rs.getObject(1);
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
BJDBCutils.closeResource(ps,null,rs);
}
return null;
}
}
package com.ada.dao;
import com.ada.bean.Customers;
import java.sql.Connection;
import java.util.Date;
import java.util.List;
/**
* @description:
* @author: Ada
* @time: 2022/3/17
*
* 此接口用于规范对于customers表的常规操作
*/
public interface CustomerDao {
/*
* 将cust对象添加到数据库中
* */
void insert(Connection conn,Customers cust);
/*
针对指定的id,删除表中的一条记录
* */
void deleteById(Connection conn,int id);
void updateById(Connection conn,Customers cust);
Customers getCustomerById(Connection conn,int id);
/*
* 查询表中的所有记录
* */
List<Customers> getAll(Connection conn);
//返回数据表中数据的条目数
Long getCount(Connection conn);
//返回数据表中最大的生日
Date getMaxBirth(Connection conn);
}
package com.ada.dao;
import com.ada.bean.Customers;
import java.sql.Connection;
import java.util.Date;
import java.util.List;
/**
* @description:
* @author: Ada
* @time: 2022/3/17
*/
public class CustomerDAOImp1 extends BaseDao implements CustomerDao {
@Override
public void insert(Connection conn, Customers cust) {
String sql="insert into customers(name,email,birth) values(?,?,?)";
update1(conn,sql,cust.getName(),cust.getEmail(),cust.getBirth());
}
@Override
public void deleteById(Connection conn, int id) {
String sql="delete from customers where id= ?";
update1(conn,sql, id);
}
@Override
public void updateById(Connection conn, Customers cust) {
String sql="update customers set name=?,email=?,birth=? where id=?";
update1(conn,sql,cust.getName(),cust.getEmail(),cust.getBirth(),cust.getId());
}
@Override
public Customers getCustomerById(Connection conn, int id) {
String sql="select id,name,email,birth from customers where id =?";
Customers customer = getInstance(conn, Customers.class, sql, id);
return customer;
}
@Override
public List<Customers> getAll(Connection conn) {
String sql="select id,name,email,birth from customers";
List<Customers> customersList = getForList(conn, Customers.class, sql);
return customersList;
}
@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);
}
}
3.cp30数据库连接数据池的两种实现方式
做之前要
1.添加驱动
2.把他放到路径里面
File–>Project Structure–>Modules–>点加号选择驱动的路径即可
package com.ada.connection;
import com.mchange.v2.c3p0.ComboPooledDataSource;
import com.mchange.v2.c3p0.DataSources;
import org.junit.Test;
import java.beans.PropertyVetoException;
import java.sql.Connection;
import java.sql.SQLException;
/**
* @description:
* @author: Ada
* @time: 2022/3/17
*/
public class C3P0Test {
@Test
public void testGetConnection() throws Exception {
//获取C3P0数据库连接池
ComboPooledDataSource cpds = new ComboPooledDataSource();
cpds.setDriverClass( "com.mysql.cj.jdbc.Driver" ); //loads the jdbc driver
cpds.setJdbcUrl( "jdbc:mysql://localhost:3306/test" );
cpds.setUser("root");
cpds.setPassword("");
//通过设置相关的参数,对数据库连接池进行管理
//设置初始时数据库连接池的连接数
cpds.setInitialPoolSize(10);
Connection conn = cpds.getConnection();
System.out.println(conn);
//销毁c3p0的连接池
// DataSources.destroy(cpds);
}
//方式二:使用配置文件
@Test
public void testConnection() throws SQLException {
ComboPooledDataSource cpds = new ComboPooledDataSource("hellc3p0");
Connection conn = cpds.getConnection();
System.out.println(conn);
}
}
配置文件放这里
user=root
password=
url=jdbc:mysql://localhost:3306/test?characterEncoding=UTF-8&rewriteBatchedStatements=true
driver=com.mysql.cj.jdbc.Driver
4.DBCP数据库连接池的两种实现方式
package com.ada.connection;
import com.mchange.v2.c3p0.DataSources;
import org.apache.commons.dbcp.BasicDataSource;
import org.apache.commons.dbcp.BasicDataSourceFactory;
import org.junit.Test;
import javax.activation.DataSource;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Properties;
/**
* @description:
* 测试DBCP的数据库连接池技术
* @author: Ada
* @time: 2022/3/17
*/
public class DBCPTest {
@Test
public void testGetConnection() throws SQLException {
//创建了DBCP的数据库连接池
BasicDataSource source= new BasicDataSource();
//设置基本信息
source.setDriverClassName("com.mysql.jdbc.Driver");
source.setUrl("jdbc:mysql:///test");
source.setUsername("root");
source.setPassword("");
//还可以设置其他涉及数据库连接池管理的相关属性
source.setInitialSize(10);
source.setMaxActive(10);
Connection conn = source.getConnection();
System.out.println(conn);
}
//方式二:使用配置文件
@Test
public void test1GetConnection() throws Exception {
Properties pros = new Properties();
//方式一:
// InputStream is = ClassLoader.getSystemClassLoader().getResourceAsStream("dbcp.properties");
FileInputStream is = new FileInputStream(new File("src/main/resources/dbcp.properties"));
pros.load(is);
javax.sql.DataSource source = BasicDataSourceFactory.createDataSource(pros);
Connection conn = source.getConnection();
System.out.println(conn);
}
}
driverClassName=com.mysql.cj.jdbc.Driver
url=jdbc:mysql:///test
username=root
password=
initialSize=10
优化后的代码如下:不能每次都新建一个连接池,所以放到静态代码块里面,每次只执行一次。
package com.ada.connection;
import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.pool.DruidDataSourceFactory;
import com.mchange.v2.c3p0.DataSources;
import org.junit.Test;
import javax.sql.DataSource;
import javax.xml.crypto.Data;
import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.util.Properties;
/**
* @description:
* @author: Ada使用Druid数据库连接池技术
* @time: 2022/3/17
*/
public class DruidTest {
private static DataSource source;
static{
try {
Properties pros = new Properties();
InputStream is = ClassLoader.getSystemClassLoader().getResourceAsStream("druid.properties");
pros.load(is);
source = DruidDataSourceFactory.createDataSource(pros);
} catch (Exception e) {
e.printStackTrace();
}
}
@Test
public void getConnection() throws Exception {
Connection conn = source.getConnection();
System.out.println(conn);
}
}
5.Druid连接数据库的实现
package com.ada.connection;
import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.pool.DruidDataSourceFactory;
import com.mchange.v2.c3p0.DataSources;
import org.junit.Test;
import javax.sql.DataSource;
import javax.xml.crypto.Data;
import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.util.Properties;
/**
* @description:
* @author: Ada使用Druid数据库连接池技术
* @time: 2022/3/17
*/
public class DruidTest {
private static DataSource source;
static{
try {
Properties pros = new Properties();
InputStream is = ClassLoader.getSystemClassLoader().getResourceAsStream("druid.properties");
pros.load(is);
source = DruidDataSourceFactory.createDataSource(pros);
} catch (Exception e) {
e.printStackTrace();
}
}
@Test
public void getConnection() throws Exception {
Connection conn = source.getConnection();
System.out.println(conn);
}
}
url=jdbc:mysql:///test
username=root
password=
driverClassName=com.mysql.cj.jdbc.Driver
initialSize=10
maxActive=10