jdbc

# <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>

```

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,372评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,368评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,415评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,157评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,171评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,125评论 1 297
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,028评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,887评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,310评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,533评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,690评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,411评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,004评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,659评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,812评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,693评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,577评论 2 353