JDBC六个基础步骤
注册驱动 Class.forName
- 将驱动类的 class 文件装载到内存中,并且形成一个描述此驱动类结构的 Class 类实例,并且初始化此驱动类,这样 jvm 就可以使用它了,这就是 Class.forName() 方法的含义。
- 加载过程中如果类中有静态初始化器,JVM 必然会执行该类的静态代码 段。
public class MyJDBCDriver implements Driver {
static {
DriverManager.registerDriver(new MyJDBCDriver());
}
}
- 任何一个 JDBC Driver 的 Driver 类的代码都必须类似如下,所以加载数据库驱动包的时候不需要调用 newInstance() 方法。
Class.forName("com.mysql.cj.jdbc.Driver");//MYSQL8.0以上
Class.forName("com.mysql.jdbc.Driver");//8.0以下
创建连接 getConnection
conn = DriverManager .getConnection("jdbc:mysql://localhost:3306/database1?serverTimezone=UTC","root","123456");
//数据库url--注意设置时区否则会报错,用户名,密码
conn = DriverManager .getConnection("jdbc:mysql://localhost:3306/database1?serverTimezone=UTC&root&123456");
//可以写成拼接样式
创建传输器 createStatement
stat = DriverManager .getConnection("jdbc:mysql://localhost:3306/tedudemo?serverTimezone=UTC&root&123456").createStatement();
stat = conn.createStatement();
传输SQL executeQuery/executeUpdate
- executeQuery 仅查询
- executeUpdate 执行增删改
rs = stat.executeQuery("SELECT * FROM employ");//仅查询
int count = stat.executeUpdate("UPDATE employ SET name = 'lvbu' ");//执行增删改,返回值是影响的行数
处理结果 ResultSet
- ResultSet表示数据库结果集的数据表,通常通过执行查询数据库的语句生成。
- ResultSet 对象具有指向其当前数据行的光标。最初,光标被置于第一行之前。next 方法将光标移动到下一行
- 该方法在 ResultSet 对象没有下一行时返回 false,可以在 while 循环中使用它来迭代结果集。
几个方法
- get方法
- int getInt(int columnIndex) 以 Java 编程语言中 int 的形式获取此 ResultSet 对象的当前行中指定列的值。
- int getInt(String columnLabel) 以 Java 编程语言中 int 的形式获取此 ResultSet 对象的当前行中指定列的值。
- Array getArray(int columnIndex) / Array getArray(String columnLabel)
- 等等
- 判断光标位置
- boolean isAfterLast() 获取光标是否位于此 ResultSet 对象的最后一行之后。
- boolean isBeforeFirst() 获取光标是否位于此 ResultSet 对象的第一行之前。
- boolean isFirst() 获取光标是否位于此 ResultSet 对象的第一行。
- boolean isLast() 获取光标是否位于此 ResultSet 对象的最后一行。
- 移动光标
- boolean next()
- boolean previous()
while (rs.next()) {
int id = rs.getInt("age");
System.out.println("age" + id);
}
关闭资源
- 后创建的先关闭
rs.close();
stat.close();
conn.close();
JDBC流程改进
- 设置properties文件,通过读取配置文件的形式可以实现代码的深层次解耦
- 封装流程,简化开发
Properties类
- Properties类表示一组持久的属性,可以保存到流中或从流中加载,属性列表中的每个键及其对应的值都是一个字符串。
- 这个类是线程安全的:多个线程可以共享一个Properties对象,而不需要外部同步。
getProperty(String key)//使用此属性列表中指定的键搜索属性
public void load(InputStream inStream)//从输入字节流读取属性列表(键和元素对)。
public void load(Reader reader)
public void store(OutputStream out, String comments)//将此属性列表(键和元素对)写入此Properties表中
public void store(Writer writer,String comments)
public void loadFromXML(InputStream in)//将指定输入流中的XML文档表示的所有属性加载到此属性表中
public void storeToXML(OutputStream os,String comment)
创建配置文件并加载
driver=com.mysql.cj.jdbc.Driver
user=root
password=123456
url=jdbc:mysql://localhost:3306/database1?serverTimezone=UTC
- 加载器,需要提供流参数
- 创建流,需要提供文件对象参数
- 创建文件对象,需要提供路径参数
- 获取文件路径(字符串类)
- 创建文件对象,需要提供路径参数
- 创建流,需要提供文件对象参数
- 所以实际顺序为 获取文件路径→创建文件→创建流→加载器加载
public class PropertiesDemo {
public static void main(String[] args) throws FileNotFoundException, IOException {
Properties properties = new Properties();
//PropertiesDemo.class获取类的字节码对象 getResource/getClassLoader/需要由字节码对象调用
//getClassLoader()获取类加载器
//getResource("src目录中的任意文件名")获取配置文件路径
//getPath()将url类型变为String类型
URL url = PropertiesDemo.class.getClassLoader().getResource("conf.properties");
String str = url.getPath();
System.out.println(url);
System.out.println(str);
//创建文件、流、并加载
properties.load(new FileInputStream(new File(str)));
properties.list(System.out);
System.out.println(properties.getProperty("driver"));
}
}
.class.getResource() 和 .class.getClassLoader().getResource ()
- .class.getResource(" "); 以当前包路径为开始
- .class.getClassLoader().getResource (" ");返回的是classpath的位置–bin 是不是可以说src路径下就相当于bin路径下?
package logindemo;
import java.net.URL;
public class PathDemo {
public static void main(String[] args) {
URL u1 = PropertiesDemo.class.getResource("");
URL u2 = PropertiesDemo.class.getClassLoader().getResource("");
System.out.println(u1);
System.out.println(u2);
URL u1p = PropertiesDemo.class.getResource("conf.properties");
URL u2p = PropertiesDemo.class.getClassLoader().getResource("conf.properties");
System.out.println(u1p);
System.out.println(u2p);
}
}
路径内有无 /
可以看到,路径名内有"/"返回的是classpath。
package logindemo;
import java.net.URL;
public class PathDemo {
public static void main(String[] args) {
URL u1 = PropertiesDemo.class.getResource("/");
URL u2 = PropertiesDemo.class.getClassLoader().getResource("");
System.out.println(u1);
System.out.println(u2);
URL u1p = PropertiesDemo.class.getResource("/conf.properties");
URL u2p = PropertiesDemo.class.getClassLoader().getResource("conf.properties");
System.out.println(u1p);
System.out.println(u2p);
}
}
封装流程
创建连接
public static Connection getConnection() throws Exception{
//读取prop对象身上的参数
Class.forName(prop.getProperty("driver"));
//创建数据库连接
return DriverManager.getConnection(prop.getProperty("url"),
prop.getProperty("user"), prop.getProperty("password"));
}
关闭资源
public static void close(Connection conn,Statement stat,ResultSet rs){
if(rs != null){
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
throw new RuntimeException(e);
}finally{
rs = null;
}
}
if(stat !=null){
try {
stat.close();
} catch (SQLException e) {
e.printStackTrace();
throw new RuntimeException(e);
}finally{
stat = null;
}
}
if(conn != null){
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
throw new RuntimeException(e);
}finally{
conn = null;
}
}
}
完整代码
- 去耦,去重
package logindemo;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;
//工厂类
public class JDBCUtils {
//不允许创建对象
private JDBCUtils(){
}
//能够读取properties配置文件的类型
private static Properties prop = new Properties();
static{
//加载配置信息到prop对象身上
try {
prop.load(new FileInputStream(new File(
//JDBCUtils.class获取类的字节码对象
//getClassLoader()获取类加载器
//getResource("src目录中的任意文件名")获取当前工程的src目录
//getPath()将uri类型变为String类型
JDBCUtils.class.getClassLoader().getResource("conf.properties").getPath()
)));
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
//通过类调用方法
//创建连接
public static Connection getConnection() throws Exception{
//读取prop对象身上的参数
Class.forName(prop.getProperty("driver"));
//创建数据库连接
return DriverManager.getConnection(prop.getProperty("url"),
prop.getProperty("user"), prop.getProperty("password"));
}
//关闭资源
public static void close(Connection conn,Statement stat,ResultSet rs){
if(rs != null){
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
throw new RuntimeException(e);
}finally{
rs = null;
}
}
if(stat !=null){
try {
stat.close();
} catch (SQLException e) {
e.printStackTrace();
throw new RuntimeException(e);
}finally{
stat = null;
}
}
if(conn != null){
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
throw new RuntimeException(e);
}finally{
conn = null;
}
}
}
}
批处理
- addBatch(" SQL语句 ");
- executeBatch(); 批量执行语句 添加完执行
package batchOperation;
import java.sql.Statement;
import logindemo.JDBCUtils;
import java.sql.Connection;
import java.sql.SQLException;
/*
create table t1(id int,name varchar(20))
insert into t1 values(1,'猪八戒')
insert into t1 values(2,'沙和尚')
insert into t1 values(3,'唐三藏')
insert into t1 values(4,'白龙马')
insert into t1 values(5,'孙悟空')
*/
public class StateBatchDemo1 {
public static void main(String[] args) {
Connection conn = null;
Statement stat = null;
try {
conn = JDBCUtils.getConnection();
stat = conn.createStatement();
stat.addBatch("create table t1(id int,name varchar(20))");
stat.addBatch("insert into t1 values(1,'猪八戒')");
stat.addBatch("insert into t1 values(2,'沙和尚')");
stat.addBatch("insert into t1 values(3,'唐三藏')");
stat.addBatch("insert into t1 values(4,'白龙马')");
stat.addBatch("insert into t1 values(5,'孙悟空')");
stat.executeBatch();
System.out.println("处理完成");
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally {
JDBCUtils.close(conn, stat, null);
}
}
}
预编译功能 PreparedStatement
- 防止SQL注入攻击
- 预编译 先将SQL语句主干传入到服务器,并保持不变,再传入参数
- 使用 ? 占位。
package batchOperation;
/*
缺点 只能执行一种语义sql
优点 1 预编译功能
2 sql主干存储在服务器中,只发送参数,不需要每次都发送主干,执行效率高
*/
import java.sql.Connection;
import java.sql.PreparedStatement;
import logindemo.JDBCUtils;
//preparedstatement批处理
public class PrepareBatchDemo1 {
public static void main(String[] args) {
Connection conn = null;
PreparedStatement ps = null;
try {
conn = JDBCUtils.getConnection();
ps = conn.prepareStatement("insert into t1 values(?,?)");
for(int i = 0;i<10000;i++) {
ps.setInt(1, i);//设置第一列数据 返回值类型是int
ps.setString(2, "name"+i);//设置第二列数据 返回值类型是String
ps.addBatch();//添加
if(i%1000==0) {
ps.executeBatch();//每到1000个语句执行操作,然后清空
ps.clearBatch();//清空操作
System.out.println("第"+i/1000+"批处理完毕");
}
}
ps.executeBatch();
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}finally {
JDBCUtils.close(conn, ps, null);
}
}
}
连接池
- 传统方式中,创建和销毁连接都会消耗大量资源,为了复用连接。
- 使用完连接还给连接池(不关闭的才可以复用)
DBCP
C3P0
SQL
聚集函数
avg count max min sun
select avg(age) from users;
select avg(age) from users where gender = '男';
//count 不指定列名会计算null行,否则不会计算
select count(*) from users;
select count(id) from users;
//max min 忽略null
//sum 忽略null
//组合
select avg(),count(),min() from XXX;
聚集不同的值 DISTINCT
10 10 4
select avg(age) from users; 8
select avg(DISTINCT age) from users; 7
分组 group by + having过滤分组
where可以用having代替
select XXX from XXX
where XXX
group by XXX
having XXX
order by XXX
limit XXX;