JDBC核心技术学习笔记

深入浅出JDBC核心技术

写在前面:这是我第一次java技术博客,以此来记录自己的学习过程,内心还是有一丢丢小紧张,文笔拙劣,望大佬指点!

1. jdbc的概述

  • 1.1 数据的持久化概念
    • 数据的非持久化:
      我们的程序在运行时,都必须别加载到内存中,然后再内存中保存我们程序中声明的数据,当某个方法执行结束后,就会释放内存中的数据。这样,每次执行和结束方法都会重新分配内存和回收内存中的数据。无法达到数据持久保存的目的。
    • 数据的持久化:
      所谓的数据持久化技术,即数据不会受到具体方法和具体程序的限制,它被预先存储在一个介质上,一般我们将数据以文件的形式存储,或者存储在磁盘上。
      • 相同点:均可以实现数据的持久化存储
      • 不同点:文件存储时,如果文件过于庞大的话,查找指定的文件开始变得复杂繁琐起来,不利于对指定文件进行操作。而如果采用数据库进行存储的话,那么就会变得易于查询和维护起来,我们可以通过sql语句进行相关的增、删、改、查等操作,达到一条语句或者多条语句实现精确处理以及批处理相关数据的目的。
      • 我们一般使用数据库的方式来管理和维护我们的持久化数据。对于数据库,存在关系型数据库(例如MysqlSql serverOracle等)而对于非关系型数据库(例如MongoDB等)
        具体可以参照该文档非关系型数据库和关系型数据库概述
  • 1.2 java中的数据存储技术
    • 通过JDBC(Java Database Connectivy)直接访问数据库
    • 通过JDO(Java Data Object)技术
    • 通过第三方O/R工具,如Hibernate, Mybatis 等
      JDBC是访问数据库的基石,其它的访问方式只是对它的封装以便于简化操作。
  • 1.3 JDBC(Java Database Coonnectivy)介绍
    1. jdbc是一个独立于特定数据库管理系统、通用的sql数据库存取和操作的公共接口定义了用来访问数据库的标准Java类库,(java.sql,javax.sql)使用这些类库可以以一种标准的方法、方便地访问数据库资源。
    2. JDBC为访问不同的数据库提供了一种统一的途径,为开发者屏蔽了一些细节问题。
    3. JDBC的目标是使Java程序员使用JDBC可以连接任何提供了JDBC驱动程序的数据库系统,这样就使得程序员无需对特定的数据库系统的特点有过多的了解,从而大大简化和加快了开发过程。
    4. 如果没有jdbc那么每个厂商都可以定义相关的操作方法,这样会造成对于不同的数据库就会存在不同的操作,不同的厂商也会有不同的实现方法,这样会导致开发不灵活繁杂

2.获取数据库连接

2.1 Driver接口概述

  • java.sql.Driver接口是所有JDBC驱动程序的公共接口,不同的厂商要操作数据库,就必须实现该接口。
  • 在程序中不需要直接去访问实现了 Driver 接口的类,而是由驱动程序管理器类(java.sql.DriverManager)去调用这些Driver实现。
    • Oracle的驱动:oracle.jdbc.driver.OracleDriver
    • mySql的驱动: com.mysql.jdbc.Driver

2.2注册驱动和加载驱动

  • 加载驱动:加载 JDBC 驱动需调用 Class 类的静态方法 forName(),向其传递要加载的 JDBC 驱动的类名

    • Class.forName(“com.mysql.jdbc.Driver”);
  • 注册驱动:DriverManager 类是驱动程序管理器类,负责管理驱动程序

    • 使用DriverManager.registerDriver(com.mysql.jdbc.Driver)来注册驱动

    • 通常不用显式调用 DriverManager 类的 registerDriver() 方法来注册驱动程序类的实例,因为 Driver 接口的驱动程序类包含了静态代码块,在这个静态代码块中,会调用 DriverManager.registerDriver() 方法来注册自身的一个实例。下图是MySQL的Driver实现类的源码

public class Driver extends NonRegisteringDriver implements java.sql.Driver {
     
    public Driver() throws SQLException {
     
    }

    static {
     
        try {
     
        //当加载驱动实现类时,会自动执行static静态代码块。会调用下面方法注册自身的一个实例。
            DriverManager.registerDriver(new Driver());
        } catch (SQLException var1) {
     
            throw new RuntimeException("Can't register driver!");
        }
    }
}

2.3 URL

  • JDBC URL 用于标识一个被注册的驱动程序,驱动程序管理器通过这个 URL 选择正确的驱动程序,从而建立到数据库的连接。

  • JDBC URL的标准由三部分组成,各部分间用冒号分隔。

    • jdbc:子协议:子名称
    • 协议:JDBC URL中的协议总是jdbc
    • 子协议:子协议用于标识一个数据库驱动程序
    • 子名称:一种标识数据库的方法。子名称可以依不同的子协议而变化,用子名称的目的是为了定位数据库提供足够的信息。包含主机名(对应服务端的ip地址),端口号,数据库名
    • 几种常用数据库的 JDBC URL
  • MySQL的连接URL编写方式:

    • jdbc:mysql://主机名称:mysql服务端口号/数据库名称?参数=值&参数=值
    • jdbc:mysql://localhost:3306/yzj
    • jdbc:mysql://localhost:3306/yzj**?useUnicode=true&characterEncoding=utf8**(如果JDBC程序与服务器端的字符集不一致,会导致乱码,那么可以通过参数指定服务器端的字符集)
    • jdbc:mysql://localhost:3306/yzj?user=root&password=123456
      注意:这里如果是本机的数据库,可以省略中间的localhost:3306,直接写为:jdbc:mysql:///yzj

2.4 JDBC获取连接的五种方式

方式一:
使用原始的方法获取连接
说明:上述代码中显式出现了第三方数据库的API

 @Test
//    使用反射方式获取,不会出现任何第三方api,使程序具有更好的可移植性。
    public void test1() throws Exception {
     
//        1.加载mysql驱动,为啥不需要注册驱动?因为该类中的静态代码块已经实现该功能!
        Driver driver=new com.mysql.jdbc.Driver();
//        2.提供数据库地址,jdbc 主协议 mysql子协议,相当于http  localhost 主机名/ip地址 3306:mysql端口号 test为数据库中的一个表名
        String url="jdbc:mysql://localhost:3306/myemployees";
//        3.提供相关信息,比如用户名或者密码
        Properties properties=new Properties();
        properties.setProperty("user","root");
        properties.setProperty("password","root");
        Connection con=driver.connect(url,properties);
        System.out.println(con);
    }

方式二:通过反射获取:
说明:相较于方式一,这里使用反射实例化Driver,不在代码中体现第三方数据库的API。体现了面向接口编程思想。

 @Test
//    使用反射方式获取,不会出现任何第三方api,使程序具有更好的可移植性。
    public void test2() throws Exception {
     
//        1.获取驱动
        Class clazz=Class.forName("com.mysql.jdbc.Driver");
        Driver driver=(Driver)clazz.newInstance();
//        提供数据库地址
        String url="jdbc:mysql://localhost:3306/myemployees";
        //        3.提供相关信息,比如用户名或者密码
        Properties properties=new Properties();
        properties.setProperty("user","root");
        properties.setProperty("password","root");
        Connection con=driver.connect(url,properties);
        System.out.println(con);
    }

方式三:使用驱动程序管理器类来获取连接
说明:使用DriverManager实现数据库的连接。体会获取连接必要的4个基本要素。

@Test
    public void test3() {
     
        try {
     
            //1.数据库连接的4个基本要素:
            String url = "jdbc:mysql://localhost:3306/test";
            String user = "root";
            String password = "abc123";
            String driverName = "com.mysql.jdbc.Driver";

            //2.实例化Driver
            Class clazz = Class.forName(driverName);
            Driver driver = (Driver) clazz.newInstance();
            //3.注册驱动
            DriverManager.registerDriver(driver);
            //4.获取连接
            Connection conn = DriverManager.getConnection(url, user, password);
            System.out.println(conn);
        } catch (Exception e) {
     
            e.printStackTrace();
        }

    }

方式四:使用驱动类自带的静态代码块中注册驱动的功能,可以不用显示注册驱动,然后使用DriverManager来获取连接。
说明:不必显式的注册驱动了。因为在DriverManager的源码中已经存在静态代码块,实现了驱动的注册。

 @Test
//使用DriverManager可以只是加载驱动,不用显示的注册驱动了!
    public void test4() throws Exception{
     
        //    1.提供三个基本的连接信息
        String url="jdbc:mysql://localhost:3306/myemployees";
        String user="root";
        String password="root";
//   2.获取Driver实现类对象,即获取驱动,省略后两步,因为在com.mysql.jdbc.Driver这个实现类中提供了静态
//        注册驱动功能!
        Class clazz=Class.forName("com.mysql.jdbc.Driver");
//        Driver driver=(Driver)clazz.newInstance();
//        注册驱动
        DriverManager.registerDriver(driver);
//        3.获取连接
        Connection conn=DriverManager.getConnection(url,user,password);
        System.out.println(conn);

    }

方式五:最终方式:将数据库的相关信息写入到一个具体的配置文件:database.properties中,默认的路径是src下,不是moudle下。

 @Test
//    获取数据库连接的最终方式:
    /*
    将数据库连接需要的三个数据和加载驱动的类型声明在一个配置文件中,通过读取配置文件的方式,
    获取数据库连接。
    好处:
    1. 实现了数据和代码的分离操作,实现了解耦
    2. 如果需要修改配置文件信息,可以避免程序重新打包。
     */
    public void testFinal() throws Exception{
     
//        获取系统类加载器
//        ClassLoader classLoader = ConnectionTest1.class.getClassLoader();
//        可以替换为不用具体的一个类获取类加载器
        ClassLoader classLoader1=ClassLoader.getSystemClassLoader();
        InputStream in=classLoader1.getResourceAsStream("database.properties");
//        读取四个基本的配置信息
        Properties pros=new Properties();
        pros.load(in);
        String user=pros.getProperty("user");
        String password=pros.getProperty("password");
        String url=pros.getProperty("url");
        String driverClass=pros.getProperty("driverClass");
//        获取驱动,附带自动注册功能,因为静态代码块随着类的加载而执行。
        Class.forName(driverClass);
//        获取连接。
        Connection conn=DriverManager.getConnection(url,user,password);
        System.out.println(conn);

    }
}

其中,配置文件声明在工程的src目录下:【jdbc.properties】

user=root
password=root
url=jdbc:mysql://localhost:3306/myemployees
driverClass=com.mysql.jdbc.Driver

说明:使用配置文件的方式保存配置信息,在代码中加载配置文件

使用配置文件的好处:

①实现了代码和数据的分离,如果需要修改配置信息,直接在配置文件中修改,不需要深入代码
②如果修改了配置信息,省去重新编译的过程。

说明一点:如果mysql的驱动包的版本 的话,可以省略Class.forName(driverClass)操作,因为该包下存在一个文件为:

如果版本低的话还是要写的!!!!!!!!!!
JDBC核心技术学习笔记_第1张图片
该文件内容为:

com.mysql.jdbc.Driver
会自动执行。

3.Orm(Object Relation Mapping)技术

所谓Orm技术,即体现出以下三点:

  • 将数据库表映射为一个具体的javaBean类
  • 将数据库中的一条记录映射为一个具体的该类的对象。
  • 将数据库中的某个字段名映射为该类的一个属性。

4.封装公共的获取连接和释放资源操作

由上面获取连接的五种方式可以得出,最后一种是很好的解决方案,因此可以将其封装在JDBCutils中,实现所有操作表格的公共获取连接操作

public class CommonConnection {
     
    //    封装获取连接的代码
    public static Connection getConnection() {
     
        Connection conn = null;
        try {
     

            ClassLoader classLoader1 = ClassLoader.getSystemClassLoader();
            InputStream in = classLoader1.getResourceAsStream("database.properties");
//        读取四个基本的配置信息
            Properties pros = new Properties();
            pros.load(in);
            String user = pros.getProperty("user");
            String password = pros.getProperty("password");
            String url = pros.getProperty("url");
            String driverClass = pros.getProperty("driverClass");
//        获取驱动,附带自动注册功能,因为静态代码块随着类的加载而执行。
            Class.forName(driverClass);
//        获取连接。
            conn = DriverManager.getConnection(url, user, password);
            System.out.println(password);

        } catch (Exception e) {
     
            System.out.println(e.getMessage());
        } finally {
     
            return conn;
        }


    }

    //    封装关闭连接和PreparedStatement的代码,用于关闭增删改操作资源
    public static void close(Connection conn, Statement ps) {
     

        try {
     
            conn.close();
        } catch (SQLException e) {
     
            e.printStackTrace();
        }
        try {
     
            ps.close();
        } catch (SQLException e) {
     
            e.printStackTrace();
        }

    }
//    重载方法,用于查询操作,关闭ResultSet资源

    public static void close1(Connection conn, Statement ps, ResultSet set) {
     
        try {
     
            conn.close();
        } catch (SQLException e) {
     
            e.printStackTrace();
        }
        try {
     
            ps.close();
        } catch (SQLException e) {
     
            e.printStackTrace();
        }

        try {
     
            set.close();
        } catch (SQLException e) {
     
            e.printStackTrace();
        }
    }
}

5.针对特定表的操作,这里使用order表来说明一些坑

order表的结构如下:

JDBC核心技术学习笔记_第2张图片

JDBC核心技术学习笔记_第3张图片

设计的orderOrm的javaBean类如下:

package com.calarqiang.java;

import java.sql.Date;

/**
 * @author yq
 * @create 2020-07-14-20:31
 * order表的ORM映射类 将该类设计为javabean类
 * 此时演示的是ORM映射类和数据库中表的字段不同的情况
 */
public class OrderORM {
     
    private int orderID;
    private String orderName;
    private Date orderDate;

    public OrderORM() {
     
    }

    public int getOrderID() {
     
        return orderID;
    }

    public void setOrderID(int orderID) {
     
        this.orderID = orderID;
    }

    public String getOrderName() {
     
        return orderName;
    }

    public void setOrderName(String orderName) {
     
        this.orderName = orderName;
    }

    public Date getOrderDate() {
     
        return orderDate;
    }

    public void setOrderDate(Date orderDate) {
     
        this.orderDate = orderDate;
    }

    @Override
    public String toString() {
     
        return "OrderORM{" +
                "orderID=" + orderID +
                ", orderName='" + orderName + '\'' +
                ", orderDate=" + orderDate +
                '}';
    }
}

对order表进行通用的查询操作

package com.calarqiang.JDBCUtils;

import com.calarqiang.java.OrderORM;
import org.junit.Test;

import java.lang.reflect.Field;
import java.sql.*;

/**
 * @author yq
 * @create 2020-07-14-20:36
 * 设计通用的order表的通用查询操作
 */
public class CommonOrderQuery {
     
    public static OrderORM query(String sql, Object... args) {
     
        Connection conn = CommonConnection.getConnection();
        PreparedStatement ps = null;
        ResultSet rs = null;
        try {
     
            ps = conn.prepareStatement(sql);
            for (int i = 0; i < args.length; i++) {
     
                ps.setObject(i + 1, args[i]);
            }
            rs = ps.executeQuery();
//               通过获取结果集的元数据来获取结果集的列数和列名
            ResultSetMetaData metaData = rs.getMetaData();
//               获取结果集的列数
            int columnNums = metaData.getColumnCount();
            if (rs.next()) {
     
                //           创建一个对象接收结果集,封装为一个对象
                OrderORM order = new OrderORM();
                for (int i = 0; i < columnNums; i++) {
     
                    //           获取每一列的数据,来自rs对象
                    Object columnData = rs.getObject(i + 1);
                    //           获取结果集的名字,来自metaData对象
                    //           getColumnName指的是获取查询结果集所属表的列名,不是表列名的别名,可以用getColumnLabel代替
                    String columnName = metaData.getColumnName(i + 1);
                    columnName = metaData.getColumnLabel(i + 1);
//                getColumnLabel既可以获取列的别名,也可以获取原列名,直接代替上面的getColumnName操作
                    //           通过反射来将结果集的值赋值给相应的结果集的名字
                    Field field = OrderORM.class.getDeclaredField(columnName);
                    field.setAccessible(true);
                    field.set(order, columnData);
                }
                return order;
            }
        } catch (Exception e) {
     
            e.printStackTrace();
        } finally {
     
            CommonConnection.close1(conn, ps, rs);
        }
        return null;
    }
}

对order表进行查询测试

package com.calarqiang.java;

import com.calarqiang.JDBCUtils.CommonOrderQuery;

/**
 * @author yq
 * @create 2020-07-14-21:33
 */
public class OrderQueryTest {
     
    public static void main(String[] args) {
     
//        执行时会报错,报错信息为java.lang.NoSuchFieldException: order_id 原因在于,查询的结果集中是order_id
//        而对象中的属性为orderId不匹配,因此要取别名,别名必须和对象的属性名一致!还有一点要注意,这里的order是关键字,如果
//        充当表名,会报sql语法错误。

        /*
        针对于表的字段名和类的属性名不一致的情况:
        1.声明sql时,必须将指定字段的别名指定为对象的属性名
        2.使用getColumnLabel代替getColumnName
        说明:getColumnLabel除了可以获取别名外,也可以获取原类名,直接代替getColumnName
         */
        String sql = "select order_id orderID,order_name,order_date from `order` where order_id=?";
        sql = "select order_id orderID,order_name orderName,order_date orderDate from `order` where order_id=?";
        OrderORM ord = CommonOrderQuery.query(sql, 1);
        System.out.println(ord);
    }
}

通过对order表的操作发现的一系列问题以及总结:
问题:

  1. order表故意命名为数据库中的一个关键字,即order排序关键字,如果在sql语句中直接写此操作,那么会造成错误,错误信息为:
    异常报告图片
    因此要用反引号``将该表的名字引起来,以区分sql中的关键字。
    2.可以注意到我们设计的类的属性名和数据库中的字段名不相同,因此我们进行编写sql语句时,要给要查询的字段命名一个别名,但是,如果只命名为一个别名,而还是使用结果集的元数据的getColumnName获取结果集中的名字时,它默认还是使用的数据库中的字段名,不会使用别名,因此,我们要使用元数据的getColumnLabel代替它,它的作用就是将Sql查询语句中指定的别名映射到数据库表中,然后通过该别名获取对应的类的列名,简而言之,该操作会将数据库查询结果集的字段名自动修改为该类的属性名,达到统一效果。然后根据该属性名,设置指定的属性值。

6.不针对特定表的增删改和查询封装操作

写在前面:下面所有的操作均是建立在前面所使用的封装好的工具类的基础上进行的,即调用了自己实现的JDBCUtils的获取连接的方法和关闭资源的方法。

6.1. 增删改操作

该操作针对任何表的增删改操作,因为增删改都不会查询数据,只会对表进行操作,如果使用execute的话就没有返回值(即不返回受到影响的有多少行),而使用executeUpdate的话,就会返回具体受到影响的行数。

public class CommonModify {
     
    public static void Modify(String sql, Object... args) {
     
//        1.获取连接对象
        Connection conn = CommonConnection.getConnection();
        PreparedStatement ps = null;
//        2.预编译Sql语句,并返回PreparedStatement实例对象
        try {
     
            ps = conn.prepareStatement(sql);
//        3.设置占位符内容
            for (int i = 0; i < args.length; i++) {
     
//                这里一定要注意:1.索引从1开始,而数组从0开始  2.这里的类型设置为Object,通用性。
                ps.setObject(i + 1, args[i]);
            }
//        4.执行sql语句
            ps.execute();
        } catch (SQLException e) {
     
            e.printStackTrace();
        } finally {
     

//        5.关闭连接
            CommonConnection.close(conn, ps);
        }

    }
}


6.2 查询操作(查询单条数据,返回一个具体的对象)

该操作不针对如何表进行查询操作。

public class CommonTableQuery{
     
/*    泛型方法,泛型类型位于权限修饰符和返回值之间
    如果声明为一个含有通配符的普通方法的话,就必须声明该类是一个泛型类,然后初始化这个类的对象时,指定具体的类型
   而泛型方法则在调用该方法时,传入具体的泛型类型
*/
public static <T> T getQuery(Class<T> clazz, String sql, Object... args) {
     
    Connection conn = CommonConnection.getConnection();
    PreparedStatement ps = null;
    ResultSet res = null;
    try {
     
        ps = conn.prepareStatement(sql);
        for (int i = 0; i < args.length; i++) {
     
            ps.setObject(i + 1, args[i]);
        }
        res = ps.executeQuery();
        ResultSetMetaData metaData = res.getMetaData();
        int columnNums = metaData.getColumnCount();
//    利用查询集的元数据获取要查询的列数量和列的别名/名字
        if (res.next()) {
     
            //        根据传入进来的orm查询类创建该类的对象
            T t = clazz.newInstance();
            for (int i = 0; i < columnNums; i++) {
     
                //            获取每一列的数据值
                Object columnData = res.getObject(i + 1);
                //            获取每一列的名字/别名
                String name = metaData.getColumnLabel(i + 1);
                //            根据反射来给与具体的列具体的值,然后将其封装为一个t对象中
                Field field = clazz.getDeclaredField(name);
                //            获取具体的某个字段,如果为私有权限,则设置为可访问
                field.setAccessible(true);
                //            给具体的列(属性名)设置具体的值
                field.set(t, columnData);

            }
            return t;
        }
    } catch (Exception e) {
     
        e.printStackTrace();
    } finally {
     
        CommonConnection.close1(conn, ps, res);
    }
    return null;
}
}

6.3 查询操作(查询多条数据,并用集合封装返回)

该操作,相对于上面的操作,只有些许区别
区别是:

  • 返回类型由单个对象变为 List集合对象
  • if(next) 改为 while(next) 因为有多条数据要遍历
  • 每次开始遍历一个结果集时,都会声明一个对象,遍历完后就会将该结果集获取的对象封装到集合中。
package com.calarqiang.JDBCUtils;

import com.calarqiang.java.OrderORM;
import org.junit.Test;

import java.lang.reflect.Field;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;

/**
 * @author yq
 * @create 2020-07-16-21:32
 * 封装查询表并且返回多条数据的操作
 */
@SuppressWarnings("ALL")
public class CommonQueryList {
     
    public static <T> List<T> getQuery(Class<T> clazz, String sql, Object... args) {
     
//        1.获取连接:
        Connection conn = CommonConnection.getConnection();
        PreparedStatement ps = null;
        ResultSet rs = null;
        List<T> list = null;
        try {
     
//        2.预编译sql语句
            ps = conn.prepareStatement(sql);
//        3.填充sql占位符
            for (int i = 0; i < args.length; i++) {
     
                ps.setObject(i + 1, args[i]);
            }
//        4.获取查询结果集
            rs = ps.executeQuery();
//        5.获取查询结果集的元数据,并通过元数据获取查询结果集的列数
            ResultSetMetaData metaData = rs.getMetaData();
            int colsNums = metaData.getColumnCount();
//        6.创建一个集合对象,用于存储返回的结果集
            list = new ArrayList<>();
//        7.循环结果集,取出一条条结果,并将每条数据封装为一个对象类型,添加到集合中
            while (rs.next()) {
     
                //            通过反射创建一个对象,用于给其赋值
                T t = clazz.newInstance();
                for (int j = 0; j < colsNums; j++) {
     
                    //                读取结果集中的每一条数据,每一个列名
                    Object colData = rs.getObject(j + 1);
                    String name = metaData.getColumnLabel(j + 1);
                    //                利用反射给对象的具体属性设置具体的值
                    Field field = clazz.getDeclaredField(name);
                    field.setAccessible(true);
                    field.set(t, colData);
                }
                list.add(t);
            }
        } catch (Exception e) {
     
            e.printStackTrace();
        } finally {
     
            CommonConnection.close1(conn, ps, rs);
            return list;
        }

    }

测试代码:
注意点:上面我们已经说过,order表的特殊性,再一点就是查询全部记录时,我们必须给每个列指定相应的别名,因此不能直接使用select * 操作

  @Test
    public void test() {
     
        String sql = "select order_id orderID,order_name orderName,order_date orderDate from `order`";
        List<OrderORM> queryList = CommonQueryList.getQuery(OrderORM.class, sql);
        System.out.println(queryList);
    }

7.常见乱码及解决方案

这里只说明mysql字符集

  • mysql字符集问题
  1. mysql默认字符集问题:
    MySQL对于字符集的指定可以细化到一个数据库,一张表,一列,应该用什么字符集。
    但是,传统的 程序在创建数据库和数据表时并没有使用那么复杂的配置,它们用的是默认的配置,那么,默认的配置从何而来呢?
    (1)编译MySQL 时,指定了一个默认的字符集,这个字符集是 latin1;
    (2)安装MySQL 时,可以在配置文件 (my.ini) 中指定一个默认的的字符集,如果没指定,这个值继承自编译时指定的;
    (3)启动mysqld 时,可以在命令行参数中指定一个默认的的字符集,如果没指定,这个值继承自配置文件中的配置,此时 character_set_server 被设定为这个默认的字符集;
    (4)当创建一个新的数据库时,除非明确指定,这个数据库的字符集被缺省设定为character_set_server ;
    (5)当选定了一个数据库时,character_set_database 被设定为这个数据库默认的字符集;
    (6)在这个数据库里创建一张表时,表默认的字符集被设定为 character_set_database ,也就是这个数据库默认的字符集;
    (7)当在表内设置一栏时,除非明确指定,否则此栏缺省的字符集就是表默认的字符集;

简单的总结一下,如果什么地方都不修改,那么所有的数据库的所有表的所有栏位的都用 latin1 存储,不过我们如果安装 MySQL,一般都会选择多语言支持,也就是说,安装程序会自动在配置文件中把 default_character_set 设置为 UTF-8,这保证了缺省情况下,所有的数据库的所有表的所有栏位的都用 UTF-8 存储。
2. 查看默认字符集的方式:

使用模糊查询:show variables like 'charar%';

JDBC核心技术学习笔记_第4张图片
可以看到这里我的字符编码都是按照系统默认的编码格式,即latin1,因此我们可以通过两种方式来改变编码:

  • 方式一:通过sql命令

该方式存在弊端:即重启完mysql后修改的值就失效。

JDBC核心技术学习笔记_第5张图片

  • 方式二:通过修改配置文件

修改mysql的my.ini文件中的字符集键值
老版本设置方式:default-character-set=utf8
新版本设置方式:character_set_server=utf8
可以两个均试试,具体版本不明确!
然后就是重启服务(service mysql restart),即可生效。

其它编码注意点:
我们使用jdbc连接数据库时,如果mysql的字符集没有设置好的话,就可以添加如下命令:

useUnicode表示允许使用自定义的Unicode,
characterEncoding是给定自定义的Unicode是什么
String url=“jdbc:mysql://localhost:3306/yzj?useUnicode=true&characterEncoding=utf8”

这样就可以保证mysql的字符集为utf8,所以写上可以保险,避免没有设置好mysql编码后,它默认用自己的编码解析我们用utf-8传递过去的数据。
还有一点,就是我们的集成开发环境的字符编码也要设置为utf-8,这样保证编码统一!

8.总结

这篇博客是自己学习了视频,并且动手实操后,写下的文章,其中参考了部分博主文章,有的知识点用链接的形式贴出来了,大家可以看看,最后,写的不好的地方,希望大神不吝赐教!

你可能感兴趣的:(jdbc,java,数据库)