JDBC核心技术

一、JDBC入门

1 JDBC概述

1.1 数据的持久化

  • 持久化(persistence):把数据保存到可掉电式存储设备中以供之后使用。大多数情况下,特别是企
    业级应用,数据持久化意味着将内存中的数据保存到硬盘上加以”固化”,而持久化的实现过程大多
    通过各种关系数据库来完成。
  • 持久化的主要应用是将内存中的数据存储在关系型数据库中,当然也可以存储在磁盘文件、XML数
    据文件中。

1.2 Java中的数据存储技术

  • 在Java中,数据库存取技术可分为如下几类:
    JDBC直接访问数据库
    JDO (Java Data Object )技术
    第三方O/R工具,如Hibernate, Mybatis 等
  • JDBC是java访问数据库的基石,JDO、Hibernate、MyBatis等只是更好的封装了JDBC。

1.3 JDBC介绍

  • JDBC(Java Database Connectivity)是一个独立于特定数据库管理系统、通用的SQL数据库存取和操
    作的公共接口(一组API),定义了用来访问数据库的标准Java类库,(java.sql,javax.sql)使用
    这些类库可以以一种标准的方法、方便地访问数据库资源。
  • JDBC为访问不同的数据库提供了一种统一的途径,为开发者屏蔽了一些细节问题。
  • JDBC的目标是使Java程序员使用JDBC可以连接任何提供了JDBC驱动程序的数据库系统,这样就使
    得程序员无需对特定的数据库系统的特点有过多的了解,从而大大简化和加快了开发过程。
  • 如果没有JDBC,那么Java程序访问数据库时是这样的:

    有了JDBC,Java程序访问数据库时是这样的:

2 JDBC编写步骤


:ODBC(Open Database Connectivity,开放式数据库连接),是微软在Windows平台下推出的。
使用者在程序中只需要调用ODBC API,由 ODBC 驱动程序将调用转换成为对特定的数据库的调用请求。

二、JDBC基本操作

:这里是基于MySql5版本,不适用于MySql8版本。
准备工作:

  1. 在工程下新建lib文件夹
  2. mysql-connector-java-5.1.47.jar导入到lib目录下
  3. 把导入的jar包添加到项目中
  4. 新建package及测试类
//包名
com.bzcxy.jdbc
//测试类名
Jdbc_Test
  1. 在数据库中新建baizhan数据库

1 获取数据库连接的方式

:Connection的包为java.sql.Connection

1.1 连接重点

Driver接口

  • java.sql.Driver 接口是所有 JDBC 驱动程序需要实现的接口。这个接口是提供给数据库厂商使用
    的,不同数据库厂商提供不同的实现。
  • 在程序中不需要直接去访问实现了 Driver 接口的类,而是由驱动程序管理器类
    (java.sql.DriverManager)去调用这些Driver实现。
    Oracle的驱动:oracle.jdbc.driver.OracleDriver
    MySql的驱动: com.mysql.jdbc.Driver

加载与注册JDBC驱动

  • 加载驱动:加载 JDBC 驱动需调用 Class 类的静态方法 forName(),向其传递要加载的 JDBC 驱动
    的类名
    Class.forName(“com.mysql.jdbc.Driver”);
  • 注册驱动:DriverManager 类是驱动程序管理器类,负责管理驱动程序
    使用DriverManager.registerDriver(com.mysql.jdbc.Driver)来注册驱动
    通常不用显式调用 DriverManager 类的 registerDriver() 方法来注册驱动程序类的实例,因为 Driver 接口的驱动程序类都包含了静态代码块,在这个静态代码块中,会调用DriverManager.registerDriver()方法来注册自身的一个实例。

URL

  • JDBC URL 用于标识一个被注册的驱动程序,驱动程序管理器通过这个 URL 选择正确的驱动程序,
    从而建立到数据库的连接。
  • JDBC URL的标准由三部分组成,各部分间用冒号分隔。
    jdbc:子协议:子名称
    协议:JDBC URL中的协议总是jdbc
    子协议:子协议用于标识一个数据库驱动程序
    子名称:一种标识数据库的方法。子名称可以依不同的子协议而变化,用子名称的目的是为了
    定位数据库提供足够的信息。包含主机名(对应服务端的ip地址),端口号,数据库名

MySQL的连接URL编写方式

  • jdbc:mysql://主机名称:mysql服务端口号/数据库名称?参数=值&参数=值
  • jdbc:mysql://localhost:3306/baizhan
  • jdbc:mysql://localhost:3306/baizhan?useSSL=false(MySql5.7之后,默认开启SSL,通过参数指定连接不使用SSL)
  • jdbc:mysql://localhost:3306/baizhan?
    useUnicode=true&characterEncoding=utf8(如果JDBC程序与服务器端的字符集不一致,会导致乱码,那么可以通过参数指定服务器端的字符集)
  • jdbc:mysql://localhost:3306/baizhan?user=root&password=123456

1.2 获取连接

方式一

public class Jdbc_Test {public static void main(String[] args) throws SQLException {//获取driver实现类的对象Driver driver = new com.mysql.jdbc.Driver();String url = "jdbc:mysql://localhost:3306/baizhan?useSSL=false";//把数据库的用户名和密码封装在Properties中Properties info = new Properties();info.setProperty("user","root");info.setProperty("password","root");Connection conn = driver.connect(url,info);System.out.println(conn);}
}

如果连接失败,会报错
方式二
对方式一的迭代:在如下的程序中不会出现第三方的api,使得程序具有更好的移植性

public class Jdbc_Test2 {public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, SQLException {//获取driver实现类的对象反射Class clazz = Class.forName("com.mysql.jdbc.Driver");Driver driver = (Driver) clazz.newInstance();//2.提供要连接的数据库String url = "jdbc:mysql://localhost:3306/baizhan?useSSL=false";//3.提供用户密码Properties info = new Properties();info.setProperty("user","root");info.setProperty("password","root");//4.获取链接Connection conn = driver.connect(url,info);System.out.println(conn);}
}

方式三
使用DriverManager替换Driver

public class Jdbc_Test3 {//DriverManager 代替 Driverpublic static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, SQLException {//1.获取Driver的实现类Class clazz = Class.forName("com.mysql.jdbc.Driver");Driver driver = (Driver) clazz.newInstance();//2. 提供另外三个获取连接信息String url = "jdbc:mysql://localhost:3306/baizhan?useSSL=false";String user = "root";String password = "root";//注册到DriverManagerDriverManager.registerDriver(driver);//获取连接Connection conn = DriverManager.getConnection(url,user,password);System.out.println(conn);}
}

方式四
MySql驱动默认会进行驱动的注册

public class Jdbc_Test4 {public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, SQLException {//Driver driver = (Driver) clazz.newInstance();//注册到DriverManager// DriverManager.registerDriver(driver);//1 提供三个获取连接信息String url = "jdbc:mysql://localhost:3306/baizhan?useSSL=false";String user = "root";String password = "root";//2.加载Driver不用显示注册驱动Class.forName("com.mysql.jdbc.Driver");//3.获取连接Connection conn = DriverManager.getConnection(url,user,password);System.out.println(conn);}
}

方式五(推荐)
将数据库链接需要的4个基本信息声明在配置文件中,通过读取配置文件的方式。
src下创建jdbc.properties文件,并写入

user=root
password=root
url=jdbc:mysql://localhost:3306/baizhan
driverClass=com.mysql.jdbc.Driver
public class Jdbc_Test5 {public static void main(String[] args) throws IOException, ClassNotFoundException, SQLException {//1 读取配置文件中4个基本信息InputStream is = Jdbc_Test5.class.getClassLoader().getResourceAsStream("jdbc.properties");Properties properties = new Properties();properties.load(is);String user = properties.getProperty("user");String password = properties.getProperty("password");String url = properties.getProperty("url");String driverClass = properties.getProperty("driverClass");//2 加载驱动Class.forName(driverClass);//3 获取链接Connection conn = DriverManager.getConnection(url,user,password);System.out.println(conn);}
}

优点:

  1. 实现数据与代码的分离 ,实现了解耦
  2. 如果要修改配置文件信息,可以避免程序重打包

2 Statement

2.1 操作和访问数据库

数据库连接被用于向数据库服务器发送命令和 SQL 语句,并接受数据库服务器返回的结果。其实一个数
据库连接就是一个Socket连接。
在 java.sql 包中有 3 个接口分别定义了对数据库的调用的不同方式:

  • Statement:用于执行静态 SQL 语句并返回它所生成结果的对象。
  • PreparedStatement:SQL 语句被预编译并存储在此对象中,可以使用此对象多次高效地执行该语
    句。
  • CallableStatement:用于执行 SQL 存储过程。

2.2 Statement使用

导入user.sql数据

  • 调用Connection对象的createStatement()方法创建Statement对象,
  • 调用以下方法执行SQL语句
int execute(String sql); //执行insert、update、delete操作
ResultSet executeQuery(String sql); //执行查询操作

执行insert

public class Statement_Test {public static void main(String[] args) throws IOException, SQLException, ClassNotFoundException {InputStream is = Jdbc_Test5.class.getClassLoader().getResourceAsStream("jdbc.properties");Properties properties = new Properties();properties.load(is);String user = properties.getProperty("user");String password = properties.getProperty("password");String url = properties.getProperty("url");String driverClass = properties.getProperty("driverClass");Class.forName(driverClass);Connection conn = DriverManager.getConnection(url,user,password);//获取statement对象Statement statement = conn.createStatement();Scanner sc = new Scanner(System.in);System.out.println("输入账号");String username = sc.next();System.out.println("输入密码");String userpassword = sc.next();//拼接sqlString sql = "insert into user(username,userpassword) values('"+ username + "','" + userpassword + "')";//执行sqlstatement.execute(sql);//关闭资源conn.close();statement.close();}
}

2.3 ResultSet

  • ResultSet 对象具有指向其当前数据行的指针。最初,指针被置于第一行之前。next 方法将指针移
    动到下一行;因为该方法在 ResultSet 对象中没有下一行时返回 false,所以可以在 while 循环中使
    用它来迭代结果集。
  • 默认的 ResultSet 对象仅有一个向前移动的指针。因此,只能迭代它一次,并且只能按从第一行到
    最后一行的顺序进行。
  • ResultSet 接口提供用于从当前t行检索列值的获取方法(getBoolean、getLong 等)。可以使用列
    的索引编号或列的名称检索值。一般情况下,使用列索引较为高效。列从 1 开始编号。为了获得最
    大的可移植性,应该按从左到右的顺序读取每行中的结果集列,而且每列只能读取一次。

ResultSet内存结构

执行select

public class Statement_Test2 {public static void main(String[] args) throws IOException, SQLException, ClassNotFoundException {InputStream is = Jdbc_Test5.class.getClassLoader().getResourceAsStream("jdbc.properties");Properties properties = new Properties();properties.load(is);String user = properties.getProperty("user");String password = properties.getProperty("password");String url = properties.getProperty("url");String driverClass = properties.getProperty("driverClass");Class.forName(driverClass);Connection conn = DriverManager.getConnection(url,user,password);//获取StatementStatement statement = conn.createStatement();String sql = "select * from user";//执行sql查询,并将结果集返回ResultSet rs = statement.executeQuery(sql);//遍历rs获取所有结果while (rs.next()){System.out.println(rs.getString("username") + "_"+ rs.getString("userpassword"));}conn.close();//关闭资源statement.close();rs.close();}
}

使用Statement的弊端:

  • 存在字符串拼接操作,繁琐。
  • 存在SQL注入问题。

3 PreparedStatement

  • PreparedStatement接口继承Statement
  • PreparedStatement是预编译的,比Statement速度快
  • 调用Connection对象的preparedStatement(String sql)方法获取PreparedStatement对象
  • 调用以下方法执行SQL语句
int executeUpdate(); //执行insert、update、delete操作
Resultset executeQuery(); //执行查询操作

3.1 PreparedStatement添加

  1. 创建PreparedStatement对象
String sql = "insert into user(username,userpassword) values(?,?)";
PreparedStatement ps = conn.prepareStatement(sql);

PreparedStatement对象所代表的SQL语句中的参数用问号(?)来表示,调用PreparedStatement对
象的setXxx()方法来设置这些参数。

  1. 填充占位符
ps.setString(1,"admin6");
ps.setString(2,"123456");

setXxx()方法有两个参数,第一个参数是要设置的SQL语句中的参数的索引(从1开始),第二个是
设置的SQL语句中的参数的值。

注:

  • 目前是设置String,所以使用setString方法。
  • setString方法会自动给参数携带引号,该语句执行过程
insert into user(username,userpassword) values('admin6','123456')
  1. 执行添加
ps.executeUpdate();
  1. 关闭资源
ps.close();
conn.close();
public class PreparedStatement_Test {public static void main(String[] args) throws IOException, ClassNotFoundException, SQLException {InputStream is = Jdbc_Test5.class.getClassLoader().getResourceAsStream("jdbc.properties");Properties properties = new Properties();properties.load(is);String user = properties.getProperty("user");String password = properties.getProperty("password");String url = properties.getProperty("url");String driverClass = properties.getProperty("driverClass");Class.forName(driverClass);Connection conn = DriverManager.getConnection(url,user,password);//获取PreparedStatementString sql = "insert into user(username,userpassword) values(?,?)";PreparedStatement ps = conn.prepareStatement(sql);ps.setString(1,"admin6");ps.setString(2,"123456");ps.executeUpdate();ps.close();conn.close();}
}

3.2 JDBCUtils封装数据库连接与释放

为了统一对Connection资源的管理及使用,创建JDBCUtils工具类,实现对数据库的连接与释放进行统一管理。

数据库连接

    public static Connection getConnection() throws IOException, ClassNotFoundException, SQLException {//1 读取配置文件中4个基本信息InputStream is = Jdbc_Test5.class.getClassLoader().getResourceAsStream("jdbc.properties");Properties properties = new Properties();properties.load(is);String user = properties.getProperty("user");String password = properties.getProperty("password");String url = properties.getProperty("url");String driverClass = properties.getProperty("driverClass");//2 加载驱动Class.forName(driverClass);//3 获取链接Connection conn = DriverManager.getConnection(url,user,password);return conn;}

释放连接:

  public static void close(Connection conn, Statement statement) throws SQLException {if(conn != null){conn.close();}if (statement != null){statement.close();}}

3.3 PreparedStatement修改

  1. 获取连接并创建PreparedStatement对象
Connection conn = JDBCUtils.getConnection();
String sql = "update user set username = ? where id = ?";
PreparedStatement ps = conn.prepareStatement(sql);
  1. 填充占位符
ps.setObject(1,"admintest");
ps.setInt(2,1);

使用setObject设置username,用setInt设置id

  1. 执行修改
ps.executeUpdate();
  1. 关闭资源
JDBCUtils.close(conn,ps);

3.4 PreparedStatement通用增删改

sql中占位符的个数与可变形参的长度相同

//通用的增删改操作public static void update(String sql,Object...args) throws Exception {//1、获取数据库的连接Connection conn = JDBCUtils.getConnection();//2、预编译sql语句,返回PrepareStatement的实例PreparedStatement ps = conn.prepareStatement(sql);//3、填充占位符for (int i = 0; i < args.length; i ++){ps.setObject(i + 1,args[i]);}//4、执行ps.executeUpdate();//5、资源的关闭JDBCUtils.close(conn,ps);}

使用通用方法删除id为1和2的数据

String sql = "delete from user where id = ? or id = ?";
update(sql,1,2);

3.5 PreparedStatement查询

 Connection conn = JDBCUtils.getConnection();String sql = "select * from user";PreparedStatement ps = conn.prepareStatement(sql);//executeQuery()查询结果集ResultSet rs = ps.executeQuery();//遍历数据while (rs.next()){System.out.println(rs.getString("username") + "_" +rs.getString("userpassword"));}rs.close();JDBCUtils.close(conn,ps);

简单的通用查询设计

public static void query(String sql,Object...args) throws SQLException, IOException, ClassNotFoundException {//1、获取数据库的连接Connection conn = JDBCUtils.getConnection();//2、预编译sql语句,返回PrepareStatement的实例PreparedStatement ps = conn.prepareStatement(sql);//3、填充占位符for (int i = 0; i < args.length; i ++){ps.setObject(i + 1,args[i]);}//4、执行ResultSet rs = ps.executeQuery();//获取ResultSet元数据//获取结果集元数据ResultSetMetaData rsmd = rs.getMetaData();//通过ResultSetMetaData获取结果集中的列数int columnCount = rsmd.getColumnCount();while (rs.next()){//根据列数,通过rs.getObject方法+索引获取列的值for (int i = 0; i < columnCount;i++ ){System.out.print(rs.getObject(i + 1 ) + " ");}System.out.println();}//5、资源的关闭rs.close();JDBCUtils.close(conn,ps);}

4 ORM编程思想

对象关系映射(英语:Object Relational Mapping,简称ORM,或O/RM,或O/R mapping)

  • 一个数据表对应一个JAVA类
  • 表中的一条记录对应JAVA类的一个对象
  • 表中的一个字段(列)对应JAVA类的一个属性(字段)

    Java典型的ORM框架中有
  • hibernate:全自动的框架,强大、复杂、笨重、学习成本较高
  • Mybatis:半自动的框架(懂数据库的人 才能操作) 必须要自己写sql
  • JPA:JPA全称Java Persistence API、JPA通过JDK 5.0注解或XML描述对象-关系表的映射关系,是Java
    自带的框架

针对user表通用查询设计
根据user表设计实体类User

package com.bzcxy.jdbc;public class User {private Integer id;private String username;private String userpassword;public Integer getId() {return id;}public void setId(Integer id) {this.id = id;}public String getUsername() {return username;}public void setUsername(String username) {this.username = username;}public String getUserpassword() {return userpassword;}public void setUserpassword(String userpassword) {this.userpassword = userpassword;}
}
package com.bzcxy.jdbc;import java.io.IOException;
import java.lang.reflect.Field;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;public class PreparedStatement_Test4 {public static void main(String[] args) throws ClassNotFoundException, SQLException, NoSuchFieldException, IllegalAccessException, IOException {String sql = "select * from user where id < ?";List<User> users = queryUser(sql,6);System.out.println();}public static List<User> queryUser(String sql,Object...args) throws SQLException, IOException, ClassNotFoundException, NoSuchFieldException, IllegalAccessException {//1、获取数据库的连接Connection conn = JDBCUtils.getConnection();//2、预编译sql语句,返回PrepareStatement的实例PreparedStatement ps = conn.prepareStatement(sql);//3、填充占位符for (int i = 0; i < args.length; i ++){ps.setObject(i + 1,args[i]);}List<User> users = new ArrayList<>();//4、执行ResultSet rs = ps.executeQuery();//获取结果集元数据ResultSetMetaData rsmd = rs.getMetaData();//通过ResultSetMetaData获取结果集中的列数int columnCount = rsmd.getColumnCount();while (rs.next()){   //创建User对象,用于保存每行数据User u = new User();//处理结果集中的每一行的列,将每列的值保存到user对象中对应的属性中for(int i = 0; i < columnCount;i ++){//获取该列的值Object columnValue = rs.getObject(i + 1);//获取该列的名称String columnName = rsmd.getColumnName(i + 1);//通过反射给user对象的columnName字段赋值为columnValue //获取该字段相关属性Field field = u.getClass().getDeclaredField(columnName);//提升权限field.setAccessible(true);//赋值field.set(u,columnValue);}//将赋值后的user对象,添加进listusers.add(u);}rs.close();//5、资源的关闭JDBCUtils.close(conn,ps);return users;}
}

三、用户登录功能

1 业务介绍

1.1 业务需求

模拟用户登录功能实现。

1.2 业务描述

程序运行的时候,提供一个输入的入口,可以让用户输入账号和密码,用户输入之后,提交信息,JAVA
程序收集到用户信息,连接数据库验证用户名和密码是否合法。
合法:显示登录成功
不合法:显示登录失败

1.3 数据表准备

需要提供一张Account数据表,其中包含用户id、用户账号、用户密码、用户昵称。
导入account.sql

2 登录界面初始化

初始化方法

    public static Map<String, String> initUI(){Scanner sc = new Scanner(System.in);System.out.println("请输入账号");String account = sc.next();System.out.println("请输入密码");String password = sc.next();Map<String,String> userLoginInfo = new HashMap<>();userLoginInfo.put("account",account);userLoginInfo.put("password",password);return userLoginInfo;}

测试界面

public static void main(String[] args) { initUI();
}

3 登录实现

既然要访问数据库,将JDBCUtils引入该项目
Account实体类

package com.bzcxy.login;public class Account {private Integer userid;private String useraccount;private String username;private String userpassword;public String getUseraccount() {return useraccount;}public void setUseraccount(String useraccount) {this.useraccount = useraccount;}public Integer getUserid() {return userid;}public void setUserid(Integer userid) {this.userid = userid;}public String getUsername() {return username;}public void setUsername(String username) {this.username = username;}public String getUserpassword() {return userpassword;}public void setUserpassword(String userpassword) {this.userpassword = userpassword;}
}

针对account表实现通用查询

public static List<Account> queryAccount(String sql,Object...args) throws SQLException, IOException, ClassNotFoundException, NoSuchFieldException, IllegalAccessException {List<Account> accounts = new ArrayList<>();//1、获取数据库的连接Connection conn = JDBCUtils.getConnection();//2、预编译sql语句,返回PrepareStatement的实例PreparedStatement ps = conn.prepareStatement(sql);//3、填充占位符for (int i = 0; i < args.length; i ++){ps.setObject(i + 1,args[i]);//小心参数声明错误}//4、执行ResultSet rs = ps.executeQuery();//获取结果集元数据ResultSetMetaData rsmd = rs.getMetaData();//通过ResultSetMetaData获取结果集中的列数int columnCount = rsmd.getColumnCount();while (rs.next()){Account account = new Account();for (int i = 0;i < columnCount; i ++){//获取该列的值Object columnValue = rs.getObject(i + 1);//获取该列的名称String columnName = rsmd.getColumnName(i + 1);//获取该字段相关属性Field field = account.getClass().getDeclaredField(columnName);//提升权限field.setAccessible(true);//注入列值//赋值field.set(account,columnValue);}accounts.add(account);}rs.close();//5、资源的关闭JDBCUtils.close(conn,ps);return  accounts;}

登录实现

 public static boolean login(Map<String,String> userLoginInfo) throws ClassNotFoundException, SQLException, NoSuchFieldException, IllegalAccessException, IOException {//定义sqlString sql = "select * from account where useraccount = ? and userpassword = ?";//获取所有匹配账号密码的account对象List<Account> accounts = queryAccount(sql,userLoginInfo.get("account"),userLoginInfo.get("password"));//如果该集合为0,代表账号或密码没有匹配,登录失败if (accounts.size() == 0){return false;}return true;}

测试登录功能

package com.bzcxy.login;import com.bzcxy.jdbc.JDBCUtils;import java.io.IOException;
import java.lang.reflect.Field;
import java.sql.*;
import java.util.*;public class Login {public static void main(String[] args) throws ClassNotFoundException, SQLException, IOException, IllegalAccessException, NoSuchFieldException {Map<String,String> userLoginInfo = initUI();System.out.println(login(userLoginInfo)?"登录成功":"登录失败");}public static boolean login(Map<String,String> userLoginInfo) throws ClassNotFoundException, SQLException, NoSuchFieldException, IllegalAccessException, IOException {String sql = "select * from account where useraccount = ? and userpassword = ?";List<Account> accounts = queryAccount(sql,userLoginInfo.get("account"),userLoginInfo.get("password"));if (accounts.size() == 0){return false;}return true;}public static Map<String, String> initUI(){Scanner sc = new Scanner(System.in);System.out.println("请输入账号");String account = sc.next();System.out.println("请输入密码");String password = sc.next();Map<String,String> userLoginInfo = new HashMap<>();userLoginInfo.put("account",account);userLoginInfo.put("password",password);return userLoginInfo;}public static List<Account> queryAccount(String sql,Object...args) throws SQLException, IOException, ClassNotFoundException, NoSuchFieldException, IllegalAccessException {List<Account> accounts = new ArrayList<>();Connection conn = JDBCUtils.getConnection();PreparedStatement ps = conn.prepareStatement(sql);for (int i = 0; i < args.length; i ++){ps.setObject(i + 1,args[i]);}ResultSet rs = ps.executeQuery();ResultSetMetaData rsmd = rs.getMetaData();int columnCount = rsmd.getColumnCount();while (rs.next()){Account account = new Account();for (int i = 0;i < columnCount; i ++){//获取列值Object columnValue = rs.getObject(i + 1);//获取列名String columnName = rsmd.getColumnName(i + 1);//获取属性信息Field field = account.getClass().getDeclaredField(columnName);//提升权限field.setAccessible(true);//注入列值field.set(account,columnValue);}accounts.add(account);}rs.close();JDBCUtils.close(conn,ps);return  accounts;}}

四、JDBC高级

1 SQL注入介绍

SQL注入是指利用某些系统没有对用户输入的数据进行充分的检查,而在用户输入数据中注入非法的
SQL语句段或者命令,从而利用系统的SQL引擎完成恶意行为的做法。

修改account表通用查询,使用Statement实现

    public static List<Account> queryAccount(String sql, Object...args) throws SQLException, IOException, ClassNotFoundException, NoSuchFieldException, IllegalAccessException {List<Account> accounts = new ArrayList<>();//获取数据库的连接Connection conn = JDBCUtils.getConnection();Statement statement = conn.createStatement();//执行ResultSet rs = statement.executeQuery(sql);//获取结果集元数据ResultSetMetaData rsmd = rs.getMetaData();//通过ResultSetMetaData获取结果集中的列数int columncount = rsmd.getColumnCount();while (rs.next()){//创建User对象,用于保存每行数据Account account = new Account();//处理结果集中的每一行的列,将每列的值保存到user对象中对应的属性中for (int i = 0; i < columncount; i ++){//获取该列的值Object columnValue = rs.getObject(i + 1);//获取该列的名称String columnName = rsmd.getColumnName(i + 1);//通过反射给user对象的columnName字段赋值为columnValue//获取该字段相关属性Field field = account.getClass().getDeclaredField(columnName);//提升权限field.setAccessible(true);//赋值field.set(account,columnValue);}accounts.add(account);}//资源的关闭JDBCUtils.close(conn, statement);return accounts;}

修改登录实现代码

    public static boolean login(Map<String,String> userLoginInfo) throws ClassNotFoundException, SQLException, NoSuchFieldException, IllegalAccessException, IOException {//定义sql,拼接参数String sql = "select * from account where useraccount = '" + userLoginInfo.get("account")+ "' and userpassword = '" + userLoginInfo.get("password") + "'";//select * from account where useraccount = 'zhangsan' and userpassword = 'baizhan'or'1=1'//获取所有匹配账号密码的account对象List<Account> accounts = queryAccount(sql);//如果该集合为0,代表账号或密码没有匹配,登录失败if (accounts.size() == 0){return false;}return true;}

测试用例正常

以上输入的账号和密码,通过SQL拼接,在执行过程中的SQL实际上是:

select * from account where useraccount = 'zhangsan' and userpassword = 'baizhan'or'1=1'

由于1=1永远成立,所以不论账号密码是否正确,都会返回。
导致SQL注入的根本原因:
用户输入的信息中包含SQL语句的关键字,并且这些关键字参与SQL语句的编译过程,导致SQL语句的原
意被扭曲,进而达到SQL注入的目的。

2 SQL注入解决方案

只要用户提供的信息不参与SQL语句的编译过程,即使用户提供的信息中包含SQL语句的关键字,但是
没有参与编译,仍然不起作用。
PreparedStatement可以将信息参数化,仍然用PreparedStatement实现登录功能:

 public static List<Account> queryAccount(String sql,Object...args) throws SQLException, IOException, ClassNotFoundException, NoSuchFieldException, IllegalAccessException {List<Account> accounts = new ArrayList<>();//1、获取数据库的连接Connection conn = JDBCUtils.getConnection();//2、预编译sql语句,返回PrepareStatement的实例PreparedStatement ps = conn.prepareStatement(sql);//3、填充占位符for (int i = 0; i < args.length; i ++){ps.setObject(i + 1,args[i]);//小心参数声明错误}//4、执行ResultSet rs = ps.executeQuery();//获取结果集元数据ResultSetMetaData rsmd = rs.getMetaData();//通过ResultSetMetaData获取结果集中的列数int columnCount = rsmd.getColumnCount();while (rs.next()){//创建User对象,用于保存每行数据Account account = new Account();//处理结果集中的每一行的列,将每列的值保存到user对象中对应的属性中for (int i = 0;i < columnCount; i ++){//获取列值Object columnValue = rs.getObject(i + 1);//获取列名String columnName = rsmd.getColumnName(i + 1);//获取属性信息Field field = account.getClass().getDeclaredField(columnName);//提升权限field.setAccessible(true);//注入列值field.set(account,columnValue);}accounts.add(account);}rs.close();//5、资源的关闭JDBCUtils.close(conn,ps);return  accounts;}

登录实现

  public static boolean login(Map<String,String> userLoginInfo) throws ClassNotFoundException, SQLException, NoSuchFieldException, IllegalAccessException, IOException {//定义sqlString sql = "select * from account where useraccount = ? and userpassword = ?";//获取所有匹配账号密码的account对象List<Account> accounts = queryAccount(sql,userLoginInfo.get("account"),userLoginInfo.get("password"));//如果该集合为0,代表账号或密码没有匹配,登录失败if (accounts.size() == 0){return false;}return true;}

结果:

以上输入的账号和密码,通过PreparedStatement预编译,将baizhan’or’1=1作为一个整体的字符串参
数设置到SQL当中,在执行过程中的SQL实际上是:

select * from account where useraccount = 'zhangsan' and userpassword = "baizhan'or'1=1"

3 批量插入数据

update、delete本身就具有批处理操作的效果。
创建一张物品空表

Create table goods( id int PRIMARY key auto_increment, goodsname VARCHAR(25)
)


向goods表中插入2000条数据

1. 通过Statement + for循环方式批量插入数据,计算执行时间

public class Insert_Test {public static void main(String[] args) throws Exception {Connection conn = JDBCUtils.getConnection();Statement statement = conn.createStatement();//获取起始时间Long start = System.currentTimeMillis();for(int i = 0; i < 2000; i ++){String sql = "insert into goods(goodsname)values('name_" + i + "')";statement.execute(sql);}//获取结束时间Long end = System.currentTimeMillis();JDBCUtils.close(conn,statement);System.out.println("插入时间为:" +  (end - start));}
}

由于方法一使用的是statement,所以每次需要重新生成sql字符串。

2. 通过PreparedStatement + for循环方式批量插入数据,计算执行时间

public class Insert_Test2 {public static void main(String[] args) throws Exception {Connection conn = JDBCUtils.getConnection();String sql = "insert into goods(goodsname)values(?)";PreparedStatement psmt = conn.prepareStatement(sql);Long start = System.currentTimeMillis();for(int i = 0; i < 2000; i ++){psmt.setObject(1,"name_" + i);psmt.executeUpdate();}Long end = System.currentTimeMillis();JDBCUtils.close(conn,psmt);System.out.println("插入时间为:" +  (end - start));}
}

方法二使用的是PreparedStatement,PreparedStatement是预编译模式,DBServer的编译器编译后的执行代码被缓存下来,那么下次调用时只要是相同的预编译语句就不需要编译,只要将参数
直接传入就可以执行。

3. 通过PreparedStatement的addBatch()和executeBatch()进行批量插入数据

  • addBatch()把若干SQL语句装载到一起,然后一次性传送到数据库执行,即是批量处理sql数
    据的。
  • executeBatch()会将装载到一起的SQL语句执行。
  • clearBatch()清除缓存
    :MySql默认情况下是不支持批处理的
    但从5.1.13开始添加了一个rewriteBatchStatement的参数,让MySql支持批处理。
    在加载url时设置该参数:rewriteBatchedStatements=true
url=jdbc:mysql://localhost:3306/baizhan?
userSSL=false&rewriteBatchedStatements=true
public class Insert_Test3 {public static void main(String[] args) throws SQLException, IOException, ClassNotFoundException {Connection conn = JDBCUtils.getConnection();String sql = "insert into goods(goodsname)values(?)";PreparedStatement psmt = conn.prepareStatement(sql);Long start = System.currentTimeMillis();for(int i = 0; i < 2000; i ++){psmt.setObject(1,"name_" + i);缓存sql psmt.addBatch();//每500条缓存执行一次if (i % 500 == 0){//一次性将500条sql全部执行psmt.executeBatch();//清空缓存psmt.clearBatch();}}Long end = System.currentTimeMillis();JDBCUtils.close(conn,psmt);System.out.println("插入时间为:" +  (end - start));}
}

4 Blob类型数据

4.1 MySql Blob类型

  • MySql中,Blob是一个二进制大型对象,是一个可以存储大量数据的容器,它能容纳不同大小的数据。
  • 插入Blob类型的数据必须使用PreparedStatement,因为Blob类型的数据无法使用字符串拼接。
  • MySql中有四种Blob类型,它们除了在存储的最大容量上不同,其他是一致的。
  • 实际使用中根据需要存入的数据大小定义不同的Blob类型。
  • 需要注意的是:如果存储的文件过大,数据库的性能会下降。
  • 如果在指定了相关的Blob类型以后,还报错:xxx too large,那么在MySql的安装目录下,找到
    my.ini文件加上如下配置参数:max_allowed_packet=16M。并重启MySql服务。

4.2 插入Blob类型数据

  1. 导入movieactor.sql

    photo列类型为MediumBlob,存储照片的二进制。
  2. 通过PreparedStatement存储Blob类型数据
//第二个参数传入流
setBlob(int parameterIndex, InputStream inputStream)
public class Blob_Test {public static void main(String[] args) throws SQLException, IOException, ClassNotFoundException {Connection conn = JDBCUtils.getConnection();String sql = "insert into movieactor(actorname,photo)values(?,?)";PreparedStatement ps = conn.prepareStatement(sql);ps.setString(1,"朱茵");//读入图片流InputStream is = new FileInputStream(new File("G:/actorimg/zhuyin.jpg"));ps.setBlob(2,is);ps.executeUpdate();JDBCUtils.close(conn,ps);}
}

防乱码

user=root
password=root
url=jdbc:mysql://localhost:3306/baizhan?useSSL=false&rewriteBatchedStatements=true&useUnicode=true&characterEncoding=utf8
driverClass=com.mysql.jdbc.Driver

可以在Navicat中直接查看

选择图像之后:

4.3 读取Blob类型数据

public class Blob_Test2 {public static void main(String[] args) throws SQLException, IOException, ClassNotFoundException {Connection conn = JDBCUtils.getConnection();String sql = "select * from movieactor where photo != ''";PreparedStatement ps = conn.prepareStatement(sql);ResultSet rs = ps.executeQuery();while (rs.next()){int id = rs.getInt("id");String name = rs.getString("actorname");//获取Blob类型数据,保存成jpg格式文件Blob blob = rs.getBlob("photo");InputStream is = blob.getBinaryStream();FileOutputStream fos = new FileOutputStream(id + "_" + name + ".jpg");byte[] Buffer = new byte[1024];int len;while ( (len = is.read(Buffer)) != -1){fos.write(Buffer,0,len);}is.close();fos.close();}JDBCUtils.close(conn,ps);}
}

4.4 特殊情况

读入一张7M左右的图片

public class Blob_Test3 {public static void main(String[] args) throws SQLException, IOException, ClassNotFoundException {Connection conn = JDBCUtils.getConnection();String sql = "insert into movieactor(actorname,photo)values(?,?)";PreparedStatement ps = conn.prepareStatement(sql);ps.setString(1,"照片");//读入7M左右的文件InputStream is = new FileInputStream(new File("G:/actorimg/photo.jpg"));ps.setBlob(2,is);ps.executeUpdate();JDBCUtils.close(conn,ps);}
}

报错

Exception in thread "main" com.mysql.jdbc.PacketTooBigException: Packet for query
is too large (8056024 > 4194304). You can change this value on the server by
setting the max_allowed_packet' variable.

虽然MediumBlob允许保存最大值为16M,但MySql默认允许值为4194304即4M
my.ini中添加max_allowed_packet=16M,并重启MySql服务。


再次执行,即可存入数据。

五、JDBC事务

1 JDBC事务概述

  1. 事务:一个包含多个步骤的业务操作。如果这个业务操作被事务管理,则这多个步骤要不同时成功,要不同时失败。
  2. 事务操作:
    • 开启事务
    • 提交事务
    • 回滚事务
  3. 在JDBC中,使用Connection对象来管理事务
    • 开启事务:void setAutoCommit(boolean autoCommit):调用该方法设置参数为false时,代
      表开启事务。
    • 提交事务:commit()
    • 回滚事务:rollback()

2 JDBC事务实现

银行转账业务:
非事务实现:

  1. 加载相关数据,导入bank.sql
  2. 由zhangsan向lisi转账500
public class Bank_Test {public static void main(String[] args) throws SQLException {Connection conn = null;PreparedStatement psmt = null;try{conn = JDBCUtils.getConnection();conn.setAutoCommit(false);String sql = "update bank set balance = balance + ? where accountname = ?";psmt = conn.prepareStatement(sql);//由zhangsan向lisi转账,所以lisi账户增加500psmt.setObject(1,500);psmt.setObject(2,"lisi");psmt.executeUpdate();//zhansan账户减500psmt.setObject(1,-500);psmt.setObject(2,"zhangsan");psmt.executeUpdate();}catch (Exception ex){System.out.println(ex.getMessage());}finally {//关闭所有资源JDBCUtils.close(conn,psmt);}}
}
  1. 如果在两个executeUpdate()之间发生异常,则部分转账正常执行

模拟异常

psmt.setObject(1,500);
psmt.setObject(2,"lisi");
psmt.executeUpdate();
//模拟异常
//int i = 1 / 0;
psmt.setObject(1,-500);
psmt.setObject(2,"zhangsan");
psmt.executeUpdate();
  1. 由于两次SQL操作之间出现异常,所以只有部分转账成功


    严重错误!lisi账户增加,zhangsan未减少。
    事务实现:
    在获取数据库连接之后,通过Connection对象开启事务
    由zhangsan向lisi转账500
public class Bank_Test {public static void main(String[] args) throws SQLException {Connection conn = null;PreparedStatement psmt = null;try{conn = JDBCUtils.getConnection();//开启事务conn.setAutoCommit(false);String sql = "update bank set balance = balance + ? where accountname = ?";psmt = conn.prepareStatement(sql);//由zhangsan向lisi转账,所以lisi账户增加500psmt.setObject(1,500);psmt.setObject(2,"lisi");psmt.executeUpdate();psmt.setObject(1,-500);psmt.setObject(2,"zhangsan");psmt.executeUpdate();//如果没出错,提交事务conn.commit();}catch (Exception ex){//如果出错,回滚事务conn.rollback();System.out.println(ex.getMessage());}finally {//不管是否正常提交事务,均关闭资源 //关闭所有资源JDBCUtils.close(conn,psmt);}}
}

模拟异常

psmt.setObject(1,500);
psmt.setObject(2,"lisi");
psmt.executeUpdate();
//模拟异常
int i = 1 / 0;
psmt.setObject(1,-500);
psmt.setObject(2,"zhangsan");
psmt.executeUpdate();

再次执行,由于有异常,所以所有操作回滚,达到多操作的原子性效果。

六、数据库连接池

1 数据库连接池概述

1.1 JDBC数据库连接池的必要性

  1. 在使用开发基于数据库的web程序时,传统的模式基本是按以下步骤:
  • 在主程序中建立数据库连接
  • 进行sql操作
  • 断开数据库连接
  1. 这种模式开发,存在的问题
  • 普通的JDBC数据库连接使用 DriverManager 来获取,每次向数据库建立连接的时候都要将Connection加载到内存中,再验证用户名和密码(得花费0.05s~1s的时间)。
  • 需要数据库连接的时候,就向数据库要求一个,执行完成后再断开连接。这样的方式将会消耗大量的资源和时间。数据库的连接资源并没有得到很好的重复利用。若同时有几百人甚至几千人在线,频繁的进行数据库连接操作将占用很多的系统资源,严重的甚至会造成服务器的崩溃。
  • 对于每一次数据库连接,使用完后都得断开。否则,如果程序出现异常而未能关闭,将会导致数据库系统中的内存泄漏,最终将导致重启数据库。
  • 这种开发不能控制被创建的连接对象数,系统资源会被毫无顾及的分配出去,如连接过多,也可能导致内存泄漏,服务器崩溃。

1.2 数据库连接池

为解决传统开发中的数据库连接问题,可以采用数据库连接池技术。

  • 数据库连接池的基本思想:为数据库连接建立一个“缓冲池”。预先在缓冲池中放入一定数量的连接,当需要建立数据库连接时,只需从“缓冲池”中取出一个,使用完毕之后再放回去。
  • 数据库连接池负责分配、管理和释放数据库连接,它允许应用程序重复使用一个现有的数据库连接,而不是重新建立一个
  • 数据库连接池在初始化时将创建一定数量的数据库连接放到连接池中,这些数据库连接的数量是由最小数据库连接数来设定的。无论这些数据库连接是否被使用,连接池都将一直保证至少拥有这么多的连接数量。连接池的最大数据库连接数量限定了这个连接池能占有的最大连接数,当应用程序向连接池请求的连接数超过最大连接数量时,这些请求将被加入到等待队列中。

1.3 数据库连接池的优点

  1. 资源重用:由于数据库连接得以重用,避免了频繁创建,释放连接引起的大量性能开销。在减少系统消耗的基础上,另一方面也增加了系统运行环境的平稳性。
  2. 更快的系统反应速度:数据库连接池在初始化过程中,往往已经创建了若干数据库连接置于连接池中备用。此时连接的初始化工作均已完成。对于业务请求处理而言,直接利用现有可用连接避免了数据库连接初始化和释放过程的时间开销,从而减少了系统的响应时间。
  3. 新的资源分配手段:对于多应用共享同一数据库的系统而言,可在应用层通过数据库连接池的配置实现某一应用最大可用数据库连接数的限制避免某一应用独占所有的数据库资源.
  4. 统一的连接管理:避免数据库连接泄露在较为完善的数据库连接池实现中,可根据预先的占用超时设定,强制回收被占用连接,从而避免了常规数据库连接操作中可能出现的资源泄露。

1.4 常用的数据库连接池

  • c3p0是一个开源组织提供的数据库连接池,速度相对较慢,稳定性还可以。
  • DBCP是Apache提供的数据库连接池。tomcat服务器自带dbcp数据库连接池。速度相对c3p0较 快,但自身存在bug。
  • Proxool是sourceforge下的一个开源项目数据库连接池,有监控连接池状态的功能,稳定性不如
    c3p0。
  • Druid是阿里提供的数据库连接池,据说是集DBCP、c3p0、proxool优点于一身的数据库连接池,目前经常使用。

2 c3p0连接池

2.1 c3p0基本使用

  1. 导入c3p0-0.9.5.2.jar包到lib目录下,并引入到项目中,c3p0依赖mysql-connector-java包。
  2. 导入mchange-commons-java-0.2.12.jar包,c3p0数据库连接池的辅助包。这是更新c3p0-0.9.2版本后分离出来的包,0.9.1的时候还是只是一个包
  3. 从c3p0连接池中获取Connection连接
public class C3P0_Test {public static void main(String[] args) throws Exception {ComboPooledDataSource cpds = new ComboPooledDataSource();//配置JDBC相关数据cpds.setDriverClass("com.mysql.jdbc.Driver");cpds.setJdbcUrl("jdbc:mysql://localhost:3306/baizhan");cpds.setUser("root");cpds.setPassword("root");//设置初始化时,数据库连接池的连接数量cpds.setInitialPoolSize(10);//获取连接Connection conn = cpds.getConnection();System.out.println(conn);}
}
  1. 可以通过DataSources销毁连接池
void destroy(DataSource pooledDataSource)
DataSources.destroy(cpds);

2.2 c3p0配置xml使用方式

  1. 在src目录下添加名为c3p0-config.xml文件。
  2. 在c3p0-config.xml配置内容如下:
<c3p0-config> <!-- 默认的数据库连接池--> <default-config> <!-- 数据库基本配置--> <property name="driverClass">com.mysql.jdbc.Driver</property> <property name="jdbcUrl">jdbc:mysql://localhost:3306/baizhan</property> <property name="user">root</property> <property name="password">root</property> <!-- 数据库连接池配置--> <!-- 当数据库连接池中的连接不够时,c3p0一次性向数据库申请的连接数--> <property name="acquireIncrement">5</property> <!-- 数据库连接池中初始化的连接数量--> <property name="initialPoolSize">10</property> <!-- 数据库连接池中最少的连接数量--> <property name="minPoolSize">10</property> <!-- 数据库连接池中最多的连接数量--> <property name="maxPoolSize">100</property> <!-- 数据库连接池中最多存在的Statement数量--> <property name="maxStatements">200</property> <!-- 每个连接最多可以使用的Statement数量--> <property name="maxStatementsPerConnection">2</property> </default-config>
</c3p0-config>
  1. 从c3p0连接池中获取Connection连接
public class C3P0_Test2 {public static void main(String[] args) throws SQLException {//使用默认的数据库连接池ComboPooledDataSource cpds = new ComboPooledDataSource("localc3p0");Connection conn = cpds.getConnection();System.out.println(conn);}
}
  1. 也可以在c3p0-config.xml设置多个数据库连接池,根据连接池名字不同,来初始化不同的连接池。
    如下的配置中,有两个数据库连接池配置
<c3p0-config><!-- 设置一个名为localc3p0的数据库连接池--><named-config name="localc3p0"><!-- 数据库基本配置--><property name="driverClass">com.mysql.jdbc.Driver</property><property name="jdbcUrl">jdbc:mysql://localhost:3306/baizhan</property><property name="user">root</property><property name="password">root</property><!-- 数据库连接池配置--><!-- 当数据库连接池中的连接不够时,c3p0一次性向数据库申请的连接数--><property name="acquireIncrement">5</property><!-- 数据库连接池中初始化的连接数量--><property name="initialPoolSize">10</property><!-- 数据库连接池中最少的连接数量--><property name="minPoolSize">10</property><!-- 数据库连接池中最多的连接数量--><property name="maxPoolSize">100</property><!-- 数据库连接池中最多存在的Statement数量--><property name="maxStatements">200</property><!-- 每个连接最多可以使用的Statement数量--><property name="maxStatementsPerConnection">2</property></named-config><!-- 设置一个名为VMc3p0的数据库连接池--><named-config name="VMc3p0"><!-- 数据库基本配置--><property name="driverClass">com.mysql.jdbc.Driver</property><property name="jdbcUrl">jdbc:mysql://xxx.xxx.xxx.xxx:3306/bjsxt</property><property name="user">root</property><property name="password">root</property><!-- 数据库连接池配置省略--></named-config>
</c3p0-config>
  1. 通过named-config中的name属性的值,来初始化不同的数据库连接池
//获取名为localc3p0连接池
ComboPooledDataSource cpds = new ComboPooledDataSource("localc3p0");
Connection conn = cpds.getConnection();
System.out.println(conn);

3 DBCP数据库连接池

  • DBCP 是 Apache 软件基金组织下的开源连接池实现
  • Tomcat 的连接池正是采用该连接池来实现的。该数据库连接池既可以与应用服务器整合使用,也
    可由应用程序独立使用。

DBCP配置信息:

3.1 DBCP基本使用

  1. 导入commons-dbcp2-2.5.0.jar包及其依赖包commons-pool2-2.6.0.jar、commons-logging-
    1.2.jar到lib目录下,并引入项目中
  2. 从dbcp连接池中获取Connection连接
public class DBCP_Test {public static void main(String[] args) throws SQLException {BasicDataSource source = new BasicDataSource();//配置JDBC相关数据source.setDriverClassName("com.mysql.jdbc.Driver");source.setUrl("jdbc:mysql://localhost:3306/baizhan");source.setUsername("root");source.setPassword("root");//设置初始化时,数据库连接池的连接数量source.setInitialSize(10);//获取数据库连接Connection conn = source.getConnection();System.out.println(conn);}
}

3.2 DBCP配置文件使用方式

  1. 在src下创建一个dbcp.properties类型的文件,并写入
url=jdbc:mysql://localhost:3306/baizhan
driverClassName=com.mysql.jdbc.Driver
username=root
password=root
initialSize=10
maxActive=20
  1. 加载配置文件
InputStream is = DBCPTest2.class.getClassLoader().getResourceAsStream("dbcp.properties");
Properties properties = new Properties();
properties.load(is);
  1. 获取连接池对象,通过该连接池对象获取连接
DataSource ds = BasicDataSourceFactory.createDataSource(properties);
Connection conn = ds.getConnection();
System.out.println(conn);
public class DBCP_Test2 {public static void main(String[] args) throws Exception {InputStream is = DBCP_Test2.class.getClassLoader().getResourceAsStream("dbcp.properties");Properties properties = new Properties();properties.load(is);DataSource ds = BasicDataSourceFactory.createDataSource(properties);Connection conn = ds.getConnection();System.out.println(conn);}
}

4 druid连接池(德鲁伊)

druid是阿里提供的数据库连接池,据说是集DBCP、c3p0、proxool优点于一身的数据库连接池,目前
经常使用。

4.1 druid基本使用

  1. 导入druid-1.0.19.jar包到lib目录下,并引入到项目中
  2. 在src下创建一个druid.properties类型的文件,并写入
url=jdbc:mysql://localhost:3306/baizhan?useSSL=false
driverClassName=com.mysql.jdbc.Driver
username=root
password=root
initialSize=10
maxActive=20

druid配置信息:

  1. 加载配置文件
InputStream is = DruidTest.class.getClassLoader().getResourceAsStream("druid.properties");
Properties properties = new Properties();
pros.load(is);
  1. 获取连接池对象,通过该连接池对象获取连接
//获取连接池对象
DataSource ds = DruidDataSourceFactory.createDataSource(properties);
//获取连接
Connection conn = ds.getConnection();
System.out.println(conn);
public class Druid_Test {public static void main(String[] args) throws Exception {InputStream is = Druid_Test.class.getClassLoader().getResourceAsStream("druid.properties");Properties properties = new Properties();properties.load(is);DataSource ds = DruidDataSourceFactory.createDataSource(properties);Connection conn = ds.getConnection();System.out.println(conn);}
}

4.2 通过druid重构JDBCUtils

  1. 创建JDBCUtilsDruid类
  2. 使用静态块初始化连接池
public class JDBCUtilsDruid {private static DataSource ds = null;static {InputStream is = Druid_Test.class.getClassLoader().getResourceAsStream("druid.properties");Properties properties = new Properties();try {properties.load(is);ds = DruidDataSourceFactory.createDataSource(properties);} catch (Exception e) {e.printStackTrace();}}
  1. 获取数据库连接方法
   public static Connection getConnection() throws SQLException {Connection conn = ds.getConnection();return conn;}
  1. 释放数据库连接方法
    public static void close(Connection conn, Statement statement) throws SQLException {if(conn != null){conn.close();}if (statement != null){statement.close();}}

七、JDBC_DAO模式

1 应用程序分层

应用程序通过创建不同的包来实现项目的分层,将项目中的代码根据功能做具体划分,并存放在不同的包下。

com.bzcxy.controller//控制层(连接前端、界面层、UI和后端的中间层)
com.bzcxy.service//业务逻辑层 (登录,具体业务逻辑)
com.bzcxy.dao//数据库操作 (JDBC)
com.bzcxy.pojo//数据库实体类(数据库的实体类)

1.1 分层优点

  1. 分层结构将应用系统划分为若干层,每一层只解决问题的一部分,通过各层的协作提供整体解决方案。大的问题被分解为一系列相对独立的子问题,局部化在每一层中,这样就有效的降低了单个问题的规模和复杂度,实现了复杂系统的第一步也是最为关键的一步分解。
  2. 分层结构具有良好的可扩展性,为应用系统的演化增长提供了一个灵活的支持,具有良好的可扩展性。增加新的功能时,无须对现有的代码做修改,业务逻辑可以得到最大限度的重用。
  3. 分层架构易于维护。在对系统进行分解后,不同的功能被封装在不同的层中,层与层之间的耦合显著降低。因此在修改某个层的代码时,只要不涉及层与层之间的接口,就不会对其他层造成严重影响。

1.2 三层结构

三层结构就是将整个业务应用划分为:
界面层(User Interface layer):提供与用户交互的界面,如GUI(图形用户界面),web页面等;
业务逻辑层(Business Logic Layer):负责各种业务逻辑,直接访问数据库,提供对业务数据的保
存、更新、删除和查询操作;
数据访问层(Data access layer):负责存放管理应用的持久性业务数据
三层结构的特点是:
所有下层向上层提供调用的接口,具体实现细节对上层透明。层与层之间存在自上而下的依赖关系,即上层会访问下层的API,但下层不依赖于上层。

2 DAO

DAO:Data Access Object访问数据库信息的类和接口,包括了对数据的CRUD(Create、Retrival、Update、Delete)、而不包含任何业务相关的信息。

2.1 通用BaseDAO实现

DAO一般是提供从数据库 增加、删除、修改记录、查询所有记录、查询符合某个条件记录、取得某条记
录等方法的底层数据操作自定义类。
由于可能操作多个数据库表,这样就需要为每个表提供一个操作他的类 xxDAO, 这些DAO继承BaseDAO 就可以省略很多重复代码(从数据库 增加、删除、修改记录、查询所有记录、查询符合某个条件记录、取得某条记录等方法的代码)。

  1. 实现一个通用的BaseDao抽象类
package com.bzcxy.dao;import com.bzcxy.jdbc.JDBCUtilsDruid;
import org.apache.commons.beanutils.BeanUtils;import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.util.ArrayList;
import java.util.List;public abstract class BaseDao<T> {//定义一个变量来接受泛型的类型private Class<T> type;//获取T的Class对象,获取泛型类型,泛型是在被子类继承时才确定public BaseDao(){//获取子类类型Class clazz = this.getClass();//获取父类类型// getGenericSuperclass()用来获取当前类的父类的类型// ParameterizedType表示的是带泛型的类型ParameterizedType parameterizedType = (ParameterizedType)clazz.getGenericSuperclass();//获取具体的泛型类型//getActualTypeArguments获取具体的泛型类型//这个方法返回一个Type类型数组Type[] types = parameterizedType.getActualTypeArguments();//获取具体的泛型类型this.type = (Class<T>)types[0];}
}
  1. 通用的增删改操作
 //通用增删改操作public int excuteQuery(String sql,Object...param) throws Exception{//获取连接Connection conn = JDBCUtilsDruid.getConnection();PreparedStatement psmt = conn.prepareStatement(sql);//绑定参数for (int i = 0; i < param.length; i ++){psmt.setObject(i + 1,param[i]);}int rows = psmt.executeUpdate();JDBCUtilsDruid.close(conn,psmt);//返回受影响的行数return rows;}
  1. 通用的查询操作,需要用到commons-beanutils-1.9.3.jar包及其依赖包commons-logging-1.2.jar,将它们导入并引入工程
    //通用查询方法,返回零条或多条查询记录public List<T> getBean(String sql,Object...param)throws Exception{List<T> list = new ArrayList<>();//获取连接Connection conn = JDBCUtilsDruid.getConnection();PreparedStatement psmt = conn.prepareStatement(sql);//绑定参数for (int i = 0; i < param.length; i ++){psmt.setObject(i + 1,param[i]);}ResultSet rs = psmt.executeQuery();//获取结果集元数据ResultSetMetaData rsmd = rs.getMetaData();while (rs.next()){T bean = type.newInstance();for (int i = 0; i < rsmd.getColumnCount(); i ++){//获得列名String comlumnName = rsmd.getColumnName(i + 1);//获取列值Object value = rs.getObject(comlumnName);//通过BeanUtil工具将类注入到对象中BeanUtils.setProperty(bean,comlumnName,value);}list.add(bean);}return list;}
  1. 获取单一值,如select(*)
    //获取单一值,如select(*) select max(date)等等public Object getValue(String sql,Object...param) throws Exception{Object res = null;Connection conn = JDBCUtilsDruid.getConnection();PreparedStatement psmt = conn.prepareStatement(sql);//绑定参数for (int i = 0; i < param.length; i ++){psmt.setObject(i + 1,param[i]);}ResultSet rs = psmt.executeQuery();if(rs.next()){//获取第一列的值res = rs.getObject(1);}return res;}

2.2 UserDAO

  1. 创建UserDAO接口
public interface UserDAO {//根据username获取一条记录User getUser(String username) throws Exception;//插入一条Uservoid insertUser(User user) throws Exception;//根据id删除一条数据void deleteUserById(Integer id) throws Exception;//获取一共有多少用户Integer getUserCount() throws Exception;
}
  1. 创建UserDAO的实现类UserDAOImplUserDAOImpl继承BaseDAO由于每个方法都要抛出异常,所以在接口UserDAO的方法后面也会throw异常
package com.bzcxy.dao;import com.bzcxy.jdbc.User;import java.util.List;public class UserDAOImpl extends BaseDao<User> implements UserDAO {@Overridepublic User getUser(String username) throws Exception {User u = null;String sql = "select * from user where username = ?";//使用BaseDAO中的通用查询方法getBeanList<User> list = this.getBean(sql,username);//判断是否有值if(list.size() != 0){u = list.get(0);}return u;}@Overridepublic void insertUser(User user) throws Exception {String sql = "insert into user(username,userpassword)values(?,?)";//使用BaseDAO中的通用增删改方法this.excuteQuery(sql,user.getUsername(),user.getUserpassword());}@Overridepublic void deleteUserById(Integer id) throws Exception {String sql = "delete from user where id = ?";//使用BaseDAO中的通用增删改方法this.excuteQuery(sql,id);}@Overridepublic Integer getUserCount() throws Exception {String sql = "select count(*) from user";//使用BaseDAO中获取单一值的方法Integer count = Integer.valueOf(this.getValue(sql).toString()) ;return count;}
}
  1. 测试各个方法
public class Main {public static void main(String[] args) throws Exception {UserDAO userDAO = new UserDAOImpl();//测试insertuser
//        User u = new User();
//        u.setUsername("test");
//        u.setUserpassword("password");
//        userDAO.insertUser(u);//测试get
//        User u = userDAO.getUser("test");
//        System.out.println(u.getUsername() + ":" + u.getUserpassword());//测试deleteuserDAO.deleteUserById(7);//获取用户总数System.out.println(userDAO.getUserCount());}
}

八、DbUtils工具

1 Apache-DbUtils

  • commons-dbutils 是 Apache 组织提供的一个开源 JDBC工具类库,它是对JDBC的简单封装,学习成本极低,并且使用dbutils能极大简化jdbc编码的工作量,同时也不会影响程序的性能。
  • API介绍:
    org.apache.commons.dbutils.QueryRunner
    org.apache.commons.dbutils.ResultSetHandler
    工具类:org.apache.commons.dbutils.DbUtils

2 QueryRunner

QueryRunner类是Dbutils的核心类之一,提供对sql语句操作的API

  • int update(String sql, Object… params):执行insert update delete操作
  • query(String sql, ResultSetHandler rsh, Object… params) :执行 select操作

2.1 通过QueryRunner添加数据

  1. 导入commons-dbutils-1.6.jar包,并引入工程
  2. 通过DataSource对象初始化QueryRunner对象
public class DBUtils_Test {public static void main(String[] args) throws Exception {InputStream is = DBUtils_Test.class.getClassLoader().getResourceAsStream("druid.properties");Properties properties = new Properties();properties.load(is);//通过数据库连接池初始化QueryRunner对象QueryRunner queryRunner = new QueryRunner(DruidDataSourceFactory.createDataSource(properties));}
}
  1. 编写sql,并通过update方法执行
 String sql = "insert into user(username,userpassword)values(?,?)";//count代表影响了几行记录int count =  queryRunner.update(sql,"QueryRunnerTest","QueryRunner");System.out.println(count);

:实际上QueryRunner的update方法仍然使用PreparedStatement来实现。

3 ResultSetHandler

ResultSetHandler接口(org.apache.commons.dbutils.ResultSethandler)执行处理一个结果集对象,将数据转变并处理为任何一种形式,供其他应用使用。

public interface ResultSetHandler<T> { T handle(ResultSet var1) throws SQLException;
}

ResultSetHandler实现类

3.1 通过QueryRunner查询数据

返回单条记录并封装成实体类对象

public class DBUtils_Test2 {public static void main(String[] args) throws Exception {InputStream is = DBUtils_Test2.class.getClassLoader().getResourceAsStream("druid.properties");Properties properties = new Properties();properties.load(is);QueryRunner queryRunner = new QueryRunner(DruidDataSourceFactory.createDataSource(properties));String sql = "select * from user where id = ?";//一条记录使用BeanHandler //给BeanHandler提供相关实体类信息BeanHandler<User> bh = new BeanHandler<>(User.class);//使用实体类对象直接接收数据 //query参数,分别为sql语句,ResultSetHandler对象,及sql语句中的占位符参数User user = queryRunner.query(sql,bh,7);System.out.println(user.getUsername());}
}

返回多条记录并封装成实体类对象列表

public class DBUtils_Test3 {public static void main(String[] args) throws Exception {InputStream is = DBUtils_Test3.class.getClassLoader().getResourceAsStream("druid.properties");Properties properties = new Properties();properties.load(is);QueryRunner queryRunner = new QueryRunner(DruidDataSourceFactory.createDataSource(properties));String sql = "select * from user";//多条记录使用BeanListHandler //给BeanListHandler提供相关实体类信息BeanListHandler<User> bh = new BeanListHandler<>(User.class);//使用实体类集合直接接收所有数据 //query参数,分别为sql语句,ResultSetHandler对象List<User> users = queryRunner.query(sql,bh);for(User u : users){System.out.println(u.getUsername());}}
}

3.2 通过QueryRunner查询特殊数据

查询User表中一共有多少条数据

public class DBUtils_Test4 {public static void main(String[] args) throws Exception {InputStream is = DBUtils_Test4.class.getClassLoader().getResourceAsStream("druid.properties");Properties properties = new Properties();properties.load(is);QueryRunner queryRunner = new QueryRunner(DruidDataSourceFactory.createDataSource(properties));String sql = "select count(*) from user";//通过ScalarHandler保存单一值ScalarHandler sh = new ScalarHandler();Long count =  (Long)queryRunner.query(sql,sh);System.out.println(count);}
}

3.3 自定义ResultSetHandler

实现ResultSetHandler接口,返回一个Map对象:

  1. 创建MyResultSetHandler类,并implements ResultSetHandler接口,重写handle方法
public class MyResultSetHandler implements ResultSetHandler { @Override public Object handle(ResultSet resultSet) throws SQLException { return null; }
}
  1. 当QueryRunner从数据库中查询完毕,会调用handle方法,并传入一个ResultSet参数,通过
    ResultSet参数封装Map返回。
public class MyResultSetHandler implements ResultSetHandler {@Overridepublic Object handle(ResultSet resultSet) throws SQLException {Map<String,String> map = null;if(resultSet.next()){//创建一个map对象map = new HashMap<>();map.put("id",resultSet.getString("id"));map.put("username",resultSet.getString("username"));map.put("userpassword",resultSet.getString("userpassword"));}return map;}
}
  1. 通过MyResultSetHandler获取map对象
public class MyResultSetHandler implements ResultSetHandler {@Overridepublic Object handle(ResultSet resultSet) throws SQLException {Map<String,String> map = null;if(resultSet.next()){map = new HashMap<>();map.put("id",resultSet.getString("id"));map.put("username",resultSet.getString("username"));map.put("userpassword",resultSet.getString("userpassword"));}return map;}public static void main(String[] args) throws Exception {InputStream is = DBUtils_Test.class.getClassLoader().getResourceAsStream("druid.properties");Properties properties = new Properties();properties.load(is);QueryRunner queryRunner = new QueryRunner(DruidDataSourceFactory.createDataSource(properties));String sql = "select * from user where id = ?";MyResultSetHandler msh = new MyResultSetHandler();Map<String,String> map = (Map<String,String>)queryRunner.query(sql,msh,7);System.out.println(map);}
}

九、JAVA与Oracle

在Oracle中创建表orcl_user

create table orcl_user( id number(11) primary key, username varchar(255), userpassword varchar(255)
);

执行插入语句

insert into orcl_user values(1,'admin1','123456');
insert into orcl_user values(2,'admin2','456789');
insert into orcl_user values(3,'admin3','aaabbb');
insert into orcl_user values(4,'admin4','cccddd');
commit;

注:Oracle默认不自动提交,需要执行commit提交,否则数据并未真正添加

Oracle的连接URL编写方式:

  • jdbc:oracle:thin:@主机名称:oracle服务端口号:数据库名称
  • jdbc:oracle:thin:@localhost:1521:orcl

Oracle的驱动:oracle.jdbc.driver.OracleDriver

  1. 导入ojdbc6.jar,并引入工程
  2. 加载驱动及获取连接
 String driver = "oracle.jdbc.driver.OracleDriver";String url = "jdbc:oracle:thin:@127.0.0.1:1521:orcl";String user = "system";String password = "root";Class.forName(driver);Connection conn = DriverManager.getConnection(url,user,password);System.out.println(conn);
  1. 通过PreparedStatement查询数据
public class JDBCOracle_Test {public static void main(String[] args) throws Exception {String driver = "oracle.jdbc.driver.OracleDriver";String url = "jdbc:oracle:thin:@127.0.0.1:1521:orcl";String user = "system";String password = "root";Class.forName(driver);Connection conn = DriverManager.getConnection(url,user,password);// System.out.println(conn);String sql = "select * from orcl_user";PreparedStatement ps = conn.prepareStatement(sql);ResultSet rs = ps.executeQuery();while (rs.next()){System.out.println(rs.getString("id") + ":" +rs.getString("username") + ":" +rs.getString("userpassword"));}conn.close();rs.close();ps.close();}
}

十、JDBC分页技术

1 分页概述

当一个操作数据库进行查询的语句返回的结果集内容如果过多,那么内存极有可能溢出,所以在大数据的情况下分页是必须的。
分页技术实现:

物理分页:

  • 在数据库执行查询时(实现分页查询),查询需要的数据—依赖数据库的SQL语句
  • 在SQL查询时,从数据库只检索分页需要的数据
  • 通常不同的数据库有着不同的物理分页语句
  • MySql/Oracle,每种数据库的分页写法是不同的
  • MySql物理分页采用limit关键字,Oracle物理分页采用rowNum

逻辑分页:

  • 在sql查询时,先从数据库检索出所有数据的结果集,在程序内,通过逻辑语句获得分页需要的数据

2 数据库中的分页使用

2.1 MySql Limit

向MySql的user表中添加4条数据

insert into user values(5,'admin5','def');
insert into user values(6,'admin6','ghi');
insert into user values(7,'admin7','jkl');
insert into user values(8,'admin8','opq');


Limit的使用

select * from user limit m,n

其中m与n为数字。n代表需要获取多少行的数据项,而m代表从哪开始(以0为起始)。
例如我们想从user表中先获取前两行数据项(1-2)的name列数据,则SQL为:

select * from user limit 0,2;

那么如果要继续往下看一页两行的数据项(3-4)则下一步的SQL应该为:

select * from user limit 2,2;

以此类推
分页公式:(当前页-1)*每页大小

2.2 Oracle rownum

向Oracle的orcl_user表中添加4条数据

insert into orcl_user values(5,'admin5','eeefff');
insert into orcl_user values(6,'admin6','ggghhh');
insert into orcl_user values(7,'admin7','iiijjj');
insert into orcl_user values(8,'admin8','kkklll');
commit;

注:Oracle默认不自动提交,需要执行commit提交,否则数据并未真正添加
rownum是对结果集加的一个伪列,即先查到结果集之后再加上去的一个列 (先要有结果集)。
简单的说 rownum 是对符合条件结果的序列号。它总是从1开始排起的。
所以通过rownum进行分页查询,必须先附加rownum。

rownum使用

select orcl_user.*,rownum from orcl_user

结果集为

通过rownum列来实现分页
例如我们想从orcl_user表中先获取前两行数据项(1-2)的name列数据,则SQL为:

select ou.* from (select orcl_user.*,rownum rn from orcl_user) ou where rn > 0 and rn <= 2

那么如果要继续往下看一页两行的数据项(3-4)则下一步的SQL应该为:

select ou.* from (select orcl_user.*,rownum rn from orcl_user) ou where rn > 2 and rn <= 4

以此类推
分页公式:
第一个r n(当前页-1)*每页大小
第二个r n当前页*每页大小

3 JDBC分页

jdbc通过分页关键字实现分页效果,将分页结果存在分页对象中
分页类

package com.bzcxy.page;import java.util.ArrayList;
import java.util.List;
import java.util.Map;public class Page {//当前页数private Integer currPage;//每页显示的记录数private Integer pageSize;//总记录数private Integer totalCount;//总页数private Integer totalPage;//每页显示的数据private List<Map<String,Object>> list = new ArrayList<>();public Integer getCurrPage() {return currPage;}public void setCurrPage(Integer currPage) {this.currPage = currPage;}public Integer getPageSize() {return pageSize;}public void setPageSize(Integer pageSize) {this.pageSize = pageSize;}public Integer getTotalCount() {return totalCount;}public void setTotalCount(Integer totalCount) {this.totalCount = totalCount;}public Integer getTotalPage() {return totalPage;}public void setTotalPage(Integer totalPage) {this.totalPage = totalPage;}public List<Map<String, Object>> getList() {return list;}public void setList(List<Map<String, Object>> list) {this.list = list;}
}

3.1 MySql实现分页查询

分页方法实现

package com.bzcxy.page;import com.bzcxy.jdbc.JDBCUtilsDruid;import java.sql.*;
import java.util.HashMap;
import java.util.Map;public class MySqlPage {public static void main(String[] args) throws Exception {Page page = new Page();//设置当前查询页数page.setCurrPage(2);//每页大小page.setPageSize(2);Page respage = selectPage(page);//输出总页数System.out.println(respage.getTotalPage());//输出总记录数System.out.println(respage.getTotalCount());//输出结果集System.out.println(respage.getList());}public static Page selectPage(Page page) throws Exception {Connection conn = JDBCUtilsDruid.getConnection();String sql = "select * from user limit ?,?";//预处理sqlPreparedStatement ps = conn.prepareStatement(sql);//设置查询页数 (当前页-1)*每页数量ps.setInt(1,(page.getCurrPage() - 1) * page.getPageSize());//设置每页数量ps.setInt(2,page.getPageSize());//执行sqlResultSet rs = ps.executeQuery();//获取元数据ResultSetMetaData rsmd = rs.getMetaData();while (rs.next()){Map<String,Object> map = new HashMap<>();//根据元数据填充mapfor (int i = 0; i < rsmd.getColumnCount(); i ++){String columnName = rsmd.getColumnName(i + 1);String columnValue = rs.getString(i + 1);map.put(columnName,columnValue);}page.getList().add(map);}//查询总记录数sql = "select count(*) from user";ps = conn.prepareStatement(sql);rs = ps.executeQuery();if (rs.next()){//获取总记录数int count = rs.getInt(1);//设置总记录数page.setTotalCount(count);//总页数=总数/每页数量  向上取整Double totalpage = Math.ceil((double) count / (double) page.getPageSize());page.setTotalPage(totalpage.intValue());}return page;}
}

分页测试

Page page = new Page();
//当前查询页数 page.setCurrPage(2);
//每页大小 page.setPageSize(2);
Page respage = selectPage(page);
//输出总页数
System.out.println(respage.getTotalPage());
//输出总记录数
System.out.println(respage.getTotalCount());
//输出结果集
System.out.println(respage.getList());

3.2 Oracle的分页实现

package com.bzcxy.page;import com.bzcxy.jdbc.JDBCUtilsDruid;import java.sql.*;
import java.util.HashMap;
import java.util.Map;public class OraclePage {public static void main(String[] args) throws Exception {Page page = new Page();//设置当前查询页数page.setCurrPage(2);//每页大小page.setPageSize(2);Page respage = selectPage(page);//输出总页数System.out.println(respage.getTotalPage());//输出总记录数System.out.println(respage.getTotalCount());//输出结果集System.out.println(respage.getList());}public static Page selectPage(Page page) throws Exception {String driver = "oracle.jdbc.driver.OracleDriver";String url = "jdbc:oracle:thin:@127.0.0.1:1521:orcl";String user = "system";String password = "root";Class.forName(driver);Connection conn = DriverManager.getConnection(url,user,password);String sql = "select ou.* from (select orcl_user.* , rownum rn from orcl_user) ou where rn > ? and rn <= ?";//预处理sqlPreparedStatement ps = conn.prepareStatement(sql);//第一个rn (当前页-1)*每页数量ps.setInt(1,(page.getCurrPage() - 1) * page.getPageSize());//第二个rn 当前页*每页数量ps.setInt(2,page.getPageSize() * page.getCurrPage());//执行sqlResultSet rs = ps.executeQuery();//获取元数据ResultSetMetaData rsmd = rs.getMetaData();while (rs.next()){Map<String,Object> map = new HashMap<>();//根据元数据填充mapfor (int i = 0; i < rsmd.getColumnCount(); i ++){String columnName = rsmd.getColumnName(i + 1);String columnValue = rs.getString(i + 1);map.put(columnName,columnValue);}page.getList().add(map);}//查询总记录数sql = "select count(*) from orcl_user";ps = conn.prepareStatement(sql);rs = ps.executeQuery();if (rs.next()){//获取总记录数int count = rs.getInt(1);//设置总记录数page.setTotalCount(count);//总页数=总数/每页数量  向上取整Double totalpage = Math.ceil((double) count / (double) page.getPageSize());page.setTotalPage(totalpage.intValue());}return page;}
}

分页测试

Page page = new Page();
//当前查询页数
page.setCurrPage(2);
//每页大小
page.setPageSize(2);
Page respage = selectPage(page);
//输出总页数
System.out.println(respage.getTotalPage());
//输出总记录数
System.out.println(respage.getTotalCount());
//输出结果集
System.out.println(respage.getList());

网页编程与设计:HTML5

数据库编程和设计——JDBC技术相关推荐

  1. 数据库编程——intro to JDBC

    [0]README 1) 本文文字描述 转自 core java volume 2 , 旨在理解 数据库编程--JDBC 的基础知识 : 2)JDBC起源: 96年, Sun公司发布了 第一版的jav ...

  2. 数据库编程与设计—SQL语言

    一.SQL语言基础 1 什么是SQL 结构化查询语言结构化查询语言(Structured Query Language)简称 SQL(发音:sequal['si:kwəl]),是一种数据库查询和程序设 ...

  3. C#——《C#语言程序设计》实验报告——数据库编程——基于ADO.NET技术和WPF技术的简单数据库可视化工具DEMO

    一.实验目的 掌握ADO .NET数据离线与在线访问模型: 掌握应用LINQ进行数据查询: 继续应用WPF技术进行界面编程. 二.实验内容 使用提供的Northwind.mdb数据库,利用DataGr ...

  4. C#——《C#语言程序设计》实验报告——数据库编程——基于ADO.NET技术的数据库操作DEMO

    一.实验目的 掌握ADO .NET数据离线与在线访问模型: 掌握应用LINQ进行数据查询: 二.实验内容 基于ADO.NET技术进行数据操作 已提供一个控制台程序的框架.附件中提供了各种格式的Nort ...

  5. Java数据库编程(JDBC)-入门笔记

    数据库(DB) 简介: • DB: Database = Data + Base • 数据库:数据+库,存放数据的库(场所) • 数据:规范.半规范.不规范数据 • 库 – 一个空间,一个场所 – 停 ...

  6. 【Java】数据库编程

    Java中数据库编程是通过JDBC实现的.使用JDBC技术涉及三种不同的角色:Java官方,开发人员和数据库厂商.如下图所示: Java官方提供JDBC接口,如:Connection,Statemen ...

  7. JAVA数据库编程(JDBC技术)-入门笔记

    本菜鸟才介入Java,我现在不急着去看那些基本的语法或者一些Java里面的版本的特征或者是一些晋级的知识,因为有一点.Net的OOP编程思想,所以对于Java的这些语法以及什么的在用到的时候在去发现学 ...

  8. java的数据库连接编程(jdbc)技术_Java的数据库连接编程(JDBC)技术

    Java的数据库连接编程(JDBC)技术 Java的数据库连接编程(JDBC)技术 [本讲的知识要点]:JDBC.JDBC的工作原理,访问数据库的方法.Statement.PreparedStatem ...

  9. 数据库-----JDBC技术

    目录 JDBC概述 数据的持久化 什么是 JDBC 什么是数据库驱动程序 Java中访问数据库技术 程序操作数据库流程 JBDC中常用的类与接口 Driver 接口 DriverManager 类 C ...

最新文章

  1. 第一个android程序HelloWorld
  2. 0. 正规鞅的混沌及可料表示
  3. java符号%3e%3e是什么意思,终于找到了!有了它你就可以读懂字节码了!
  4. 计算机ppt文字1是什么原因,ppt让答案一个个出现,ppt让文字一个个出现
  5. java todo error_运行我的第一个Java应用程序出错
  6. Git时出现“error: 源引用表达式 main 没有匹配 error: 推送一些引用到 ‘https://github.com/***.git‘ 失败”的错误提示
  7. 自然语言处理——语言模型(一)
  8. php中的ul怎么居中,让 UL 与 LI 左对齐
  9. 安装软件提示计算机管理员权限,Win7安装软件需要管理员权限的解决方法
  10. java语言判断101到200之间素数
  11. speedoffice(Excel)如何隐藏编辑栏
  12. Nepxion Discovery(2) 全链路条件蓝绿发布
  13. BeyondCompare的三种破解方法
  14. python中英文切换_python国际化(i18n)和中英文切换
  15. PHP socket以及http、socket、tcp、udp
  16. python编写游戏测试机器人客户端(四)
  17. 苹果iPad怎么录屏?简单易懂,1分钟学会
  18. 【Python】在字符串的头尾做文本匹配
  19. Kotlin Native - 原生平台 Hollo World!
  20. Java习题练习:拉马车

热门文章

  1. Spring详解-------依赖注入的三种方式实例详解
  2. 用php web编程作业,代做CSE2ISD作业、代做Web,php程序作业、代写Java/web编程作业、代写C/C++/Java留学生作业...
  3. apple个人开发者证书无线发布app的实现
  4. 动态规划入门——多重背包与单调优化
  5. Leetcode 最接近的三数之和
  6. Linux下把WIFI网卡设置成AP热点
  7. 多函数计算并返回n的阶乘
  8. nginx 配置域名映射到本地IP
  9. Python3之编码encoding='cp936'全面认识
  10. java mongodb 备份_mongodb 备份、还原、导入、导出简单操作