# <font color='red'>jdbc</font>
![image-20201215145708863](jdbc.assets/image-20201215145708863.png)
## 数据的持久化
简单来讲:<font color='red'>数据持久化,</font>即就是将数据(内存对象)保存到可以长时间保存的设备(磁盘)的过程。
<font color='red'>数据持久化就是让数据在 “持久状态” 和 “瞬间状态” 相互转换的一种机制</font>(例如:IO 操作,JDBC 操作)。
- 持久状态:系统或应用重启之后数据(内存对象)的状态依然存在。
- 瞬间状态:系统或应用在工作时可操作的状态
## 获取数据库连接
## 类加载时机
- <font color='red'>创建类的实例,也就是new一个对象</font>
- <font color='red'>访问某个类或接口的静态变量,或者对该静态变量赋值</font>
- <font color='red'>调用类的静态方法</font>
- <font color='red'>反射(Class.forName("com.lyj.load"))</font>
- <font color='red'>初始化一个类的子类(会首先初始化子类的父类)</font>
- <font color='red'>JVM启动时标明的启动类,即文件名和类名相同的那个类 </font>
**除此之外,下面几种情形需要特别指出:**
<font color='red'>对于一个final类型的静态变量,如果该变量的值在编译时就可以确定下来,那么这个变量相当于“宏变量”。Java编译器会在编译时直接把这个变量出现的地方替换成它的值,因此即使程序使用该静态变量,也不会导致该类的初始化。反之,如果final类型的静态Field的值不能在编译时确定下来,则必须等到运行时才可以确定该变量的值,如果通过该类来访问它的静态变量,则会导致该类被初始化。</font>
### Driver(驱动)接口实现
#### 加载和注册<font color='red'>DBUtils</font>驱动
<font color='red'>第一次主动使用Class类,就会加载到内存中</font>。
### SPI
SPI全称为(Service Provider Interface) ,是JDK内置的一种服务提供发现机制;主要被框架的开发人员使用,**比如java.sql.Driver接口**,数据库厂商实现此接口即可,当然要想让系统知道具体实现类的存在,还需要使用固定的存放规则,需要**在classpath下的META-INF/services/目录里创建一个以服务接口命名的文件,这个文件里的内容就是这个接口的具体的实现类**
### URL(统一资源定位器)
```properties
url=jdbc:mysql://127.0.0.1:3306/job?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai
```
URL 是统一资源定位符(Uniform Resource Locator)的简称,它表示 Internet 上某一资源的地址。通过 URL 用户可以访问各种网络资源,比如常见的 WWW 以及 FTP 站点。浏览器可以通过解析给定的 URL 在网络上查找相应的文件或其他资源。
## 加载驱动
因为这里只讨论jdbc,所以通过反射来获取驱动。
```java
Class.forName("com.mysql.jdbc.Driver");
```
## 创建连接,调用DriverManager.getConnection方法,返回一个连接对象Connection
```java
Connection conn = DriverManager.getConnection("url", "user", "password");
```
> url是指你的数据库地址,jdbc:mysql://(这里写你服务器的ip地址)/(这里写你的服务名)
> 服务器ip地址若是本机的话就是127.0.0.1:3306(其中3306是mysql的默认端口号)
> user就是你的数据库用户名,同理passwrd就是你的数据库密码
### Java连接MySQL数据库
```Java
public void test1() throws Exception{
String url = "jdbc:mysql://127.0.0.1:3306/job?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai";
String user = "root";
String password = "root";
String driverName = "com.mysql.cj.jdbc.Driver";
//实例化Driver
Class clazz = Class.forName(driverName);
Driver driver = (Driver) clazz.newInstance();
//注册驱动
DriverManager.registerDriver(driver);
//获取连接
Connection conn = DriverManager.getConnection(url,user,password);
System.out.println(conn);
}
```
### java创建配置文件连接
```Java
@Test
public void test3() throws Exception{
Properties properties = new Properties();
properties.load(JdbcTest.class.getClassLoader().getResourceAsStream("1.properties"));
String url = properties.getProperty("url");
System.out.println(url);
String username = properties.getProperty("mysql.username");
System.out.println(username);
String password = properties.getProperty("mysql.password");
//实例化Driver
//注册驱动
//获取连接
Connection conn = DriverManager.getConnection(url,username,password);
System.out.println(conn);
}
```
配置文件
```properties
url=jdbc:mysql://127.0.0.1:3306/job?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai
mysql.username=rootpo
mysql.password=root
mysql.dirvername=com.mysql.cj.jdbc.Driver
```
## 执行sql语句
### 创建sql命令发送器
```java
Statement stat = conn.createStatement();
1
当然也可以创建PreparedStatement对象
String sql = "你的sql语句"
PreparedStatement prep = conn.prepareStatement(sql语句);
PreparedStatement的效率比 Statement好些,还能使用预编译
```
### 执行sql命令
#### 查询语句,然后返回结果
```java
@Test
public void testStatement2() {
List<Course> courses = new ArrayList<>();
Connection connection = null;
Statement statement = null;
ResultSet resultSet = null;
try {
String sql = "select * from user";
connection = jdbcUtil.getConnection();
statement = connection.createStatement();
resultSet = statement.executeQuery(sql);
while (resultSet.next()) {
Integer id = resultSet.getInt("id");
String username = resultSet.getString("username");
Integer password = resultSet.getInt("password");
courses.add(new Course(id,username,password));
}
}catch (SQLException e){
e.getErrorCode();
}finally {
jdbcUtil.closeAll(connection,statement,resultSet);
}
System.out.println(courses);
}
------------------------------------------------------------------------------------------------------
public static void closeAll(Connection connection, Statement statement, ResultSet resultSet){
if (connection != null){
try {
connection.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if (statement != null){
try {
statement.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if (resultSet != null){
try {
resultSet.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
```
#### 插入语句,然后返回结果
```java
int i = stmt.executeUpdate("insert into 表名 values(id,'字段1','字段2')");
//处理结果
if (i > 0) {
System.out.println("插入数据成功");
} else {
System.out.println("插入数据失败");
}
```
#### 修改语句,然后返回结果
```java
int i = stmt.executeUpdate("update 表名 set 字段1='xx1',字段2='xx2' where id=xxx");
// 处理结果
if (i > 0) {
System.out.println("修改数据成功");
} else {
System.out.println("修改数据失败");
}
```
#### 删除语句,然后返回结果
```java
int i = stmt.executeUpdate("delete from 表名 where id=xxx");
// 处理结果
if (i > 0) {
System.out.println("删除数据成功");
} else {
System.out.println("删除数据失败");
}
```
## 关闭资源
```java
//若创建了结果集ResultSet,也需要关闭
rs.close();
stmt.close();
conn.close();
```
### **JDBC常用类**
**资源关闭:ResultSet,Statement,Connection的顺序执行close;**
#### DriverManager类:
管理一组 JDBC 驱动程序的基本服务;Driver的子类;
方法:**static Connection **getConnection**(String url, String user, String password);//返回Connection接口;
- String url="jdbc:mysql://localhost:3306/test";
- String url="jdbc:mysql://localhost:3306/test?useSSL=false&useUnicode=true&setCharacterEncoding=utf8";
- 屏蔽SSL的警告; 解决乱码;
- String url="jdbc:mysql:///test?useSSL=false&serverTimezone=UTC";//8.0新版本;
#### Connection接口:
**方法:**
- Statement **createStatement**();//创建一个 Statement 对象来将 SQL 语句发送到数据库。
- **Prepared****Statement** **prepare****Statement**(String **sql**);
- 创建一个 PreparedStatement 对象来将**参数化的 SQL** 语句发送到数据库。
- close();
#### Statement接口:
用于执行静态 SQL 语句并返回它所生成结果的对象。
**方法:**
- int executeUpdate(String sql);//执行**DDL**和**DML**语句;DML返回影响的行数;DDL返回0;
- ResultSet executeQuery(String sql);//执行**DQL**语句;返回 ResultSet结果集;
- close();
```Java
public class Test {
public static void main(String[] args) throws Exception {
// 1、加载驱动
//把com.mysql.jdbc.Driver这份字节码加载进JVM
//当一份字节码被加载到JVMs时,就会执行该字节码中的静态代码块
Class.forName("com.mysql.jdbc.Driver");
// 2、创建连接
String url = "jdbc:mysql://localhost:3306/test";
String user = "root";
String password = "000000";
Connection conn = DriverManager.getConnection(url, user, password);
// 3、创建sql
String sql = "select * from user where name = ?";
PreparedStatement prepareStatement = conn.prepareStatement(sql);
prepareStatement.setString(1, "ls");
// 4、执行sql
ResultSet resultSet = prepareStatement.executeQuery();
// 5、处理返回结果
while (resultSet.next()) {
System.out.println(resultSet.getString("name"));
}
// 6、关闭资源
prepareStatement.close();
conn.close();
}
}
```
### SQL注入:
##### sql注入:
正常代码:sid:001 ----> delete from student where sid = "001";
问题代码:sid:999 or 1=1 -----> delete from student where sid = "999" or 1=1;
//delete from student;等于删除整表;
解决后代码:sid:999 or 1=1 -----> delete from student where sid = "999 or 1=1";
<font color='red'>解决办法:PreparedStatement;Statement的子接口;</font>
```Java
public static boolean login(String username,String password){
List<Course> courses = new ArrayList<>();
Connection connection = null;
Statement statement = null;
ResultSet resultSet = null;
try {
String sql = "select id,username,password from user where username='" + username
+"'and password='" + password +"'";
connection = jdbcUtil.getConnection();
statement = connection.createStatement();
resultSet = statement.executeQuery(sql);
return resultSet.next();
}catch (SQLException e){
e.getErrorCode();
}finally {
jdbcUtil.closeAll(connection,statement,resultSet);
}
return false;
}
public static void main(String[] args) {
boolean login = login("1","334' or '1' = '1");
System.out.println(login);
}
```
### prepared Statement的使用
- 可以通过调用 Connection 对象的 **prepareStatement(String sql)** 方法获取 PreparedStatement 对象
- **PreparedStatement 接口是 Statement 的子接口,它表示一条预编译过的 SQL 语句**
- PreparedStatement 对象所代表的 SQL 语句中的参数用问号(?)来表示(?在SQL中表示占位符),调用 PreparedStatement 对象的 setXxx() 方法来设置这些参数. setXxx() 方法有两个参数,第一个参数是要设置的 SQL 语句中的参数的索引(从 1 开始),第二个是设置的 SQL 语句中的参数的值
![img](jdbc.assets/20201121213156938.png)
#### 和Statement的对比
- 代码的可读性和可维护性。
- PreparedStatement 能最大可能提高性能:
- DBServer会对**预编译**语句提供性能优化。因为预编译语句有可能被重复调用,所以语句在被DBServer的编译器编译后的执行代码被缓存下来,那么下次调用时只要是相同的预编译语句就不需要编译,只要将参数直接传入编译过的语句执行代码中就会得到执行。
- 在statement语句中,即使是相同操作但因为数据内容不一样,所以整个语句本身不能匹配,没有缓存语句的意义.事实是没有数据库会对普通语句编译后的执行代码缓存。这样每执行一次都要对传入的语句编译一次。
- (语法检查,语义检查,翻译成二进制命令,缓存)
- PreparedStatement 可以防止 SQL 注入
#### 插入案例
**PreparedStatement常用的方法:**
void setObject(int parameterIndex, Object x, int targetSqlType)
![在这里插入图片描述](jdbc.assets/20201121225840166.png)
**parameterIndex** the first parameter is 1, the second is 2, …占位符参数索引是从1开始的
其余也是如此:
> void setInt(int parameterIndex, int x)
> void setLong(int parameterIndex, long x)
> void setString(int parameterIndex, String x)
> void setBlob (int parameterIndex, Blob x)
> void setDate(int parameterIndex, java.sql.Date x, Calendar cal)
![在这里插入图片描述](jdbc.assets/20201121225713195.png)
```Java
@Test
public void getConnection(){
Connection connection = null;
PreparedStatement preparedStatement = null;
try{
//加载配置文件
InputStream is = ClassLoader.getSystemClassLoader().getResourceAsStream("1.properties");
Properties pr = new Properties();
pr.load(is);
//读取配置信息
String user = pr.getProperty("mysql.username");
String password = pr.getProperty("mysql.password");
String url = pr.getProperty("url");
String dirvername = pr.getProperty("mysql.dirvername");
//加载驱动
Class.forName(dirvername);
//获取连接
connection = DriverManager.getConnection(url,user,password);
String sql = "insert into user(username,password) value(?,?)";
//预编译sql语句,得到PreparedStatement对象
preparedStatement = connection.prepareStatement(sql);
//填充占位符
preparedStatement.setString(1,"123456");
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
Date date = new Date();
preparedStatement.setDate(2,new java.sql.Date(date.getTime()));
//执行操作
preparedStatement.execute();
}catch (Exception e){
e.printStackTrace();
}finally {
if (preparedStatement != null){
try {
preparedStatement.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
}
```
## 读取XML文件
```Java
public class XmlTest {
public static void main(String[] args) {
readXml();
}
public static Clazz readXml(){
Clazz clazz = new Clazz();
SAXReader reader = new SAXReader();
try{
Document document = reader.read(Test.class.getClassLoader().getResourceAsStream("jdbc.xml"));
Element root = document.getRootElement();
clazz.setName(root.attribute("name").getValue());
List<Element> elements = root.elements();
for (Element element : elements) {
System.out.println(element.getName() + "---" + element.getData());
}
}catch (DocumentException e){
e.getCause();
}
return clazz;
}
}
```
XML文件:
```properties
<class name="jdbc">
<username>root</username>
<password>root</password>
<url>jdbc:mysql://127.0.0.1:3306/job?useUnicode=true characterEncoding=utf8 serverTimezone=Asia/Shanghai</url>
<dirvername>com.mysql.cj.jdbc.Driver</dirvername>
</class>
```