JDBC
概述
全称
( Java DataBase Connectivity ) Java 数据库连接
JDBC 就是使用Java语言操作关系型数据库的一套API
好处
希望程序员只写一套代码就能够操作所有的数据库
JDBC 入门
- 新建项目
- 导入jar
- 编写代码
public static void main(String[] args) throws Exception {
//1. 注册驱动: 告诉底层用的是mysql
Class.forName("com.mysql.jdbc.Driver");
//2. 获取连接
String url = "jdbc:mysql://127.0.0.1:3306/数据库名";
String username = "root";
String password = "密码";
Connection conn = DriverManager.getConnection(url, username, password);
//3. 定义sql
String sql = "update 表名 set 修改的数据 where 条件语句";
//4. 获取执行sql的对象 Statement
Statement stmt = conn.createStatement();
//5. 执行sql
int count = stmt.executeUpdate(sql);//受影响的行数
//6. 处理结果
System.out.println(count);
//7. 释放资源
stmt.close();
conn.close();
}
解析
注册驱动
// 虚拟机会加载该类的字节码,从而会执行static 代码块,完成注册驱动
Class.forName("com.mysql.jdbc.Driver"); // 优点: 后续可以改为通过配置文件配置更加灵活
底层
static {
try {
DriverManager.registerDriver(new Driver());
} catch (SQLException var1) {
throw new RuntimeException("Can't register driver!");
}
}
URL 链接
String url = “jdbc:mysql://127.0.0.1:3306/数据库名”;
- 如果链接的是本地的3306 ip 和端口号可以不写
jdbc:mysql:///数据库名 - 本机ip 固定 127.0.0.1 或者 localhost
- 端口号 一台电脑端口号有很多 1-65535 都可以用 每个软件启动时会占用一个或多个,请求到该软件占用的端口则请求由这个软件处理
连接对象
两台电脑(服务) 之间想要数据交互必须有一个链接对象Connection
-
Connection 不负责执行sql , 会创建一个Statement(小弟) 对象,负责sql 的执行
-
Connection 负责事务
// 开启事务
conn.setAutoCommit(false);
// 提交事务
conn.commit();
// 回滚事务
conn.rollback(); -
Connection 负责自己的销毁
conn.close();
Statement 对象
- int count = stmt.executeUpdate(sql);//执行完DML语句,受影响的行数
- 也可以执行 DDL ,但是不使用
- ResultSet resultSet = stmt.executeQuery(sql);
ResultSet
ResultSet 和Java里的set完全没有关系
//6. 处理结果, 遍历rs中的所有数据
// 默认 光标指向的是 数据外面, 必须 调用 .next() 方法才能获取数据
// 6.1 光标向下移动一行,并且判断当前行是否有数据
while (rs.next()){
//6.2 获取数据 getXxx()
// String name1 =rs.getString("uname"); // 如果有别名,必须使用别名
int id = rs.getInt(1);
String name = rs.getString(2);
double money = rs.getDouble(3);
System.out.println(id);
System.out.println(name);
System.out.println(money);
System.out.println("--------------");
}
// 6.1 光标向下移动一行,并且判断当前行是否有数据
while (rs.next()){
//6.2 获取数据 getXxx()
int id = rs.getInt("id"); // 如果有别名,应该使用别名
String name = rs.getString("name");
double money = rs.getDouble("money");
System.out.println(id);
System.out.println(name);
System.out.println(money);
System.out.println("--------------");
}
- java 分包开发 ,一般会把同一类对象放在一个统一的包中,方便后续的维护
- java 实体类命名一般和数据库保持一直(不完全一样)
- Java 实体类 属性名 和数据库保持一直(不完全一样) 保持驼峰式命名习惯
- Date 类型: jdbc 返回的是 java.sql.Date
// java.sql.Date
Date birthday = rs.getDate(“birthday”);
字符串转义符
// 字符串转义符
String sql2 = " select *from tb_user where username =\"zhangsan\" and password =\"123\" ";
String sql = "select * from tb_user where username = '"+name+"' and password = '"+pwd+"'";
sql 注入及PreparedStatement
原因
字符串拼接导致拼出来的sql 和预期不一致
select * from tb_user where username = ‘X’ and password = ‘X’
黑客输入的字符串 : ’ or ‘1’='1
select * from tb_user where username = ‘X’ and password = ‘’ or ‘1’=‘1’
解决方案
// 接收用户输入用户名和密码
String name = "zhangsan";
String pwd = "' or '1' = '1";
// 定义sql
String sql = "select * from tb_user where username = ? and password = ?";
// 获取pstmt对象
PreparedStatement pstmt = conn.prepareStatement(sql);
// 设置?的值
pstmt.setString(1,name);
pstmt.setString(2,pwd);
// 执行sql
ResultSet rs = pstmt.executeQuery();
PreparedStatement 原理及优势
原理
对传入的参数 中的特殊字符 进行转移处理
优点
- 解决了sql 注入问题
- 提升了sql 效率
jdbc:mysql:///数据库名?useSSL=false&useServerPrepStmts=true
-
select * from tb_user where username = 'zhangsan' and password = '123' /* * 0)解析前判断有没有解析过 * 1)解析 : 查询那个表,where 后有哪些条件等 * 2)会把解析的结果缓存 * 3)执行返回 * 4) 如果有相同的sql 执行 * */ select * from tb_user where username = ? and password = ? /* * 0)解析前判断有没有解析过 * 1)解析 : 查询那个表,where 后有哪些条件等 * 2)会把解析的结果缓存 * 3) 执行(传递参数)返回 * 4) 如果有相同的sql 执行 * */
数据库连接池
数据库访问 执行sql 影响效率的步骤是 创建链接 及关闭链接,可以使用连接池的思想解决问题
Driud使用
static DataSource dataSource=null;
static {
Properties prop =new Properties();
try {
prop.load(new FileInputStream("src/druid.properties"));
dataSource = DruidDataSourceFactory.createDataSource(prop);
} catch (Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
try {
DButil();
} catch (Exception e) {
e.printStackTrace();
}
}
public static void DButil() throws Exception {
//1. 注册驱动
Class.forName("com.mysql.jdbc.Driver");
//2. 获取连接:如果连接的是本机mysql并且端口是默认的 3306 可以简化书写
String url="jdbc:mysql://127.0.0.1:3306/mydb?useSSL=false";
String username = "root";
String password = "密码";
Connection conn=DriverManager.getConnection(url,username,password);
//3. 定义sql
String sql="select * from stu";
//4. 获取执行sql的对象 Statement
Statement statement = conn.createStatement();
//5. 执行sql
ResultSet resultSet = statement.executeQuery(sql);
//6.遍历获取的数据
while (resultSet.next()){
int id = resultSet.getInt("id");
String name = resultSet.getString("name");
int age = resultSet.getInt("age");
String sex = resultSet.getString("sex");
System.out.println(id+" "+name+" "+age+" "+sex);
}
//7. 释放资源
statement.close();
conn.close();
}
三层架构开发思想
- web
- service
- dao
JDBC缺点
- 占位符赋值繁琐
- 代码繁琐(重复多)
- 查询结果集返回封装繁琐
动态代理
public class BrandService {
private BrandDao dao = new BrandDaoImpl();
/**
* Proxy jdk 内置的对象, 主要是用来增强对象的(创建代理对象)
* 1) ClassLoader : 增强那个对象,使用那个对象的类加载器
* 2) Class<?>[] 数组 : 增强那个对象就传递那个对象的接口(要求必须有接口)
* 3) 是一个匿名内部类对象 InvocationHandler,在 invoke 方法中可以写增强逻辑
*/
private BrandDao dao_plus = (BrandDao)Proxy.newProxyInstance(dao.getClass().getClassLoader(), dao.getClass().getInterfaces(),
new InvocationHandler() {
/*
obj : 最终生成对象的引用,没用
* method 调用那个方法 就是对应的方法名对象
* args 方法参数
* */
@Override
public Object invoke(Object obj, Method method, Object[] args) throws Throwable {
// // 增强逻辑
// String methodName = method.getName();
// if("findAll".equals(methodName)){
// long start =System.currentTimeMillis();
// int num = dao.findAll();
// long end =System.currentTimeMillis();
// System.out.println("BrandDaoImpl_findAll"+new Date()+":"+(end-start));
// return num;
// }
// if("insert".equals(methodName)){
// long start =System.currentTimeMillis();
// dao.insert();
// long end =System.currentTimeMillis();
// System.out.println("BrandDaoImpl_insert"+new Date()+":"+(end-start));
// return null;
// }
long start =System.currentTimeMillis();
Object result = method.invoke(dao,args); // 反射调用更加通用
long end =System.currentTimeMillis();
System.out.println(method.getDeclaringClass().getName()+method.getName()+new Date()+":"+(end-start));
return result;
}
});
public int findAll(){
return dao_plus.findAll();
}
public void insert(){
dao_plus.insert();
}
}