Java项目之ChatRoomWebSocket/准备工作/JDBC+Druid+Gson+PrepareStatement

首先,在IDEA中新建Maven:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
注意,这一步需要把Maven改成我们下载的版本,而不是IDEA自带的Maven。需要override。
等待一会,控制台会出现“BUILD SUCCESS”
在这里插入图片描述

此时Maven创建成功。
先来修改个参数,将下图中的版本改为1.8(原来是1.7)。
在这里插入图片描述
的内容是不需要的,删掉。
接下来,添加依赖。

<dependencies>
    <!--junit单元测试-->
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.11</version>
    </dependency>
    <!-- websocket-->
    <dependency>
      <groupId>javax.websocket</groupId>
      <artifactId>javax.websocket-api</artifactId>
      <version>1.1</version>
      <scope>provided</scope>
    </dependency>
    <!-- freemarker模版引擎 -->
    <dependency>
      <groupId>org.freemarker</groupId>
      <artifactId>freemarker</artifactId>
      <version>2.3.28</version>
    </dependency>
    <!-- druid数据源 -->
    <dependency>
      <groupId>com.alibaba</groupId>
      <artifactId>druid</artifactId>
      <version>1.1.13</version>
    </dependency>
    <!-- Apache commons I/O处理和内容编码 -->
    <dependency>
      <groupId>org.apache.commons</groupId>
      <artifactId>commons-io</artifactId>
      <version>1.3.2</version>
    </dependency>
    <dependency>
      <groupId>commons-codec</groupId>
      <artifactId>commons-codec</artifactId>
      <version>1.11</version>
    </dependency>
    <!-- Servlet API -->
    <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>javax.servlet-api</artifactId>
      <version>3.1.0</version>
      <scope>provided</scope>
    </dependency>
    <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <version>5.1.47</version>
    </dependency>
    <dependency>
      <groupId>org.projectlombok</groupId>
      <artifactId>lombok</artifactId>
      <version>1.18.6</version>
    </dependency>
    <dependency>
      <groupId>com.google.code.gson</groupId>
      <artifactId>gson</artifactId>
      <version>2.8.5</version>
    </dependency>
  </dependencies>

在这里插入图片描述
看到右边有这些依赖时就证明导入依赖成功了。
接下来,在右边栏的database中选择如下:
在这里插入图片描述
在这里插入图片描述
(此时需要保证MySQL服务已开启)在user中填root,然后输入密码。点Test Connection,会显示successful。
在这里插入图片描述
点“Apply”、“OK”。
接下来,在以下页面中写sql语句创建数据库和表。(在命令行中写效果也是一样的)

在这里插入图片描述

CREATE DATABASE IF NOT EXISTS `jdbc`
        DEFAULT CHARACTER SET `utf8`;

USE jdbc;

CREATE TABLE user(
    id INT PRIMARY KEY AUTO_INCREMENT COMMENT '用户id',
    username VARCHAR(20) UNIQUE NOT NULL COMMENT '用户名',
    password VARCHAR(100) NOT NULL COMMENT 'MD5加密后的密码'
)CHARSET='utf8';

效果如下:
在这里插入图片描述
接下来,插入数据:

INSERT INTO jdbc.user(username, password)
    VALUES ('zs','123'),('ls','123'),('ww','123');

在这里插入图片描述
可得到结果:
在这里插入图片描述
接下来,不再用主方法测试了,创建单元测试:
首先,在src下新建test文件夹:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
就会发现test变成了绿色的。
在这里插入图片描述
在test中新建class:
在这里插入图片描述
写代码:

import org.junit.Test;
import java.sql.*;

public class JDBCDemo1 {
    @Test
    public void test() throws ClassNotFoundException, SQLException {Class.forName("com.mysql.jdbc.Driver");
    //加载驱动

        Connection connection=DriverManager.getConnection("jdbc:mysql://localhost:3306/jdbc","root","jdpy1229jiajia");
//获取连接

        String sql="select * from user";
    //执行语句
        Statement statement=connection.createStatement();
       //方法一:
        //statement.execute(sql); 返回布尔型,返回true就说明查询成功。
        //一般用executeQuery

        ResultSet resultSet=statement.executeQuery(sql);
        while(resultSet.next())
        {int id=resultSet.getInt("id");
        String username=resultSet.getString("username");
        String password=resultSet.getNString("password"); 
System.out.println("id为"+id+",用户名"+username+",密码为"+password);}
        

//释放资源
connection.close();
        statement.close();
        resultSet.close();

    }
}

执行结果为:
在这里插入图片描述
当sql语句是查询语句时,用ResultSet封装结果,用executeQuery(sql)。
而insert、delete和update操作用int executeUpdate(String sql,int autoGeneratedKeys),返回值为sql语句执行后的影响行数。

再写一个单元测试test1(),实现insert操作:

@Test
    public void test1()

    {
        try {
            Class.forName("com.mysql.jdbc.Driver");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        //加载驱动

        Connection connection = null;
        try {
            connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/jdbc", "root", "jdpy1229jiajia");
        } catch (SQLException e) {
            e.printStackTrace();
        }
//获取连接

        String sql = "insert into user(username,password) values ('test','456')";
        //执行语句
        Statement statement = null;
        try {
            statement = connection.createStatement();
        } catch (SQLException e) {
            e.printStackTrace();
        }
        //方法一:
        //statement.execute(sql); 返回布尔型,返回true就说明查询成功。
        //一般用executeQuery
        try {
            int resultRows=statement.executeUpdate(sql,
                    Statement.RETURN_GENERATED_KEYS);
            System.out.println(resultRows);
        } catch (SQLException e) {
            e.printStackTrace();

        }



//释放资源
        try {
            connection.close();
        } catch (SQLException e) {
            e.printStackTrace();
        }
        try {
            statement.close();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}

执行结果为:
在这里插入图片描述

即此时受影响行数为1。
在这里插入图片描述
此时数据插入成功。
再写个单元测试test2()实现删除操作。

    String sql = "delete from user where id=4";

只是在test1()的基础上修改sql。
运行结果也是1。
在这里插入图片描述
确实被删除了。
接下来,讨论一下sql注入问题。
首先,在test()基础上,

 String sql="select * from user where username='zs'and password='13'";
 ResultSet resultSet=statement.executeQuery(sql);

        if(resultSet.next())
        {System.out.println("登录成功");}
        else System.out.println("登录失败");


运行结果为:
在这里插入图片描述
("zs"的密码为“123”而不是“13”,自然登录失败)
改为:

  String username="zs";
        String  password="123";
        String sql="select * from jdbc.user where username='"+username+"'and password='"+password+"'";

(这里用到了字符串的拼接,需要注意的是:在sql语句中,字符串是用单引号括起来的,比如insert into user value(name,‘jiajia’) )
运行结果为:
在这里插入图片描述

而在获取连接后写入:

   //假设从网络读取的username和password
        String username="zs' or 1=1";
        String  password="123123123";
        String sql="select * from user where username='"+username+" "+"and password='"+password+"'";

注意:这里的格式要求严格,连空格都要是对应的。
运行结果为:
在这里插入图片描述
是不是感到奇怪,他传进来的密码明明是有问题的,数据库里有这个用户名,但是是没有这个密码的,可是却显示登录成功了。需要分析一下黑客常用的sql注入:
在这里插入图片描述
注意蓝色标记处的空格不可漏掉,有空格 才能产生A or B的效果。
也可以写成:

 String username="zs'--";

在这里插入图片描述
运行结果仍为登录成功。
为了避免sql注入,使用PrepareStatement类(预处理SQL),开发中用的都是这个。

 /*假设从网络读取的username和password*/
        String username="zs";
        String  password="ohh";
        String sql="select * from user"+" where username=?and password=?";
        //预编译
        PreparedStatement statement=connection.prepareStatement(sql);
        statement.setString(1,username);
        statement.setString(2,password);

PreparedStatement类的使用是这样的:在sql语句中写入“?“占位符,将之前Statement statement=connection.createStatement()改为PreparedStatement statement=connection.preparedStatement(sql),然后用statement.setXXX(parameterindex,x)语句,其中第一个参数是占位符的位置,与数组不同,它是从”1“开始,第二个参数是替换的属性值。"XXX"与属性类型一致。

注意:在上述代码中,“ where username=? and password=?”中 where的前面需要一个空格,否则它会自动加上一个‘。
(或者直接写成 String sql=“select * from user where username=? and password=?”; 就行了呗)
用预编译后,sql注入失败:
在这里插入图片描述
可以发现:无论是Statement还是PreparedStatement,都需要步骤:1.加载驱动,2.连接数据库,3.执行sql语句,4.释放资源,这都是相同的操作,可以封装在一个工具类中。
在main中新建java文件夹,用来放源文件。
在这里插入图片描述
之前学过Properties,是Hashtable的子类,key=value的格式。( 在项目的应用中,经常将一些配置放入properties文件中,在代码应用中读取properties文件,就需要专门的类Properties类,通过这个类可以进行读取。)
需要写到资源文件中的:
1.驱动
2.url
3.username
4.password
接下来,创建资源文件。
在这里插入图片描述
创建成功后的图标为:
在这里插入图片描述
写入:

drivername=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/jdbc
username=root
password=jdpy1229jiajia

接下来加载配置信息:
新建一个类,封装共有的工具方法:在这里插入图片描述

package com.bit.chatroom.utils;

import sun.plugin2.ipc.InProcEvent;

import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;

//用于封装共有的工具方法,如加载配置工具,所有的工具类都属于静态方法
public class CommUtil {
private CommUtil(){}

public static Properties loadProperties(String fileName)
{Properties properties=new Properties();
    InputStream in=CommUtil.class.getClassLoader().getResourceAsStream(fileName);
    //获取类加载器下的和它同目录的所有文件

    try {
        properties.load(in);
    } catch (IOException e) {
        e.printStackTrace();
    }

    return properties;
}}

在这里插入图片描述
结构如上所示,(注意:需要把java文件都变成Source,图标变成蓝色,因为”java“是关键字,不能用作报名 。)

这样用CommUtils.class.getClassLoader().getResourceAsStream()就可以获得类加载器同目录下的所有文件。

其中,
Properties类的load方法:

public void load(InputStream inStream)
          throws IOException从输入字节流读取属性列表(键和元素对)。 输入流采用load(Reader)中规定的简单的面向行的格式,假设使用ISO 8859-1字符编码; 每个字节是一个拉丁字符。 不在Latin1中的字符和某些特殊字符在键和元素中使用The Java™ Language Specification的 3.3节中定义的Unicode转义来表示 。 
此方法返回后指定的流保持打开状态。 

每次写完一个功能都需要进行单元测试。测试文件夹与main同目录。
在这里插入图片描述
选中类名,快捷键Ctrl+Shift+T。
在这里插入图片描述
并将方法选中:
在这里插入图片描述
在测试类中写:

 @Test
    public void loadProperties() {
    String fileneame="db.properties";
        Properties properties=CommUtil.loadProperties(fileneame);
        System.out.println(properties);

    }

运行结果为:
在这里插入图片描述
说明加载配置资源文件成功了,而这样的单元测试是不规范的,可以使用断言Assert。比如,如果配置资源文件加载成功了,那么应该能够取得url的值,该值不为空。使用Asset.assertNotNull(url).

  @Test
    public void loadProperties() {
    String fileneame="db.properties";
        Properties properties=CommUtil.loadProperties(fileneame);
        String url=properties.getProperty("url");
        //如果加载资源配置文件成功,url的值一定不为空,在此使用断言
        Assert.assertNotNull(url);
    }
}

运行结果为:
在这里插入图片描述
没有输出,但是 左边打对勾了,表示测试通过。
如果写成;

  Assert.assertEquals(null,url);

在这里插入图片描述
单元测试不通过。(在项目开发中应多多使用单元测试和断言)
以上,在CommUtils中便写好了获取配置文件信息的代码,接下来,考虑到刚刚说的与数据库交互共有的几步:加载驱动、取得连接、释放资源,应当再写一个工具类用于以上几个步骤的封装,即封装JDBC公共方法。
在这里插入图片描述

package com.bit.chatroom.utils;

public class JDBCUtils {
    private static String drtveName;
    private static String url;
    private static String username;
    private static String password;

无论是数据库的什么操作首先都需要先加载以上四个属性,需要在类加载时一同加载属性,而且只需加载一次,这就需要使用静态代码块。(静态代码块常用于加载配置)
静态代码块中只写四个属性的获取,和加载数据库驱动,因为取得连接不是共有的,不同用户取得的连接应是不同的。而关闭资源的操作,如果只是查询需要返回ResultSet,而更新操作是不需要返回ReseltSet的,因此用到方法重载,因此获取连接和关闭资源也不写在静态代码块中,而是写成public static方法:

package com.bit.chatroom.utils;

import javax.swing.plaf.nimbus.State;
import java.sql.*;
import java.util.Properties;
import java.util.Stack;

public class JDBCUtils {
    private static String drtvename;
    private static String url;
    private static String username;
    private static String password;

    //不论进行数据库说明操作,首先都要加载这些属性。
    // 而需要再类加载时就能加载这四个属性,而且只加载一次就行,
    //使用静态代码块。

    static
    {Properties properties=CommUtil.loadProperties("db.properties");
    //之前单元测试测过,可放心使用
        drtvename=properties.getProperty("drivername");
        url=properties.getProperty("url");
        username=properties.getProperty("username");
        password=properties.getProperty("password");

    //接下来,加载驱动,加载一次即可:
        try {
            Class.forName(drtvename);
        } catch (ClassNotFoundException e) {
            //e.printStackTrace();
        System.err.println("加载数据库驱动出错");

        }}

        //但是获取连接不能放到静态代码块中,因为不同用户使用的连接是不同的。

public static Connection getConnection()

{
    try {
        return DriverManager.getConnection(url,username,password);
    } catch (SQLException e) {
       // e.printStackTrace();
    System.err.println("获取数据库连接出错");
    }
    return null;
}
//关闭数据库资源操作

//方法的重载:一个类中方法名称相同,参数列表不通
    //有时不需要关闭三个资源,用到方法的重载
public static void closeResource(Connection connection,Statement statement)
{
    if(connection!=null) {
        try {
            connection.close();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    if (statement != null) {
        try {
            statement.close();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}



    public static void closeResources(Connection connection, Statement statement, ResultSet resultSet)
    {
        closeResource(connection,statement);
        //在这里调用上面的两个参数的。
        if(resultSet!=null)
        {
            try {
                resultSet.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }}

(在Java中,同一个类中的多个方法可以有相同的方法名称,但是有不同的参数列表,这就称为方法重载(method overloading)。重载是面向对象的一个基本特性。
参数列表又叫参数签名,包括参数的类型、参数的个数、参数的顺序,只要有一个不同就叫做参数列表不同。)

写单元测试:

    @Test
    public void test3() throws SQLException {

        Connection connection=null;
        Statement statement=null;
        ResultSet resultSet=null;

        try{
connection=JDBCUtils.getConnection();
//当使用JDBCUtils这个类时,就会调用它的静态代码块,就先进行了加载驱动
String sql="select * from user";
statement=connection.createStatement();
resultSet=statement.executeQuery(sql);
while (resultSet.next())
{int id=resultSet.getInt("id");
String username=resultSet.getString("username");
String password=resultSet.getString("password");
System.out.println("id为"+id+",用户名为"+username+",密码为"+password);}
}
        catch(SQLException e){}
            finally
        { JDBCUtils.closeResources(connection,statement,resultSet);
        }
    }

运行结果为: 在这里插入图片描述
以上是执行了简单的查询操作的sql语句,使用Statement类,接下来使用PreparedStatement类,写入单元测试:

@Test
    public void test4() throws SQLException {
        Connection connection=null;
        PreparedStatement statement=null;
      ResultSet resultSet=null;

        try{
            connection=JDBCUtils.getConnection();
            String sql="select * from user where id = ? and username = ?";
            statement=connection.prepareStatement(sql);

            statement.setInt(1,1);
            statement.setString(2,"ZS");
            //替代第几个占位符,从1开始

            resultSet=statement.executeQuery();
            while (resultSet.next())
            {int id=resultSet.getInt("id");
                String username=resultSet.getString("username");
                String password=resultSet.getString("password");
                System.out.println("id为"+id+",用户名为"+username+",密码为"+password);}

        }
        catch (SQLException e){}
        finally {
            JDBCUtils.closeResources(connection, statement, resultSet);

        }
    }
}

运行结果为:
在这里插入图片描述
接下来写更新操作:
在pom文件中,我们有导入:
在这里插入图片描述
是apache组织关于编码的工具类(工具类一定是静态方法)。它有个DigestUtils方法,可以采用md5码哈希。
在这里插入图片描述
在单元测试中写,并采用断言:


@Test
public void testInsert()
{Connection connection=null;
PreparedStatement statement=null;
    try{
connection=JDBCUtils.getConnection();
String sql="insert into user (username,password) values(?,?)";
statement=connection.prepareStatement(sql);
    statement.setString(1,"java1");
    statement.setString(2,DigestUtils.md5Hex("java1"));
int influeRow=statement.executeUpdate();

        Assert.assertEquals(1,influeRow);
    }
    catch (SQLException e){}
    finally {
        JDBCUtils.closeResources(connection,statement);
    }}

运行结果为:
在这里插入图片描述
通过测试,并且可以查看user表:
然而这时发现 user表中并没有更新,重新写了类,在主方法中运行,得到以下结果:
在这里插入图片描述
报错的原因是插入值对于password属性来说太长了,因此,修改属性长度:在命令行写入:
alter table user modify column password varchar(100);在这里插入图片描述
这时,再去单元测试中:
在这里插入图片描述
测试通过,再看user表:
在这里插入图片描述
插入成功了。
下面引入一个 概念:DataSource数据源(用于管理数据库连接)。可类比于线程池,线程池中的线程可以复用。之前的Connection可以类比于线程。 这里用到的数据源是DruidDataSource:
(资源复用是系统性能优化中的一种常用手段,如单例,数据库连接池,线程池等都是资源复用的常用技巧。 数据库连接池的基本原理是在内部对象池中维护一定数量的数据库连接,并对外暴露数据库连接获取和返回方法)
(可参考github上托管的源码和中文文档:https://github.com/alibaba/druid
https://github.com/alibaba/druid/wiki/常见问题)
(除了DruidDataSource以外,还有Hakari性能更快,功能较少。)
在pom.xml中已进行过配置:

在这里插入图片描述
需要写入一个配置文件 database.properties:

在这里插入图片描述

driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/jdbc?charset=utf8&useSSL=false
username=root
password=jdpy1229jiajia
filters=stat
initialSize=5
maxActive=30
maxWait=60000
timeBetweenEvictionRunsMillis=60000
minEvictableIdleTimeMillis=300000
validationQuery=SELECT 1
testWhileIdle=true
testOnBorrow=false
testOnReturn=false
poolPreparedStatements=false

接下来,在utils包中新建一个类JDBCUtilsWizGruid:

package com.bit.chatroom.utils;


import com.alibaba.druid.pool.DruidDataSourceFactory;
import com.alibaba.druid.pool.DruidPooledConnection;

import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;

//基于DruidDateSource
public class JDBCUtilsWizDruid
{
private static DataSource dataSource;


static {
    Properties properties=CommUtil.loadProperties("datasource.properties");
    try {

        //注册驱动,连属性也获取完成了。
        dataSource=DruidDataSourceFactory.createDataSource(properties);
    } catch (Exception e) {
        //e.printStackTrace();
    System.err.println("获取数据源失败");
    }
}


public  static DruidPooledConnection getConnection()
{
    try {
        return (DruidPooledConnection) dataSource.getConnection();
    } catch (SQLException e) {
       // e.printStackTrace();
    System.err.println("连接数据库失败");
    }
return null; }

//这里用Connection类,以后传参数可以向上转型,支持更多的方法调用.


    public static void closeResources(Connection connection, Statement statement)
    {
        if(connection!=null)
        {
            try {
                connection.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }

        if(statement!=null)
        {
            try {
                statement.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }

public static void closeResources(Connection connection, Statement statement, ResultSet  resultSet)
{closeResources(connection,statement);
    if(resultSet!=null)
    {
        try {
            resultSet.close();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}
}

对比这个类和之前不使用数据源的类:
这个类获取属性较为简便, 只需要用:
CommUtil.loadProperties(“datasource.properties”)赋给Properties类的properties完成属性的获取,而之前的类还需要给drivename、url、usename、password赋值,此类不需要,接着用DruidDataSourceFactory.createDataSource(properties)赋给DataSource类的dataSourse,即可完成加载驱动,而不像之前的类,使用Class.forname(drivename)。
而获取连接,此类用的是
dataSource.getConnection()赋给DruidPooledConnection;
之前的类用的是DriverManager.getConnection()赋给Connection类。方法名是一样的。而返回值,DruidPooledConnection其实实现了Connection接口。

(通过源码可知 DruidAbstractDataSource是实现了DataSource接口的。

 public abstract class DruidAbstractDataSource extends WrapperAdapter implements DruidAbstractDataSourceMBean, DataSource, DataSourceProxy, Serializable {_

通过源码可知DruidPooledConnection也实现了Connection接口。)

关闭操作都是一样的。

public class DruidPooledConnection extends PoolableWrapper implements PooledConnection, Connection {

写一个单元测试:

 @Test
    @Rollback(false)
    public void testInsertwizGruid()
    {Connection connection=null;
        PreparedStatement statement=null;
        try{
            connection=JDBCUtilsWizDruid.getConnection();
            String sql="insert into user (username,password) values(?,?)";
            statement=connection.prepareStatement(sql);
            statement.setString(1,"java2");
            statement.setString(2,DigestUtils.md5Hex("java2"));
            int influeRow=statement.executeUpdate();

            Assert.assertEquals(1,influeRow);
        }
        catch (SQLException e){}
        finally {
            JDBCUtilsWizDruid.closeResources(connection,statement);
        }}

运行结果为:
在这里插入图片描述
说明加载了一个数据源。
也可以在user表中看到:
在这里插入图片描述
确实插值成功了。
再写一个单元测试,用来测试查找操作:

 @Test
    public void testquerywizGruid() throws SQLException {

        Connection connection=null;
        Statement statement=null;
        ResultSet resultSet=null;

        try{
            connection=JDBCUtilsWizDruid.getConnection();
//当使用JDBCUtils这个类时,就会调用它的静态代码块,就先进行了加载驱动
            String sql="select * from user";
            statement=connection.createStatement();
            resultSet=statement.executeQuery(sql);
            while (resultSet.next())
            {int id=resultSet.getInt("id");
                String username=resultSet.getString("username");
                String password=resultSet.getString("password");
                System.out.println("id为"+id+",用户名为"+username+",密码为"+password);}
        }
        catch(SQLException e){}
        finally
        { JDBCUtilsWizDruid.closeResources(connection,statement,resultSet);
        }
    }

运行结果为:
在这里插入图片描述

接下来再来了解一个库Gson:
Gson是Google提供的用来在Java对象和JSon数据之间进行映射的Java类库。可以将一个Json字符转成一个Java对象,或者将一个Java对象转化为Json字符串。
特点: a、快速、高效
  b、代码量少、简洁
  c、面向对象
 d、数据传递和解析方便)

  (**Json**:JavaScript Object  Notation

是一种传递对象的语法,对象可以是name/value对,数组和其他对象。
Json很像XML:

  • 值都是可列举的
  • 都是有层级的(例如,可以在值里再存放值。)
  • 都能被多种的编程语言解析和使用。

而Json和XML又有不同之处:

  • XML里在元素的开始和结尾处有尖括号和标签名:JSON使用花括号,而且只在数据的开始和结束时使用。
  • JSON更简练,毫无疑问更适合人类书写,也许也能让我们更快速的阅读。
  • JSON可以在JavaScript里简单的传递到eval()方法里使用
  • JSON里有数组{每个元素没有自己的名称}
  • 在XML里你可以对一个元素使用任意想要的名称,在JSON里不能使用Javascript里的保留字

再有对比:
XML方式:

  • 取回一个XML文件
  • 循环它,从中提取值
  • 处理这些值

JSON方式:

  • 取回JSON字符串。
  • ‘eval’ JSON数据

现在的开发是用字符串作传输的。比如用户注册,会有很多参数:username、password、age、picture、brirf…按照原来javaweb的方式,需要通过servlet的request.getParameter(属性名),那么有多少个属性就需要调用多少次方法,现在其实提交参数只是提交一个字符串,所有的属性都是在以一个字符串中,这个字符串就是JSON字符串。
JSON序列化就是将任意对象转化成JSON字符串。
反序列化即将JSON字符串转化成对象。
举个例子:
创建一个类:
在这里插入图片描述
这个类中的属性就对应着数据库中表user的属性,并为其设置get、set方法、toString方法(方法在此省略):

package com.bit.chatroom.entity;
//对应数据表user
public class user {
    private Integer id;
    private String username;
    private String password;}
在CommUtil类中通过GsonBuilder类的create()方法创建一个Gson对象gson,接下来调用gson的 toJson方法,即可把object类的对象转化成String,也就是JSON字符串。

(Gson gson=new GsonBuilder().create();

datasource=DruidDataSourceFactory.createDataSource(properties)
很像,都是"create")

  private static final Gson gson=new GsonBuilder().create();
//和datasource=DruidDataSourceFactory.createDataSource(properties)很像,都是"create"

    public static String objectToJson(Object obj)
    {return gson.toJson(obj);}

    

进行单元测试:

@Test
public void gsaonTest1()
{user user1=new user();
user1.setId(12);
user1.setUsername("xiaoJia");
user1.setPassword("eat");
String Jsonstr=CommUtil.objectToJson(user1);
System.out.println(Jsonstr);
}

运行结果为: 在这里插入图片描述

看到了JSON字符串的样子。
接下来,如果想反序列化:
在CommUtils中写以下方法,传入的参数为JSON字符串和想要转化成的对象的类:

 private static Object jsonToObject(String jsonstr,Class objclass)
    {return gson.fromJson(jsonstr,objclass);}

进行单元测试:

@Test
public void gsonTest2()
{String jsonstr="{\"id\":12,\"username\":\"xiaoJia\",\"password\":\"eat\"}";
user user2= (user) CommUtil.jsonToObject(jsonstr,user.class);
System.out.println(user2);

}

运行结果为:
在这里插入图片描述

发布了47 篇原创文章 · 获赞 1 · 访问量 1293

猜你喜欢

转载自blog.csdn.net/weixin_41750142/article/details/97784194