JDBC学习笔记

来源:互联网 发布:windows重置此电脑失败 编辑:程序博客网 时间:2024/05/29 04:54

JDBC简介

数据库驱动

数据库厂商为了方便开发人员从程序中操作数据库而提供的一套jar包,导入这个jar包就可以调用其中的方法操作数据库,这样的jar包就叫做数据库驱动。

JDBC

SUN公司为了简化、统一对数据库的操作,定义了一套Java操作数据库的规范,称之为JDBC。
JDBC全称为:Java DataBase Connectivity(java数据库连接),它主要由接口组成。
组成JDBC的2个包(均包含在J2SE里):
java.sql
javax.sql
开发JDBC应用除了需要以上2个包的支持外,还需要导入相应JDBC的数据库实现(即数据库驱动)。

jdbc

JDBC快速入门

首先利用cmd命令建立一个数据库,新建一个user表并写入三条数据。代码如下:

create table user(    id int primary key auto_increment,    name varchar(40),    password varchar(40),    email varchar(60),    birthday date)character set utf8 collate utf8_general_ci;insert into user(name,password,email,birthday) values('zs','123456','zs@sina.com','1980-12-04');insert into user(name,password,email,birthday) values('lisi','123456','lisi@sina.com','1981-12-04');insert into user(name,password,email,birthday) values('wangwu','123456','wangwu@sina.com','1979-12-04');

建好数据库和表之后,再新建一个Java工程,工程建好后需要新建一个lib文件夹导入MySQL的驱动包mysql-connector-java-5.0.8-bin.jar,并添加到BuildPath中。

准备工作完成以后,新建一个类JDBCDemo1.java,用来实现对user表的查询。基本的代码如下:

package me.zipstream.jdbc;import java.sql.Connection;import java.sql.DriverManager;import java.sql.ResultSet;import java.sql.SQLException;import java.sql.Statement;import com.mysql.jdbc.Driver;public class JDBCDemo1 {    public static void main(String[] args) throws SQLException {        //1,注册数据库驱动        DriverManager.registerDriver(new Driver());        //2,获取数据库连接        Connection conn =             DriverManager.getConnection("jdbc:mysql://localhost:3306/day10", "root", "root");        //3,获取传输器对象        Statement statement = conn.createStatement();        //4,利用传输器对象传输SQL语句到数据库中执行,获取结果集对象        ResultSet rs = statement.executeQuery("select * from user");        //5,遍历结果集获取查询结果        while (rs.next()){            String name = rs.getString("name");            System.out.println(name);        }        //6,关闭资源        rs.close();        statement.close();        conn.close();    }}

程序的细节和优化

DriverManager

JDBC程序中的DriverManager用于加载驱动,并创建与数据库的连接,这个API的常用方法有:

DriverManager.registerDriver(new Driver());DriverManager.getConnection(String url, String user, String password);

注意:在实际开发中并不推荐采用registerDriver方法注册驱动。原因有二:
1、查看Driver的源代码可以看到,如果采用此种方式,会导致驱动程序注册两次,也就是在内存中会有两个Driver对象。
2、程序依赖mysql的api,脱离mysql的jar包,程序将无法编译,将来程序切换底层数据库将会非常麻烦。

推荐方式:

Class.forName(“com.mysql.jdbc.Driver”);

采用此种方式不会导致驱动对象在内存中重复出现,并且程序仅仅只需要一个字符串,不需要依赖具体的驱动,使程序的灵活性更高。

同样,在开发中也不建议采用具体的驱动类型指向getConnection方法返回的connection对象。正所谓面向接口编程,在导包的时候尽量不要导入与具体数据库有关的实现类,而是要导入接口。

附:Driver的源代码

package com.mysql.jdbc;import java.sql.SQLException;public class Driver extends NonRegisteringDriver implements java.sql.Driver {    static {        try {            java.sql.DriverManager.registerDriver(new Driver());        } catch (SQLException E) {            throw new RuntimeException("Can't register driver!");        }    }    public Driver() throws SQLException {        // Required for Class.forName().newInstance()    }}

数据库URL

URL用于标识数据库的位置,程序员通过URL地址告诉JDBC程序连接哪个数据库,URL的写法为:
jdbc:mysql:[]//localhost:3306/test ?参数名:参数值
常用数据库URL地址的写法:
Oracle写法:jdbc:oracle:thin:@localhost:1521:sid
SqlServer写法:jdbc:microsoft:sqlserver://localhost:1433; DatabaseName=sid
MySql写法:jdbc:mysql://localhost:3306/sid
Mysql的url地址的简写形式: jdbc:mysql:///sid
url可以接的参数
userpassword
useUnicode=true&characterEncoding=UTF-8

Connection

JDBC程序中的Connection,代表数据库的连接,Connection是数据库编程中最重要的一个对象,客户端与数据库所有交互都是通过Connection对象完成的,这个对象的常用方法有:

  • createStatement():创建向数据库发送sql的statement对象。
  • prepareStatement(sql) :创建向数据库发送预编译sql的PrepareSatement对象。
  • prepareCall(sql):创建执行存储过程的callableStatement对象。
  • setAutoCommit(boolean.autoCommit):设置事务是否自动提交。
  • commit():在链接上提交事务。
  • rollback():在此链接上回滚事务。

Statement

JDBC程序中的Statement对象用于向数据库发送SQL语句,Statement对象常用方法有:
- executeQuery(String.sql):用于向数据发送查询语句。
- executeUpdate(String.sql):用于向数据库发送insert、update或delete语句
- execute(String sql):用于向数据库发送任意sql语句
- addBatch(String.sql):把多条sql语句放到一个批处理中。
- executeBatch():向数据库发送一批sql语句执行。

ResultSet

JDBC程序中的ResultSet用于代表Sql语句的执行结果。Resultset封装执行结果时,采用的类似于表格的方式。ResultSet对象维护了一个指向表格数据行的游标,初始的时候,游标在第一行之前,调用ResultSet.next()方法,可以使游标指向具体的数据行,进行调用方法获取该行的数据。
ResultSet用于封装执行结果,所以该对象提供的都是用于获取数据的get方法:
获取任意类型的数据:
getObject(int index)
getObject(string columnName)
其实以上两个方法主要是给框架设计者使用的。

获取指定类型的数据,例如:
getString(int index)
getString(String columnName)

常用的数据类型转换表:
转换表

ResultSet还提供了对结果集进行滚动的方法(操作游标):
- next():移动到下一行
- Previous():移动到前一行
- absolute(int row):移动到指定行
- beforeFirst():移动resultSet的最前面。
- afterLast():移动到resultSet的最后面。

释放资源

JDBC程序运行完后,切记要释放程序在运行过程中,创建的那些与数据库进行交互的对象,这些对象通常是ResultSet, Statement和Connection对象。

特别是Connection对象,它是非常稀有的资源,用完后必须马上释放,如果Connection不能及时、正确的关闭,极易导致系统宕机。Connection的使用原则是尽量晚创建,尽量早的释放。

为确保资源释放代码能运行,资源释放代码一定要放在finally语句中。

释放时后创建的先释放。

最终优化后的代码

package me.zipstream.jdbc;import java.sql.Connection;import java.sql.DriverManager;import java.sql.ResultSet;import java.sql.SQLException;import java.sql.Statement;public class JDBCDemo1 {    public static void main(String[] args) {        Connection conn = null;        Statement statement = null;        ResultSet rs = null;        try{            //1,注册数据库驱动            Class.forName("com.mysql.jdbc.Driver");            //2,获取数据库连接            conn =                 DriverManager.getConnection("jdbc:mysql:///day10?user=root&password=root");            //3,获取传输器对象            statement = conn.createStatement();            //4,利用传输器对象传输SQL语句到数据库中执行,获取结果集对象            rs = statement.executeQuery("select * from user");            //5,遍历结果集获取查询结果            while (rs.next()){                String name = rs.getString("name");                System.out.println(name);            }        } catch (Exception e) {            e.printStackTrace();        } finally{            //6,关闭资源            if (rs != null){                try {                    rs.close();                } catch (SQLException e) {                    e.printStackTrace();                } finally{                    rs = null;                }            }            if (statement != null){                try {                    statement.close();                } catch (SQLException e) {                    e.printStackTrace();                } finally{                    statement = null;                }            }            if (conn != null){                try {                    conn.close();                } catch (SQLException e) {                    e.printStackTrace();                } finally{                    conn = null;                }            }                   }           }}

使用JDBC对数据库进行CRUD

准备工作

为了提高程序的复用性,减少书写重复代码,提高代码的灵活性。新建一个配置文件config.properties

driver=com.mysql.jdbc.Driverurl=jdbc:mysql:///day10user=rootpassword=root

再新建一个工具类JDBCUtils.java,实现获取连接和关闭资源的功能。

package me.zipstream.utils;import java.io.FileReader;import java.sql.Connection;import java.sql.DriverManager;import java.sql.ResultSet;import java.sql.SQLException;import java.sql.Statement;import java.util.Properties;public class JDBCUtils {    private static Properties prop = null;    private JDBCUtils(){}    static{        prop = new Properties();        try {            prop.load(new FileReader(JDBCUtils.class.getClassLoader().getResource("config.properties").getPath()));        } catch (Exception e) {            e.printStackTrace();            throw new RuntimeException(e);        }    }    /**     * 获取连接     * @return 对数据库的连接     * @throws ClassNotFoundException     * @throws SQLException     */    public static Connection getConn() throws ClassNotFoundException, SQLException{        Class.forName(prop.getProperty("driver"));        return DriverManager.getConnection(prop.getProperty("url"), prop.getProperty("user"), prop.getProperty("password"));    }    /**     * 关闭资源     * @param rs     * @param st     * @param conn     */    public static void close(ResultSet rs, Statement st, Connection conn){        if (rs != null){            try {                rs.close();            } catch (Exception e) {                e.printStackTrace();            } finally{                rs = null;            }        }        if (st != null){            try {                st.close();            } catch (Exception e) {                e.printStackTrace();            } finally{                st = null;            }        }        if (conn != null){            try {                conn.close();            } catch (Exception e) {                e.printStackTrace();            } finally{                conn = null;            }        }    }}

CRUD操作-create

想user表中增加一个记录。代码如下:

@Testpublic void add(){    Connection conn = null;    Statement st = null;    try {        conn = JDBCUtils.getConn();        st = conn.createStatement();        int count =             st.executeUpdate("insert into user values(null,'zhaoliu','123456','zhaoliu@qq.com','1999-09-09')");        if (count > 0){            System.out.println("操作成功!影响到"+ count +"行数据!");        } else{            System.out.println("操作失败!");        }    } catch (Exception e) {        e.printStackTrace();    } finally{        JDBCUtils.close(null, st, conn);    }}

CRUD操作-updata

@Testpublic void update(){    Connection conn = null;    Statement st = null;    try{        conn = JDBCUtils.getConn();        st = conn.createStatement();        st.executeUpdate("update user set password=999 where name='zhaoliu'");    } catch (Exception e) {        e.printStackTrace();    } finally{        JDBCUtils.close(null, st, conn);    }}

CRUD操作-read

@Testpublic void find(){    Connection conn = null;    Statement st = null;    ResultSet rs = null;    try{        conn = JDBCUtils.getConn();        st = conn.createStatement();        rs = st.executeQuery("select * from user where name='zhaoliu'");        while (rs.next()){            String name = rs.getString("name");            String password = rs.getString("password");            System.out.println(name + ": " + password);        }    } catch (Exception e) {        e.printStackTrace();    } finally{        JDBCUtils.close(rs, st, conn);    }}

CRUD操作-delete

@Testpublic void delete(){    Connection conn = null;    Statement st = null;    try{        conn = JDBCUtils.getConn();        st = conn.createStatement();        st.executeUpdate("delete from user where name='zhaoliu'");    } catch (Exception e) {        e.printStackTrace();    } finally{        JDBCUtils.close(null, st, conn);    }}

PreparedStatement

SQL注入攻击

SQL 注入是用户利用某些系统没有对输入数据进行充分的检查,从而进行恶意破坏的行为。由于dao中执行的SQL语句是拼接出来的,其中有一部分内容是由用户从客户端传入,所以当用户传入的数据中包含sql关键字时,就有可能通过这些关键字改变sql语句的语义,从而执行一些特殊的操作,这样的攻击方式就叫做sql注入攻击。典型的手段例如:
username' or '1=1
username'#
就可以不输入密码而登录了。

PreparedStatement的使用

PreparedStatementStatement的孩子,不同的是,PreparedStatement使用预编译机制,在创建PreparedStatement对象时就需要将sql语句传入,传入的过程中参数要用?替代,这个过程回导致传入的sql被进行预编译,然后再调用PreparedStatementsetXXX将参数设置上去,由于sql语句已经经过了预编译,再传入特殊值也不会起作用了。从而有效防止SQL注入攻击。

代码样例:

package me.zipstream.jdbc;import java.sql.Connection;import java.sql.PreparedStatement;import java.sql.ResultSet;import me.zipstream.utils.JDBCUtils;public class JDBCDemo3 {    public static void main(String[] args) {        Connection conn = null;        PreparedStatement ps = null;        ResultSet rs = null;        try{            conn = JDBCUtils.getConn();            ps = conn.prepareStatement("select * from user where name=? and password=?");            ps.setString(1, "zs");            ps.setString(2, "123456");            rs = ps.executeQuery();            while (rs.next()){                System.out.println(rs.getString("email"));            }        } catch (Exception e) {            e.printStackTrace();        } finally{            JDBCUtils.close(rs, ps, conn);        }    }}

使用JDBC处理大数据

在实际开发中,程序需要把大文本Text或二进制数据Blob保存到数据库。
注:Text是mysql叫法,Oracle中叫Clob

基本概念:大数据也称之为LOB(Large Objects),LOB又分为:
Clob和Blob
Clob用于存储大文本。
Blob用于存储二进制数据,例如图像、声音、二进制文等。

对MySQL而言只有Blob,而没有Clob,mysql存储大文本采用的是Text
Text分为:
TINYTEXT(255)、TEXT(64k)、MEDIUMTEXT(16M)和LONGTEXT(4G)

Blob分为:
TINYBLOB(255)、BLOB(64k)、MEDIUMBLOB(16M)和LONGBLOB(4G)

使用JDBC处理大文本

首先在数据库中新建一个表,用来存储一个10.3M的文本数据。代码如下:

create table textdemo(    id int primary key auto_increment,    name varchar(100),    content MEDIUMTEXT);

这里还需要修改执行程序虚拟机的最大和最小内存以及MySQL数据库存储文件的大小。在IDE的Run Dialog窗口的Argument选项卡中添加:
-Xms64m
-Xmx256m

来修改虚拟机内存。
在MySQL的my.ini文件的[mysqld]下添加:
max_allowed_packet=64M
修改MySQL存储文件的大小。

大文本的存储

@Testpublic void addText(){    Connection conn = null;    PreparedStatement ps = null;    try{        conn = JDBCUtils.getConn();        ps = conn.prepareStatement("insert into textdemo values(null, ?, ?)");        ps.setString(1, "藏地密码");        File file = new File("1.txt");        ps.setCharacterStream(2, new FileReader(file), (int) file.length());        ps.executeUpdate();    } catch (Exception e){        e.printStackTrace();    } finally{        JDBCUtils.close(null, ps, conn);    }}

大文本的查询

@Testpublic void findText(){    Connection conn = null;    PreparedStatement ps = null;    ResultSet rs = null;    try{        conn = JDBCUtils.getConn();        ps = conn.prepareStatement("select * from textdemo where id=?");        ps.setInt(1, 1);        rs = ps.executeQuery();        while (rs.next()){            String filename = rs.getString("name");            Reader reader = rs.getCharacterStream("content");            Writer writer = new FileWriter(filename);            char[] cs = new char[1024];            int i = 0;            while ((i=reader.read(cs))!=-1){                writer.write(cs, 0, i);            }            reader.close();            writer.close();        }    } catch (Exception e){        e.printStackTrace();    } finally{        JDBCUtils.close(rs, ps, conn);    }}

使用JDBC处理大二进制数据

方法与处理大文本并无两样。只要把setCharacterStreamgetCharacterStream替换为setBinaryStreamgetBinaryStream就可以了。

使用JDBC进行批处理

业务场景:当需要向数据库发送一批SQL语句执行时,应避免向数据库一条条的发送执行,而应采用JDBC的批处理机制,以提升执行效率。

Statement方式执行批处理

优点:可以执行多条不同结构的sql语句
缺点:没有使用预编译机制,效率低下,如果要执行多条结构相同仅仅参数不同的sql时,仍然需要写多次sql语句的主干
例子:一段代码完成创建数据库新建表和插入数据的操作

package me.zipstream.batch;import java.sql.Connection;import java.sql.Statement;import me.zipstream.utils.JDBCUtils;public class StatementBatch {    public static void main(String[] args) {        Connection conn = null;        Statement st = null;        try{            conn = JDBCUtils.getConn();            st = conn.createStatement();            st.addBatch("create database day10batch");            st.addBatch("use day10batch");            st.addBatch("create table batchDemo("+                    "id int primary key auto_increment,"+                    "name varchar(20)"+                    ")");            st.addBatch("insert into batchDemo values(null,'aaaa')");            st.addBatch("insert into batchDemo values(null,'bbb')");            st.addBatch("insert into batchDemo values(null,'cc')");            st.addBatch("insert into batchDemo values(null,'d')");            st.executeBatch();        } catch (Exception e){            e.printStackTrace();        } finally{            JDBCUtils.close(null, st, conn);        }    }}

prparedStatement方式执行批处理

优点:有预编译机制,效率比较高.执行多条结构相同,参数不同的sql时,不需要重复写sql的主干
缺点:只能执行主干相同参数不同的sql,没有办法在一个批中加入结构不同的sql
例子:向psbatch表中存储10000条数据。

package me.zipstream.batch;import java.sql.Connection;import java.sql.PreparedStatement;import java.sql.Statement;import me.zipstream.utils.JDBCUtils;public class PreparedStatementBatch {    public static void main(String[] args) {        Connection conn = null;        PreparedStatement ps = null;        try{            conn = JDBCUtils.getConn();            ps = conn.prepareStatement("insert into psbatch values(null,?)");            for (int i=1; i<=10000; i++){                ps.setString(1, "name"+i);                ps.addBatch();                if (i%1000 == 0){                    ps.executeBatch();                    ps.clearBatch();                }            }            ps.executeBatch();        } catch (Exception e){            e.printStackTrace();        } finally{            JDBCUtils.close(null, ps, conn);        }    }}
0 0
原创粉丝点击