JDBC

JDBC

1.JDBC概述

JDBC(Java Data Base Connectivity,java数据库连接)是一种用于执行SQL语句的Java API,可以为多种关系数据库提供统一访问,它由一组用Java语言编写的类和接口组成,是Java访问数据库的标准规范。

JDBC提供了一种基准,据此可以构建更高级的工具和接口,使数据库开发人员能够编写数据库应用程序。

JDBC需要连接驱动,驱动是两个设备要进行通信,满足一定通信数据格式,数据格式由设备提供商规定,设备提供商为设备提供驱动软件,通过软件可以与该设备进行通信。

今天我们使用的是mysql的驱动mysql-connector-java-5.1.39-bin.jar

2.JDBC原理

Java提供访问数据库规范称为JDBC,而生产厂商提供规范的实现类称为驱动。

JDBC是接口,驱动是接口的实现,没有驱动将无法完成数据库连接,从而不能操作数据库。每个数据库厂商都需要提供自己的驱动,用来连接自己公司的数据库,也就是说驱动一般都由数据库生产厂商提供。

3.JDBC开发步骤

3.1导入驱动jar包

创建lib目录,用于存放当前项目需要的所有jar包。

选择jar包,右键执行build path / Add to Build Path。

3.2开发步骤

  1. 注册驱动

    告知JVM使用的是哪一个数据库的驱动。

    • Class.forName("com.mysql.jdbc.Driver")

      利用反射技术,将驱动类加入到内存中,通过全限定类名(包名+类名)获得对象,

      Class clazz=Class.forName("全限定类名");

    • DriverManager.registerDriver(new com.mysql.jdbc.Driver())

      不推荐使用这种方式,存在两方面不足:硬编码,后期不易于程序扩展和维护;驱动被注册两次(new Driver执行了源代码中的静态方法,源码中的静态方法也执行了一次注册程序)。

  2. 获得连接

    获取连接需要方法 DriverManager.getConnection(url,username,password)

    url:需要连接数据库的位置(网址);username:用户名;password:密码。

    Connection con=
    DriverManager.getConnection("jdbc:mysql://localhost:3306/rev1_jdbc","root", "root123.");
    

    jdbc:mysql://localhost:3306/rev1_jdbc

    JDBC规定url的格式由三部分组成,每个部分中间使用冒号分隔。

    • 第一部分是jdbc,这是固定的;

    • 第二部分是数据库名称,那么连接mysql数据库,第二部分当然是mysql了;

    • 第三部分是由数据库厂商规定的,由数据库服务器的IP地址(localhost)、端口号(3306),以及DATABASE名称(rev1_jdbc)组成。

  3. 获得语句执行平台

    Statement st=con.createStatement();
    
  4. 执行sql语句

    String sql="select * from sort";
    ResultSet rs=st.executeQuery(sql);//查询
    

    常用方法:

    • int executeUpdate(String sql); 执行insert update delete语句,返回值int表示影响的行数

    • ResultSet executeQuery(String sql); 执行select语句

    • boolean execute(String sql); 执行select返回true,执行其它语句返回false

  5. 处理结果集

    执行insert,update,delete无需处理。

    while(rs.next()) {
    				System.out.println(rs.getInt("sid")+"	"+rs.getString("sname"));
    		}
    

    ResultSet实际上就是一张二维的表格,调用其boolean next()方法指向某行记录,当第一次调用next()方法时,便指向第一行记录的位置。

    常用方法:

    • Object getObject(int index) / Object getObject(String name) :获得任意对象

    • String getString(int index) / Object getObject(String name) :获得字符串

    • int getInt(int index) / Object getObject(String name): 获得整型

    • double getDouble(int index) / Object getObject(String name) :获得双精度浮点型

  6. 释放资源

    与IO流一样,使用后的东西都需要关闭,关闭顺序是先得到的后关闭,后得到的先关闭。

    rs.close();//关闭结果集
    st.close();//关闭sql语句执行平台
    con.close();//关闭连接
    

4.SQL注入问题

假设有登录案例SQL语句如下:

SELECT * FROM 用户表 WHERE NAME = 用户输入的用户名 AND PASSWORD = 用户输的密码;

当用户输入正确的账号与密码后,查询到了信息则让用户登录。但是当用户输入的账号为XXX,密码为:XXX’ OR ‘a’=’a时,则真正执行的代码变为:

SELECT * FROM 用户表 WHERE NAME = ‘XXX’ AND PASSWORD =’ XXX’ OR ’a’=’a’;

此时,上述查询语句时永远可以查询出结果的。那么用户就直接登录成功了,显然我们不希望看到这样的结果,这便是SQL注入问题。

为此,我们使用PreparedStatement预处理对象来解决对应的问题。

4.1预处理对象PreparedStatement

相比较以前的statement, 预先处理给定的sql语句,对其执行语法检查。 在sql语句里面使用 ? 占位符来替代后续要传递进来的变量。 后面进来的变量值,将会被看成是字符串,不会产生任何的关键字。

String sql="select * from user where uname=? and upassword=?";	
PreparedStatement ps=con.prepareStatement(sql);
		/*
		 *  可以直接调用ps对象的setObject方法(比较省事),void setObject(int parameterIndex, Object x), 使用给定对象设置指定参数的值 ,第一个参数代表第几个?,第二个代表?中应填入的值。从左到右数过来,第一个?为1
		 */
ps.setObject(1, "tom");//或ps.setString(1,"tom")
ps.setObject(2, p);

4.2预处理对象executeUpdate方法

  • 开发步骤
    1. 注册驱动
    2. 获取连接
    3. 获取预处理对象
    4. sql语句占位符设置实际参数
    5. 执行sql语句
    6. 释放资源

4.2.1插入记录insert

//向分类表中插入新的分类
public class Test2 {
	public static void main(String[] args) throws Exception {
		//1.注册驱动
		Class.forName("com.mysql.jdbc.Driver");
		//2.获取连接
		Connection con=DriverManager.getConnection("jdbc:mysql://localhost:3306/rev1_jdbc", "root", "root123.");
		//3.获取预处理对象
		String sql="insert into sort values(null,?)";
		PreparedStatement ps=con.prepareStatement(sql);
		//4.设置sql语句参数
		ps.setString(1, "方便面");
		//5.执行sql语句
		int i=ps.executeUpdate();
		System.out.println(i);
		//6.关闭资源
		ps.close();
		con.close();
	}
}

4.2.2更新记录upate

//更新分类表中指定分类ID所对应记录的分类名称
public class Test3 {
	public static void main(String[] args) throws Exception {
		Class.forName("com.mysql.jdbc.Driver");
		Connection con=DriverManager.getConnection("jdbc:mysql://localhost:3306/rev1_jdbc", "root", "root123.");
		String sql="update sort set sname=? where sid=?";
		PreparedStatement ps=con.prepareStatement(sql);
		ps.setString(1, "面包");
		ps.setInt(2, 8);
		int i=ps.executeUpdate();
		System.out.println(i);
		ps.close();
		con.close();
	}
}

4.2.3删除记录delete

//删除分类表中指定分类ID的记录
public class Test4 {
	public static void main(String[] args) throws Exception {
		Class.forName("com.mysql.jdbc.Driver");
		Connection con=DriverManager.getConnection("jdbc:mysql://localhost:3306/rev1_jdbc", "root", "root123.");
		String sql="delete from sort where sid=?";
		PreparedStatement ps=con.prepareStatement(sql);
		ps.setInt(1, 8);
		int i=ps.executeUpdate();
		System.out.println(i);
		ps.close();
		con.close();
	}
}

4.3预处理对象executeQuery方法

通过预处理对象的executeQuery方法,完成记录的select语句的执行。

  • 开发步骤
    1. 注册驱动
    2. 获取连接
    3. 获取预处理对象
    4. sql语句占位符设置实际参数
    5. 执行sql语句
    6. 处理结果集
    7. 释放资源

4.3.1查询记录select

  • 查询分类表所有记录
//查询分类表所有记录
public class Test5 {
	public static void main(String[] args) throws Exception {
		//1.注册驱动
		Class.forName("com.mysql.jdbc.Driver");
		//2.获取连接
		Connection con=DriverManager.getConnection("jdbc:mysql://localhost:3306/rev1_jdbc", "root", "root123.");
		//3.获取预处理对象
		String sql="select * from sort";
		PreparedStatement ps=con.prepareStatement(sql);
		//4.设置sql语句参数
		//5.执行sql语句
		ResultSet rs=ps.executeQuery();
		//6.处理结果集
		while(rs.next()) {
			System.out.println(rs.getInt("sid")+"	"+rs.getString("sname"));
		}
		//7.关闭资源
		rs.close();
		ps.close();
		con.close();
	}
}
  • 查询分类表中指定分类名称的记录
public class Test6 {
	public static void main(String[] args) throws Exception {
		Class.forName("com.mysql.jdbc.Driver");
		Connection con=DriverManager.getConnection("jdbc:mysql://localhost:3306/rev1_jdbc", "root", "root123.");
		String sql="select * from sort where sname=?";
		PreparedStatement ps=con.prepareStatement(sql);
		ps.setString(1, "酒水");
		ResultSet rs=ps.executeQuery();
		while(rs.next()) {
			System.out.println(rs.getInt("sid")+"	"+rs.getString("sname"));
		}
		rs.close();
		ps.close();
		con.close();
	}
}

5.JDBC工具类

获得数据库连接操作,在以后的增删改查所有功能中都存在,可以封装工具类JDBCUtils,提供获取连接对象的方法,从而达到代码的复用。

public class JDBCUtils {
	private static final String DRIVERNAME="com.mysql.jdbc.Driver";
	private static final String URL="jdbc:mysql://localhost:3306/rev1_jdbc";
	private static final String USERNAME="root";
	private static final String PASSWORD="root123.";
	static{
		try {
			Class.forName("DRIVERNAME");
		} catch (ClassNotFoundException e) {
			System.out.println("数据库驱动注册失败");
		}
	}
	public static Connection getConnection() throws SQLException {
		return DriverManager.getConnection(URL, USERNAME, PASSWORD);
	}
}

6.properties配置文件

6.1使用properties配置文件

开发中获得连接的4个参数(驱动、URL、用户名、密码)通常都存在配置文件中,方便后期维护,程序如果需要更换数据库,只需要修改配置文件即可。

properties文件要求:

  • 文件位置:任意,建议src下

  • 文件名称:任意,扩展名为properties

  • 文件内容:一行一组数据,格式是key=value.

    • key命名自定义,如果是多个单词,习惯使用点分隔,如:jdbc.driver

    • value值不支持中文,如果需要使用非英文字符,将进行unicode转换

6.2创建配置文件

在项目src目录下,创建db.properties文件。

driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/rev1_jdbc
user=root
password=root123.

6.3加载配置文件:properties对象

加载properties文件获得流,然后使用Properties对象进行处理。

public class JDBCUtils {	
	private static String driver;
	private static String url;
	private static String user;
	private static String password;
	static {
		try {
			//1.使用Properties处理流
			Properties pro=new Properties();
			//使用类加载器(在src目录下找到这个文件并加载进去)
			/*
			 * 要拿到类加载器,必须先拿到这个类的class对象
			 * ClassLoader getClassLoader() 返回该类的类加载器 
			 *InputStream getResourceAsStream(String name)返回读取指定资源的输入流 
			 */
			InputStream is=
       JDBCUtils.class.getClassLoader().getResourceAsStream("db.properties");
			pro.load(is);
            /*
             或者:
             Reader reader=new FileReader("db.properties");
			 pro.load(reader);
            */
			//2.使用getProperty(key)获取值
			driver=pro.getProperty("driver");
			url=pro.getProperty("url");
			user=pro.getProperty("url");
			password=pro.getProperty("password");
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	public static Connection getConnection() throws Exception {
		Class.forName(driver);
		return DriverManager.getConnection(url, user, password);
	}	
}

7.DBUtils

如果只使用JDBC进行开发,我们会发现冗余代码过多,为了简化JDBC开发,采用apache commons组件一个成员:DBUtils。DBUtils就是JDBC的简化开发工具包。需要项目导入commons-dbutils-1.6.jar才能够正常使用DBUtils工具。

7.1概述

DBUtils是java编程中的数据库操作实用工具,小巧简单实用。

DBUtils封装了对JDBC的操作,简化了JDBC操作,可以少写代码。

Dbutils三个核心功能介绍:

  • QueryRunner中提供对sql语句操作的API

  • ResultSetHandler接口,用于定义select操作后,怎样封装结果集

  • 定义了关闭资源与事务处理的方法

7.2QueryRunner核心类

  • update(Connection conn, String sql, Object... params) ,用来完成表数据的增加、删除、更新操作

  • query(Connection conn, String sql, ResultSetHandler rsh, Object... params) ,用来完成表数据的查询操作

7.3QueryRunner实现增加,删除,修改操作

  • 增加
@Test
public void insert() throws SQLException {
    QueryRunner qr=new QueryRunner();
    String sql="insert into sort values(null,?)";
    String param="办公用品";
    Connection con=JDBCUtils.getConnection();
    int line=qr.update(con,sql, param);
    System.out.println(line);
}
  • 删除
@Test
public void delete() throws SQLException  {
    QueryRunner qr=new QueryRunner();
    String sql="delete from sort where sid=?";
    int param=11;
    Connection con=JDBCUtils.getConnection();
    int line=qr.update(con,sql, param);
    System.out.println(line);
}
  • 修改
@Test
public void update() throws SQLException  {
    QueryRunner qr=new QueryRunner();
    String sql="update sort set sname=? where sid=?";
    Object[] params= {"面包",9};
    Connection con=JDBCUtils.getConnection();
    int line=qr.update(con,sql, params);
    System.out.println(line);
}

7.4QueryRunner实现查询操作

query(Connection conn, String sql, ResultSetHandler rsh, Object... params) ,用来完成表数据的查询操作

7.4.1ResultSetHandler结果集处理类

  • ArrayHandler

将结果集中的第一条记录封装到一个Object[]数组中,数组中的每一个元素就是第一条记录的每一列字段的值。

@Test
public void testArrayHandler() throws Exception {
    QueryRunner qr=new QueryRunner();
    String sql="select * from sort";
    Connection con=JDBCUtils.getConnection();
    Object[] result=qr.query(con, sql, new ArrayHandler());
    for(Object o:result) {
        System.out.println(o);
    }
}
  • ArrayListHandler

将结果集中的每一条记录都封装到一个Object[]数组中,再将这些数组封装到List集合中。

@Test
public void testArrayListHandler() throws Exception {
    QueryRunner qr=new QueryRunner();
    String sql="select * from sort";
    Connection con=JDBCUtils.getConnection();
    List<Object[]> result=qr.query(con, sql, new ArrayListHandler());
    for(Object[] obj:result) {
        for(Object o:obj) {
            System.out.print(o+"	");
        }
        System.out.println();
    }
}
  • BeanHandler

将结果集中第一条记录封装到一个指定的JavaBean中,注意此JavaBean中必须有空参构造。

JavaBean就是一个类,在开发中常用封装数据。具有如下特性:

需要实现接口:java.io.Serializable ,通常实现接口这步骤省略了,不会影响程序;

提供私有字段:private 类型 字段名;

提供getter/setter方法;

提供无参构造。

@Test
public void testBeanHandler() throws Exception {
    QueryRunner qr=new QueryRunner();
    String sql="select * from sort";
    Connection con=JDBCUtils.getConnection();
    Sort result=qr.query(con, sql, new BeanHandler<Sort>(Sort.class));
    System.out.println(result.getSid()+"	"+result.getSname());
}

public class Sort {
	private int sid;
	private String sname;
	public Sort() {
		super();
	}
	public int getSid() {
		return sid;
	}
	public void setSid(int sid) {
		this.sid = sid;
	}
	public String getSname() {
		return sname;
	}
	public void setSname(String sname) {
		this.sname = sname;
	}
}
  • BeanListHandler

将结果集中每一条记录封装到指定的JavaBean中,再将这些JavaBean封装到List集合中。

@Test
public void testBeanListHandler() throws Exception {
    QueryRunner qr=new QueryRunner();
    String sql="select * from sort";
    Connection con=JDBCUtils.getConnection();
    List<Sort> result=qr.query(con, sql, new BeanListHandler<Sort>(Sort.class));
    for(Sort sort:result) {
        System.out.println(sort.getSid()+"	"+sort.getSname());
    }
}
  • ColumnListHandler

将结果集指定列的字段值封装到一个List集合中。

@Test
public void testColumnListHandler() throws Exception {
    QueryRunner qr=new QueryRunner();
    String sql="select * from sort";
    Connection con=JDBCUtils.getConnection();
    List<String> result=qr.query(con, sql, new ColumnListHandler<String>("sname"));
    //sname类型为String,所以填String。如果不确定具体类型,可以写Object

    for(String s:result) {
        System.out.println(s);
    }
}
  • ScalarHandler

只适用于查询后只有单条数据,例如 select count(*) from 表操作。

@Test
public void testScalarHandler() throws Exception {
    QueryRunner qr=new QueryRunner();
    String sql="select count(*) from sort";
    Connection con=JDBCUtils.getConnection();
    Object o=qr.query(con, sql, new ScalarHandler<Object>());
    System.out.println(o);
}
  • MapHandler

将结果集第一行数据封装到Map双列集合中:Map<键,值> 键:列名,值:这列的数据。

@Test
public void testMapHandler() throws Exception {
    QueryRunner qr=new QueryRunner();
    String sql="select * from sort";
    Connection con=JDBCUtils.getConnection();
    Map<String,Object> map=qr.query(con, sql, new MapHandler());
    for(String s:map.keySet()) {
        System.out.print(map.get(s)+"	");
    }
}
  • MapListHandler

将结果集每一行数据封装到一个Map双列集合中,键:列名,值:数据,再将多个Map集合存储到List集合中。

@Test
public void testMapListHandler() throws Exception {
    QueryRunner qr=new QueryRunner();
    String sql="select * from sort";
    Connection con=JDBCUtils.getConnection();
    List<Map<String,Object>> result=qr.query(con, sql, new MapListHandler());
    for(Map<String,Object> map:result) {
        for(String s:map.keySet()) {
            System.out.print(map.get(s)+"	");
        }
        System.out.println();
    }
}

8.连接池

实际开发中“获得连接”和“释放资源”是非常消耗系统资源的两个过程,为了解决此类性能问题,通常采用连接池技术,来共享连接Connection。这样就不需要每次都创建连接、释放连接了,这些操作都交给了连接池。

8.1连接池概述

有了池,就不用自己来创建Connection,而是通过池来获取Connection对象。当使用Connection后,调用Connection的close()方法也不会真的关闭Connection,而是把Connection归还给池,池就可以再利用这个Connection对象了。

Java为数据库连接池提供了公共的接口:javax.sql.DataSource,各个厂商需要让自己的连接池实现这个接口。这样应用程序可以方便的切换不同厂商的连接池。

常见的连接池:DBCP、C3P0。

8.2DBCP连接池

DBCP是一个开源的连接池,是Apache Common成员之一,在企业开发中也比较常见,tomcat内置的连接池。

8.2.1导入jar包

8.2.2编写工具类

DataSource :是java中提供的连接池,作为 DriverManager工具的替代项。在DBCP包中提供了DataSource接口的实现类BasicDataSource类。

public class JDBCUtils {
	private static final String DRIVERNAME="com.mysql.jdbc.Driver";
	private static final String URL="jdbc:mysql://localhost:3306/rev1_jdbc";
	private static final String USERNAME="root";
	private static final String PASSWORD="root123.";	
	//创建连接池
	public static BasicDataSource dataSource=new BasicDataSource();
	//静态代码块,对连接池对象进行基本的配置
	static {
		dataSource.setDriverClassName(DRIVERNAME);
		dataSource.setUrl(URL);
		dataSource.setUsername(USERNAME);
		dataSource.setPassword(PASSWORD);
	}
	public static DataSource getConnection() throws SQLException {
		return dataSource;
	}
}

常见配置项:

8.2.3工具类的使用

  • 增加操作
@Test
public void insert() throws SQLException {
    //获取一个用来执行sql语句的QueryRunner对象
    QueryRunner qr=new QueryRunner(JDBCUtils.getDataSource());
    String sql="insert into sort values(null,?)";
    String param="办公用品";
    int line=qr.update(sql, param);
    System.out.println(line);
}
  • 删除操作
@Test
public void delete() throws SQLException  {
    QueryRunner qr=new QueryRunner(JDBCUtils.getDataSource());
    String sql="delete from sort where sid=?";
    int param=11;
    int line=qr.update(sql, param);
    System.out.println(line);
}
  • 修改操作
@Test
public void update() throws SQLException  {
    QueryRunner qr=new QueryRunner(JDBCUtils.getDataSource());
    String sql="update sort set sname=? where sid=?";
    Object[] params= {"面包",9};
    int line=qr.update(sql, params);
    System.out.println(line);
}
  • 查询操作
@Test
public void query() throws SQLException  {
    QueryRunner qr=new QueryRunner(JDBCUtils.getDataSource());
    String sql="select * from sort where sid>?";
    int param=5;
    List<Sort> list=qr.query(sql, new BeanListHandler<Sort>(Sort.class), param);
    for(Sort sort:list) {
        System.out.println(sort.getSid()+"	"+sort.getSname());
    }
}

8.3C3P0连接池

8.3.1导入jar包

c3p0-0.9.1.2.jar

8.3.2不使用配置文件

public class Test8 {
	private static ComboPooledDataSource dataSource=null;
	static {
		try {
			//1.创建datasource
			dataSource=new ComboPooledDataSource();
			//2.设置连接的数据信息
			dataSource.setDriverClass("com.mysql.jdbc.Driver");
			dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/rev1_jdbc");
			dataSource.setUser("root");
			dataSource.setPassword("root123.");
		} catch (PropertyVetoException e) {
			e.printStackTrace();
		}
	}
	public static Connection getConnection() throws Exception {
		return dataSource.getConnection();
	}
}

8.3.3使用配置文件

c3p0的配置文件支持 properties , 也支持 xml 方式。 不过开发中,一般使用xml方式来配置。

  • 使用db.properties
public class Test9 {
	private static ComboPooledDataSource dataSource=null;
	static {
		try {
			//1.创建datasource
			dataSource=new ComboPooledDataSource();
			//2.设置连接的数据信息
			Properties ps=new Properties();
			InputStream is=JDBCUtils.class.getClassLoader().getResourceAsStream("db.properties");
			ps.load(is);
			dataSource.setDriverClass(ps.getProperty("driver"));
			dataSource.setJdbcUrl(ps.getProperty("url"));
			dataSource.setUser(ps.getProperty("user"));
			dataSource.setPassword(ps.getProperty("password"));
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	public static Connection getConnection() throws Exception {
		return dataSource.getConnection();
	}
}
  • 使用xml

src下, 创建xml文件,名为:c3p0-config.xml,注意此处,名字需要固定了。

<c3p0-config>
    <default-config>
        <property name="driverClass">com.mysql.jdbc.Driver</property>
        <property name="jdbcUrl">jdbc:mysql://localhost/user</property>
        <property name="user">root</property>
        <property name="password">root</property>
  
        <!-- 可选配置 -->
        <property name="initialPoolSize">5</property>
    </default-config>
</c3p0-config>
public class Test10 {
	private static ComboPooledDataSource dataSource=null;
	static {
		try {
			dataSource=new ComboPooledDataSource();
			Properties ps=new Properties();
			Reader r=new FileReader("c3p0-config.xml");
			ps.load(r);
			dataSource.setDriverClass(ps.getProperty("driverClass"));
			dataSource.setJdbcUrl(ps.getProperty("jdbcUrl"));
			dataSource.setUser(ps.getProperty("user"));
			dataSource.setPassword(ps.getProperty("password"));
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	public static Connection getConnection() throws Exception {
		return dataSource.getConnection();
	}
}
原文地址:https://www.cnblogs.com/ALiWang/p/13712594.html