什么是JDBC?
JDBC(Java Database Connectivity)是一个**独立于特定数据库管理系统、通用的SQL数据库存取和操作的公共接口**(一组API),定义了用来访问数据库的标准Java类库,(**java.sql,javax.sql**)使用这些类库可以以一种**标准**的方法、方便地访问数据库资源。
如何使用JDBC?
大致分为六大步,分别是:
①加载驱动
②和数据库建立链接
③通过连接创建一个SQL命令发送器Statement(更好用的避免SQL注入的现在使用prepareStatement)
④使用SQL命令发送器向数据库发送SQL命令。
⑤处理结果
⑥关闭资源
接下来根据这六步分别作出阐述和拓展
一、加载驱动
原始的比较机械的写法如下:
@Test
public void testConnection1() {
try {
//1.提供java.sql.Driver接口实现类的对象
Driver driver = null;
driver = new com.mysql.jdbc.Driver();
//2.提供url,指明具体操作的数据
String url = "jdbc:mysql://localhost:3306/test";
//3.提供Properties的对象,指明用户名和密码
Properties info = new Properties();
info.setProperty("user", "root");
info.setProperty("password", "abc123");
//4.调用driver的connect(),获取连接
Connection conn = driver.connect(url, info);
System.out.println(conn);
} catch (SQLException e) {
e.printStackTrace();
}
}
但现在一般使用将数据库需要的基本信息声明在配置文件中通过读取配置文件的方式获取连接。
首先是手动创建Properties文件
文件内容下;
#配置信息
user=root
password=你的密码
url=jdbc:mysql://localhost:3306/数据库名称?useUnicode=true&characterEncoding=UTF-8&rewriteBatchedStatements=true
driverClass=com.mysql.jdbc.Driver
二、连接数据库
然后是加载文件内容完成连接驱动:
/**
* 使用德鲁伊数据库连接驱动
*/
private static DataSource ds;
static {
try {
Properties pro = new Properties();
pro.load(TestDruid.class.getClassLoader().getResourceAsStream("druid.properties"));
ds = DruidDataSourceFactory.createDataSource(pro);
} catch (Exception e) {
e.printStackTrace();
}
}
public static Connection getConn1() throws SQLException {
Connection conn = ds.getConnection();
return conn;
}
//获取数据库连接
注:将创建对象的语句封装成一个静态代码块是一种节约资源的做法,如果写在方法内会多次创建浪费资源。
三、通过连接创建一个SQL命令发送器Statement(更好用的避免SQL注入的现在使用prepareStatement)
通用的增删改和查询方法为例:
/**
* 通用增删改
* @param sql
* @param args
* @throws SQLException
* @throws ClassNotFoundException
*/
public static void update(String sql, Object... args) {
Connection conn = null;
PreparedStatement pre = null;
try {
conn = JDBCUtils.getConn();
pre = conn.prepareStatement(sql);
for (int i = 0; i < args.length; i++) {
pre.setObject(i + 1, args[i]);
}
pre.execute();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
}finally {
JDBCUtils.closeResource(conn, pre);
}
}
pre.excute;表示执行,但查询的执行语句和增删改有所不同:
/**
* 查询
* @param sql
* @param args
* @throws SQLException
* @throws ClassNotFoundException
*/
public static List<SelectTools> select1(String sql, Object... args) {
Connection conn = null;
PreparedStatement pre = null;
ResultSet resultSet=null;
try {
conn = JDBCUtils.getConn1();
pre = conn.prepareStatement(sql);
for (int i = 0; i < args.length; i++){
pre.setObject(i +1,args[i]);
}//当存在可变参数时,args相当于一个数组,
// 底层系统会先判断它的长度然后创建一个同等长度的数组,之后获取参数就遍历一下数组即可。
resultSet =pre.executeQuery();
int columnCount=rsmd.getColumnCount();
List<SelectTools> list = new ArrayList<>();
while (resultSet.next()){
SelectTools se=new SelectTools();
for (int i = 0; i <columnCount; i++) {
Object columnValue= resultSet.getObject(i+1);
String columnName=rsmd.getColumnLabel(i+1);
Field file= SelectTools.class.getDeclaredField(columnName);
file.setAccessible(true);
file.set(se,columnValue);
}
list.add(se);
}
return list;
} catch (SQLException e) {
e.printStackTrace();
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} finally {
JDBCUtils.closeResource1(conn,pre,resultSet);
}
return null;
}
执行块与增删改有区别:封装为结果集
?resultSet =pre.executeQuery();
处理结果集
if(resultSet.next()){
//判断结果集的下一条是否有数据,如果有就返回true,并且指针下移,反返回false,指针不下移。
//获取各个字段值
int id=resultSet.getInt(1);
String name=resultSet.getString(2);
..........以此类推
}
四、使用SQL命令发送器向数据库发送SQL命令。
一般将SQL写在执行类(测试类):
事先准备:提前写好一个声明字段属性的工具类,属性和增删改查所要用的字段或者别名保持一致。
public class SelectTools {
private String name;
private String com;
private int count;
@Override
public String toString() {
return "SelectTools{" +
"name='" + name + '\'' +
", com='" + com + '\'' +
", count=" + count +
'}';
}
public int getCount() {
return count;
}
public void setCount(int count) {
this.count = count;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getCom() {
return com;
}
public void setCom(String com) {
this.com = com;
}
public SelectTools() {
}
}
然后在执行类(测试类)里面定义和写入SQL语句:
public class QueryRunnerTest {
@Test
public void QueryRunnerTest() throws SQLException {
Connection conn= null;
try {
conn = JDBCUtils.getConn1();
QueryRunner runner=new QueryRunner();
String sql="SELECT p.p_name `name`,COUNT(*) `count`\n" +
"FROM sales s,deal d,production p\n" +
"WHERE s.s_no=d.s_no AND p.p_no=d.p_no\n" +
"GROUP BY d.p_no";
MapListHandler handler=new MapListHandler();
List<Map<String,Object>> wan = runner.query(conn,sql,handler);
wan.forEach(System.out::println);}
}
}
由于没有关闭资源的操作,执行并不是很快。
上文查询是通过德鲁伊数据库连接驱动的查询并使用了其中的Druid的jar包中的封装的查询方法。
五、处理结果
ResultSet类的作用
ResultSet(结果集)是数据库结果集的数据表,通常通过执行查询数据库的语句生成
一个ResultSet对象对应着一个由查询语句返回的一个表这个表中包含所有的查询结果。可以说结果集是一个存储查询结果的对象,但是结果集并不仅仅具有存储的功能,他同时还具有操纵数据的功,可能完成对数据的更新等。
实际上,我们就可以将一个ResultSet对象看成一个表。对ResultSet对象的处理必须逐行进行,而对每一行中的各个列,可以按任何顺序进行处理。
//处理结果集
try {
conn = JDBCUtils.getConn1();
pre = conn.prepareStatement(sql);
for (int i = 0; i < args.length; i++){
pre.setObject(i +1,args[i]);
}//当存在可变参数时,args相当于一个数组,
// 底层系统会先判断它的长度然后创建一个同等长度的数组,之后获取参数就遍历一下数组即可。
resultSet =pre.executeQuery();
int columnCount=rsmd.getColumnCount();
List<SelectTools> list = new ArrayList<>();
while (resultSet.next()){
SelectTools se=new SelectTools();
for (int i = 0; i <columnCount; i++) {
Object columnValue= resultSet.getObject(i+1);
String columnName=rsmd.getColumnLabel(i+1);
Field file= SelectTools.class.getDeclaredField(columnName);
file.setAccessible(true);
file.set(se,columnValue);
}
list.add(se);
}
return list;
} catch (SQLException e) {
e.printStackTrace();
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} finally {
JDBCUtils.closeResource1(conn,pre,resultSet);
}
return null;
六、关闭资源
关闭资源基本是就是做一个if非空判断,然后根据判断结果关闭资源。
/**针对查询的
* 关闭资源(包含结果集)
* @param conn
* @param pre
*/
public static void closeResource1(Connection conn, PreparedStatement pre, ResultSet resultSet){
//最简单的写法直接调用DbUtils里面的方法,其底层和正常写法一致:
DbUtils.closeQuietly(conn);
DbUtils.closeQuietly(pre);
DbUtils.closeQuietly(resultSet);
//正常写法:
// try{
// if (pre != null) {
// pre.close();
// }
// } catch (SQLException e) {
// e.printStackTrace();
// }
// try {
// if (pre != null) {
// conn.close();
// }
// } catch (SQLException e) {
// e.printStackTrace();
// }
// try {
// if (resultSet != null) {
// conn.close();
// }
// } catch (SQLException e) {
// e.printStackTrace();
// }
}
针对Blob类型字段的操作:
BLob字段类型
类型 | 字节 | TinyBlob | 255B | Blob | 65Kb | LongBLOb | 4G | MediumBlob | 16M |
注:如果指定了Blob类型以后还报错,找my.ini文件加上
max_allowed_packed=16M?? 然后重启mysql服务。
// 向数据表中插入大数据类型
//获取连接
Connection conn = JDBCUtils.getConnection();
String sql = "insert into customers(name,email,birth,photo)values(?,?,?,?)";
PreparedStatement ps = conn.prepareStatement(sql);
// 填充占位符
ps.setString(1, "徐海强");
ps.setString(2, "xhq@126.com");
ps.setDate(3, new Date(new java.util.Date().getTime()));
// 操作Blob类型的变量
FileInputStream fis = new FileInputStream("xhq.png");
ps.setBlob(4, fis);
//执行
ps.execute();
fis.close();
JDBCUtils.closeResource(conn, ps);
//从数据表中读取大数据类型
//……
String sql = "SELECT id, name, email, birth, photo FROM customer WHERE id = ?";
conn = getConnection();
ps = conn.prepareStatement(sql);
ps.setInt(1, 8);
rs = ps.executeQuery();
if(rs.next()){
Integer id = rs.getInt(1);
String name = rs.getString(2);
String email = rs.getString(3);
Date birth = rs.getDate(4);
Customer cust = new Customer(id, name, email, birth);
System.out.println(cust);
//读取Blob类型的字段
Blob photo = rs.getBlob(5);
InputStream is = photo.getBinaryStream();
OutputStream os = new FileOutputStream("c.jpg");
byte [] buffer = new byte[1024];
int len = 0;
while((len = is.read(buffer)) != -1){
os.write(buffer, 0, len);
}
JDBCUtils.closeResource(conn, ps, rs);
if(is != null){
is.close();
}
if(os != null){
os.close();
}
}
批量操作,根据事务的特性可以批量操作:
举例:向数据表中插入20000条数据
/*
*
* 使用Connection 的 setAutoCommit(false) / commit()
*/
@Test
public void testInsert2() throws Exception{
long start = System.currentTimeMillis();
Connection conn = JDBCUtils.getConnection();
//1.设置为不自动提交数据
conn.setAutoCommit(false);
String sql = "insert into goods(name)values(?)";
PreparedStatement ps = conn.prepareStatement(sql);
for(int i = 1;i <= 1000000;i++){
ps.setString(1, "name_" + i);
//1.“攒”sql
ps.addBatch();
if(i % 500 == 0){
//2.执行
ps.executeBatch();
//3.清空
ps.clearBatch();
}
}
//2.提交数据
conn.commit();
long end = System.currentTimeMillis();
System.out.println("花费的时间为:" + (end - start));//1000000条:4978
JDBCUtils.closeResource(conn, ps);
}
|