【Java Se】JDBC

2023-05-29,,

启停服务
net start mysql
net stop mysql

登录

mysql -u -p

访问指定IP的mysql

mysql -u root -P 3306 -h localhost -pabc123

-p后面不能加空格

-h表示host,-h-P均为默认本机

两种架构

B/S:Browser Server

C/S:Client Server

1 JDBC(Java Database Connectivity)

是一个独立于特定数据库管理系统、通用的SQL数据库存储和操作的公共接口,接口封装了一系列抽象方法,定义了用来访问数据库的标准Java类库(java.sql.javax.sql),使用这些类库可以以一种比较标准的方法,方便地访问数据库资源。

2 JDBC程序编写步骤

2.1 获取数据库连接方式一:

2.1.1 Driver接口实现类

java.sql.Driver接口是所有JDBC驱动程序需要实现的接口。由数据库厂商提供,不同数据库厂商提供不同的实现

Driver driver = new com.mysql.jdbc.Driver();
2.1.2 URL
String url = "jdbc:mysql://localhost:3306/mydb";

jdbc:协议

mysql:子协议

localhost:ip

3306:端口号

mydb:数据库名称

?:可添加关键字

2.1.3 info
        Properties info = new Properties();
info.setProperty("user", "root");
info.setProperty("password", "abc123");

用一个Properities封装数据库的用户名和密码

2.1.4 获得连接
        Connection connection = driver.connect(url, info);

合起来:

    @Test
public void testConnection() throws SQLException {
Driver driver = new com.mysql.jdbc.Driver(); String url = "jdbc:mysql://localhost:3306/mydb";
Properties info = new Properties();
info.setProperty("user", "root");
info.setProperty("password", "abc123"); Connection connection = driver.connect(url, info); System.out.println(connection);
}

2.2 方式二  :利用反射对方式一进行迭代

    @Test
public void testConnection1() throws ClassNotFoundException, IllegalAccessException, InstantiationException, SQLException {
Class clazz = Class.forName("com.mysql.jdbc.Driver");
Driver driver = (Driver) clazz.newInstance(); String url = "jdbc:mysql://localhost:3306/mydb";
Properties properties = new Properties();
properties.setProperty("user", "root");
properties.setProperty("password", "abc123"); Connection connection = driver.connect(url, properties);
}

利用反射的动态性动态获取数据库驱动Driver实现类

2.3 方式三:使用DriverManager替换Driver

    @Test
public void testConnection2() throws ClassNotFoundException, IllegalAccessException, InstantiationException, SQLException {
Class clazz = Class.forName("com.mysql.jdbc.Driver");
Driver driver = (Driver) clazz.newInstance();
DriverManager.registerDriver(driver); String url = "jdbc:mysql://localhost:3306/mydb";
String user = "root";
String password = "abc123"; Connection connection = DriverManager.getConnection(url, user, password);
System.out.println(connection);
}

DriverManager.registerDriver(driver):注册驱动

2. 4 方式四:可以只是加载驱动,不用显示地注册驱动了

    @Test
public void testConnection3() throws ClassNotFoundException, IllegalAccessException, InstantiationException, SQLException {
String url = "jdbc:mysql://localhost:3306/mydb";
String user = "root";
String password = "abc123"; Class.forName("com.mysql.jdbc.Driver"); Connection connection = DriverManager.getConnection(url, user, password);
System.out.println(connection);
}

这一方面是由于反射机制会加载Driver类到内存,另一方面Driver父类com.mysql.cj.jdbc.Driver中存在静态代码块,在加载到内存时就进行了隐式地注册。

public class Driver extends NonRegisteringDriver implements java.sql.Driver {
public Driver() throws SQLException {
} static {
try {
DriverManager.registerDriver(new Driver());
} catch (SQLException var1) {
throw new RuntimeException("Can't register driver!");
}
} }
2.5 方式五(伪):java.sql.Driver文件在jar包导入时就加载了Driver父类com.mysql.cj.jdbc.Driver
    @Test
public void testConnection4() throws ClassNotFoundException, IllegalAccessException, InstantiationException, SQLException {
String url = "jdbc:mysql://localhost:3306/mydb";
String user = "root";
String password = "abc123"; // Class.forName("com.mysql.jdbc.Driver"); Connection connection = DriverManager.getConnection(url, user, password);
System.out.println(connection);
}

2.5 方式五(final版)配置文件

    @Test
public void testConnection5() throws IOException, ClassNotFoundException, SQLException {
//1.读取配置文件的四个信息:通过类的系统类加载器读取配置文件
InputStream inputStream = ConnectionTest.class.getClassLoader().getResourceAsStream("jdbc.properties");
Properties properties = new Properties();
properties.load(inputStream); String url = properties.getProperty("url");
String user = properties.getProperty("user");
String password = properties.getProperty("password");
String driverClass = properties.getProperty("driverClass");
//2.加载驱动
Class.forName(driverClass);
//3.获得连接
Connection connection = DriverManager.getConnection(url, user, password);
System.out.println(connection);
}

实现了数据和代码的分离,实现了解耦

如果需要修改配置文件信息,可以避免项目重新打包

3 操作和访问数据库

数据库连接用于向数据库发送命令和SQL语句,并接受数据库服务器返回的结果。其实一个数据库连接就是一个Socket连接。

在java.sql包中有3个接口分别定义了对数据库的调用的不同方式:

3.1 Statement:用于向数据库发送SQL语句

    @Test
public void testLogin() throws IOException, ClassNotFoundException, SQLException {
InputStream resourceAsStream = StatementTest.class.getClassLoader().getResourceAsStream("jdbc.properties");
Properties info = new Properties();
info.load(resourceAsStream); String url = info.getProperty("url");
String user = info.getProperty("user");
String pwd = info.getProperty("password");
String driverClass = info.getProperty("driverClass"); Class.forName(driverClass);
Connection connection = DriverManager.getConnection(url, user, pwd);
String sql = "SELECT * FROM USER WHERE uer = 'AA' AND pwd = '123456'"; Statement statement = connection.createStatement();
ResultSet rs = statement.executeQuery(sql); while(rs.next()) {
System.out.println(rs.getString(1) + " " + rs.getString(2));
} }

Statement的缺陷

①需要拼写sql语句

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

SELECT * FROM USER WHERE uer = '1' OR ' AND pwd = ' =1 OR '1' = '1'

③导致恶意攻击

3.2 PrepatedStatement:SQL语句被预编译并存储在此对象中,可以使用此对象多次高效地执行该语句

    @Test
public void preparedStatementTest() throws ClassNotFoundException, SQLException, IOException {
InputStream resourceAsStream = StatementTest.class.getClassLoader().getResourceAsStream("jdbc.properties");
Properties info = new Properties();
info.load(resourceAsStream); String url = info.getProperty("url");
String user = info.getProperty("user");
String pwd = info.getProperty("password");
String driverClass = info.getProperty("driverClass"); Class.forName(driverClass);
Connection connection = DriverManager.getConnection(url, user, pwd); String sql = "INSERT INTO USER(uer, pwd) VALUES (?, ?)";
PreparedStatement ps = connection.prepareStatement(sql); ps.setString(1, "111111");
ps.setString(2, "111111"); boolean result = ps.execute(); ps.close();
connection.close(); System.out.println(result);
}

execute方法返回一个boolean值,如果第一个结果是ResultSet对象则返回true,如果是更新计数或者没有返回结果则返回false。所以执行插入或者更新语句应该使用executeUpdate。

用于获取关闭连接的JDBCUtils工具类:

public class JDBCUtils {
public static Connection getConnection() {
Connection conn = null;
InputStream resourceAsStream = null;
try {
resourceAsStream = JDBCUtils.class.getClassLoader().getResourceAsStream("jdbc.properties");
Properties info = new Properties();
info.load(resourceAsStream); String user = info.getProperty("user");
String password = info.getProperty("password");
String url = info.getProperty("url");
String driverClass = info.getProperty("driverClass"); Class.forName(driverClass);
conn = DriverManager.getConnection(url, user, password);
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException throwables) {
throwables.printStackTrace();
} finally {
} return conn;
}
public static void resourceClose(Connection conn, Statement statement) {
try {
conn.close();
statement.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
} }

可用作实现增删改的通用方法:

public void update(String sql, Object ...args) {
Connection conn = null;
PreparedStatement ps = null;
try {
conn = JDBCUtils.getConnection();
ps = conn.prepareStatement(sql); for(int i=0;i<args.length;i++)
ps.setObject(i+1, args[i]);
ps.execute();
} catch (SQLException throwables) {
throwables.printStackTrace();
} finally {
JDBCUtils.resourceClose(conn, ps);
}
}

测试:

    @Test
public void testUpdate() throws ParseException {
String sql = "insert into user(uer,pwd,birthday) values(?,?,?)";
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
java.util.Date date = sdf.parse("1997-05-30");
java.sql.Date birthday =new Date(date.getTime()); // System.out.println(birthday);
update(sql, "my", "pwdd", birthday);
}

注意这里通过sql.Date毫秒数的构造器实现了util.Date与sql.Date的转换:

        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
java.util.Date date = sdf.parse("1997-05-30");
java.sql.Date birthday =new Date(date.getTime());

ResultSet查询结果集

    @Test
public void queryTest() {
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
String sql = "select uer, pwd, birthday from user where id=?"; try {
conn = JDBCUtils.getConnection();
ps = conn.prepareStatement(sql);
ps.setInt(1, 14);
rs = ps.executeQuery(); while(rs.next()) {
String user = rs.getString(1);
String pwd = rs.getString(2); System.out.println(user + ", " + pwd);
}
} catch (SQLException throwables) {
throwables.printStackTrace();
} finally {
JDBCUtils.resourceClose(conn, ps, rs);
}
}

区别于迭代器的next,结果集的next方法判断为空的同时还进行了指针下移

★★★★★利用反射实现通用查询操作:(ResultSetMeta确定字段列数和列名)
public User query(String sql, Object ...args) {
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
User user = null;
try {
conn = JDBCUtils.getConnection();
ps = conn.prepareStatement(sql); for(int i=0;i<args.length;i++) {
ps.setObject(i + 1, args[i]);
}
rs = ps.executeQuery();
Class clazz = User.class; while(rs.next()) {
ResultSetMetaData metaData = rs.getMetaData();
user = new User();
for(int i=0;i<metaData.getColumnCount();i++) {
Object value = rs.getObject(i+1);
String value_name = metaData.getColumnName(i+1); // String value_set_method = "set" + value_name.substring(0,1).toUpperCase(Locale.ROOT) + value_name.substring(1);
// System.out.println(value_set_method);
// Method method = clazz.getMethod(value_set_method, String.class);
// method.invoke(user, value); Field field = clazz.getDeclaredField(value_name);
field.setAccessible(true);
field.set(user, value);
}
}
} catch (SQLException throwables) {
throwables.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (NoSuchFieldException e) {
e.printStackTrace();
} finally {
JDBCUtils.resourceClose(conn,ps,rs);
}
return user;
}

这里只能使用反射Field的set方法,使用Method的invoke方法不能确定参数类型(实际也能通过ResultSetMetaData获取数据库字段类型对应java数据类型,但是很麻烦)

    @Test
public void queryTest1() {
String sql = "select uer,pwd,birthday from User where id = ?";
User user = query(sql, 14);
System.out.println(user);
}

针对数据库字段名和java类属性名不一致的情况:

①必须使用java类的属性名命名字段的别名

String sql = "select user_uer uer,user_pwd pwd,user_birthday birthday from User where id = ?";

②ResultSetMetaData的getColumnName()方法获取的是数据库的字段名,getColumnLabel()方法获取的是别名

★利用泛型方法实现对不同表的查询操作

    public <T> T query(Class<T> clazz, String sql, Object ...args) {
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
T t = null;
try {
conn = JDBCUtils.getConnection();
ps = conn.prepareStatement(sql); for(int i=0;i<args.length;i++) {
ps.setObject(i + 1, args[i]);
}
rs = ps.executeQuery(); while(rs.next()) {
ResultSetMetaData metaData = rs.getMetaData();
t = clazz.newInstance();
for(int i=0;i<metaData.getColumnCount();i++) {
Object value = rs.getObject(i+1);
String value_name = metaData.getColumnName(i+1); // String value_set_method = "set" + value_name.substring(0,1).toUpperCase(Locale.ROOT) + value_name.substring(1);
// System.out.println(value_set_method);
// Method method = clazz.getMethod(value_set_method, String.class);
// method.invoke(user, value); Field field = clazz.getDeclaredField(value_name);
field.setAccessible(true);
field.set(t, value);
}
}
} catch (SQLException throwables) {
throwables.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} finally {
JDBCUtils.resourceClose(conn,ps,rs);
}
return t;
}

PreparedStatement为什么能防止SQL注入?

PreparedStatement实行预编译,在还没有填充占位符的时候就确定了sql语句的逻辑结构。

PreparedStatement可以使用流填充,因此可以操作Blob型数据向数据库传输视频、图片等。

PreparedStatement可以实现更高效的批量操作:对于sql语句只需要校验一次然后缓存到PreparedStatement对象中,之后只需要对占位符进行填充即可。

Blob数据

    @Test
public void BlobTest() {
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
conn = JDBCUtils.getConnection();
String sql = "select photo from user where id=23";
ps = conn.prepareStatement(sql);
rs = ps.executeQuery(); while(rs.next()) { Blob blob= rs.getBlob(1); InputStream is = blob.getBinaryStream();
FileOutputStream fos = new FileOutputStream("2B.jpg");
byte[] buffer = new byte[1024];
int len;
while((len = is.read(buffer)) != -1) {
fos.write(buffer, 0, len);
}
}
} catch (SQLException | FileNotFoundException throwables) {
throwables.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
}
}
类型 大小(字节)
TinyBlob 255
Blob 65K
MediumBlob 16M
LongBlob 4G

批量插入数据

①update、delete本身就具有批量操作的能力。此处的批量操作,主要是指批量插入:

@Test
public void testInsert1() {
Connection conn = null;
PreparedStatement ps = null;
try {
conn = JDBCUtils.getConnection();
String sql = "insert into goods(name) values(?)";
// boolean flag = true;
for(int i=0;i<20000;i++) {
ps = conn.prepareStatement(sql);
String str = "goods" + (i + 1);
ps.setString(1, str);
ps.executeUpdate();
} } catch (SQLException throwables) {
throwables.printStackTrace();
} finally {
try {
if(conn != null) {
conn.close();
}
} catch (SQLException throwables) {
throwables.printStackTrace();
}
try {
if(ps != null) {
ps.close();
}
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}

②此种方式每次填充完占位符都需要与数据库交互,效率较低,可以存积一定数量ps数据库操作再与数据库交互:

            for(int i=0;i<20000;i++) {
String str = "goods" + (i + 1);
ps.setString(1, str);
ps.addBatch();
if((i+1)%500==0) {
ps.executeBatch();
ps.clearBatch();
}
}

mysql默认服务器是关闭批处理的,我们需要一个在配置文件中加一个url参数:

url=jdbc:mysql://localhost:3306/mydb?rewriteBatchedStatements=true

时间由20s变为277ms

③conn.setAutoCommit(false):设置连接不可自动提交数据

            conn = JDBCUtils.getConnection();
conn.setAutoCommit(false);
String sql = "insert into goods(name) values(?)";
ps = conn.prepareStatement(sql);
// boolean flag = true;
long stime = System.currentTimeMillis();
for(int i=0;i<1000000;i++) {
String str = "goods" + (i + 1);
ps.setString(1, str);
ps.addBatch();
if((i+1)%500==0) {
ps.executeBatch();
ps.clearBatch();
}
}
conn.commit();
long etime = System.currentTimeMillis();
System.out.println(etime - stime);

3.3 数据库事务

事务:一组逻辑单元,使数据库从一种状态变换到另一种状态。

事务处理:保证所有事务都作为一个工作单元来执行,即使出现了故障,都不能改变这种工作方式。当一个事务中执行多个操作,要么所有的事务都被提交(commit),那么这些修改就被永久地保存下来,要么数据库放弃所有修改,整个事务回滚(rollback) 到最初状态。

哪些操作会导致数据库的自动提交?

①DDL操作一旦执行,都会自动提交,set autocommit = false对DDL操作无效。

②DML默认情况会自动提交,可以通过set autocommit = false的方式取消DML操作的自动提交。

DML(Data Manipulation Language)数据操纵语言:

适用范围:对数据库中的数据进行一些简单操作,如insert,delete,update,select等.

DDL(Data Definition Language)数据定义语言:

适用范围:对数据库中的某些对象(例如,database,table)进行管理,如Create,Alter和Drop.

    @Test
public void updateTest() { Connection conn = null;
try {
conn = JDBCUtils.getConnection();
conn.setAutoCommit(false); // System.out.println(conn.getAutoCommit()); String sql1 = "update user set balance = balance - 100 where uer=?";
update(conn, sql1, "AA"); System.out.println(1/0); String sql2 = "update user set balance = balance + 100 where uer=?";
update(conn, sql2, "2B"); System.out.println("转账成功");
conn.commit(); } catch (SQLException throwables) {
throwables.printStackTrace();
} finally {
try {
if(conn!=null) {
conn.close();
}
} catch (SQLException throwables) {
throwables.printStackTrace();
}
} } ##### ※ 代码块在子类对象生成前获取父类的泛型参数,

private Class clazz = null;

{
//代码块在子类对象生成前获取父类的泛型参数
Type genericSuperclass = this.getClass().getGenericSuperclass();
ParameterizedType parameterizedType = (ParameterizedType) genericSuperclass; Type[] typeArguments = parameterizedType.getActualTypeArguments();
clazz = (Class<T>) typeArguments[0]; }
这么做的目的是为了不用因为反射需要,每次都写明类型

public List getForList(Connection conn, String sql, Object...args) {

List list = new ArrayList<>();

PreparedStatement ps = null;

T t = null;

ResultSet rs = null;

    try {
ps = conn.prepareStatement(sql); for(int i=0;i<args.length;i++) {
ps.setObject(i+1, args[i]);
} rs = ps.executeQuery();
while(rs.next()) {
ResultSetMetaData metaData = rs.getMetaData();
t = clazz.newInstance();
for(int i=0;i<metaData.getColumnCount();i++) {
Object value = rs.getObject(i+1);
Field field = clazz.getDeclaredField(metaData.getColumnName(i+1));
field.setAccessible(true);
field.set(t, value);
}
list.add(t);
}
} catch (SQLException throwables) {
throwables.printStackTrace();
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} finally {
JDBCUtils.resourceClose(null, ps, rs);
} return list;
}

4 数据库连接池

在使开发基于数据库的web程序时,基本按照以下步骤:先在主程序中建立数据库连接,然后执行sql操作,最后断开sql连接。

这种开发模式存在的问题:

普通的JDBC数据库连接使用DriverManager获取,每次连接数据库都需要将Connection加载到内存,并且每次都需要验证用户名密码花费时间。数据库连接资源没有得到很好的重复利用。

每次连接使用完后都得断开,如果程序出现异常没有关闭,将会导致数据库系统中的内存泄漏,最终导致重启数据库。

内存对象不能被JVM回收的情况,称为内存泄漏

这种开发不能控制被创建的连接对象数,系统资源毫无顾忌地分配出去,如连接过多,也可能导致内存泄漏,服务器崩溃。

4.1 多种开源的数据库连接池

JDBC的数据库连接池java.sql.DataSource来表示,DataSource只是一个接口,该接口由服务器(Weblogic,WebSphere,Tomcat)提供实现。

C3P0

方式一:硬编码方式创建数据库连接池

    @Test
public void test() throws Exception {
ComboPooledDataSource cpds = new ComboPooledDataSource();
cpds.setDriverClass( "com.mysql.cj.jdbc.Driver" ); //loads the jdbc driver
cpds.setJdbcUrl( "jdbc:mysql://localhost/mydb" );
cpds.setUser("root");
cpds.setPassword("abc123");
//设置初始时数据库连接池的参数
cpds.setInitialPoolSize(10); Connection connection = cpds.getConnection();
System.out.println(connection);
}

方式二:通过配置文件设置数据库连接池

<c3p0-config>
<named-config name="HelloC3P0">
<!-- 提供获取连接的4个基本信息 -->
<property name="DriverClass">com.mysql.cj.jdbc.Driver</property>
<property name="JdbcUrl">jdbc:mysql://localhost:3306/mydb</property>
<property name="User">root</property>
<property name="Password">abc123</property> <!-- 数据库连接池管理的基本信息 --> <!-- 当数据库连接池中的连接数不足时,数据库连接池一次性申请的连接数-->
<property name="acquireIncrement">5</property>
<!-- C3P0数据库连接池维护的初始化连接数 -->
<property name="initialPoolSize">10</property>
<!-- C3P0数据库连接池维护的最少连接数 -->
<property name="minPoolSize">10</property>
<!-- C3P0数据库连接池维护的最多连接数 -->
<property name="maxPoolSize">100</property>
<!-- C3P0数据库连接池维护的最多Statement的个数-->
<property name="maxStatements">0</property>
<!-- 每个连接最多使用的Statement个数-->
<property name="maxStatementsPerConnection">5</property> </named-config>
</c3p0-config>

获取连接

    @Test
public void test1() throws SQLException {
ComboPooledDataSource cpds = new ComboPooledDataSource("HelloC3P0");
Connection conn = cpds.getConnection();
System.out.println(conn);
}
DBCP
Druid(德鲁伊)数据库连接池

配置文件

url=jdbc:mysql://localhost:3306/mydb
username=root
password=abc123
driverClassName=com.mysql.cj.jdbc.Driver initialSize=10
maxActive=10
    @Test
public void test2() throws Exception {
Properties properties = new Properties();
InputStream resourceAsStream = ClassLoader.getSystemClassLoader().getResourceAsStream("druid.properties");
properties.load(resourceAsStream); DataSource dataSource = DruidDataSourceFactory.createDataSource(properties); Connection conn = dataSource.getConnection();
System.out.println(conn);
}

5 Apache DBUTILS

commons-dbutils是Apache组织提供的一个开源JDBC工具类库,封装了针对于数据库的增删改查操作。

DButils增删改操作
    @Test
public void test() {
Connection conn = null;
try {
QueryRunner qr = new QueryRunner();
conn = JDBCUtils.getConnection();
String sql = "insert into user(user,balance) values(?,?)";
int insert_count = qr.update(conn, sql, "1A", 100000);
System.out.println(insert_count);
} catch (SQLException throwables) {
throwables.printStackTrace();
} finally {
JDBCUtils.resourceClose(conn, null);
} }
DButils查询操作

runner参数包括:

query(Connection conn, String sql, ResultSetHandler rsh, Object... params)

其中ResultSetHandler为接口,需要创建一个接口实现类,可供使用的接口实现类有:

AbstractKeyedHandler, AbstractListHandler, ArrayHandler, ArrayListHandler, BeanHandler, BeanListHandler, ColumnListHandler, KeyedHandler, MapHandler, MapListHandler, ScalarHandler

BeanListHandler 用于返回一条数据
       BeanHandler<User> handler = new BeanHandler<>(User.class)
    @Test
public void test1() {
Connection conn = null;
try {
conn = JDBCUtils.getConnection();
QueryRunner runner = new QueryRunner();
String sql = "select user,balance from user where id = ?";
BeanHandler<User> handler = new BeanHandler<>(User.class); User user = runner.query(conn, sql, handler, 1);
System.out.println(user);
} catch (SQLException throwables) {
throwables.printStackTrace();
} finally {
JDBCUtils.resourceClose(conn, null);
}
}
BeanListHandler用于返回多条数据
BeanListHandler<User> handler = new BeanListHandler<>(User.class);
    @Test
public void test2() {
Connection conn = null;
try {
conn = JDBCUtils.getConnection();
QueryRunner runner = new QueryRunner();
String sql = "select user,balance from user";
BeanListHandler<User> handler = new BeanListHandler<>(User.class); List<User> list = runner.query(conn, sql, handler);
list.forEach(System.out::println);
} catch (SQLException throwables) {
throwables.printStackTrace();
} finally {
JDBCUtils.resourceClose(conn, null);
}
}

MapListHandler将字段作为key,字段值为value返回map组成的list

    @Test
public void test3() {
Connection conn = null; try {
conn = JDBCUtils.getConnection();
QueryRunner runner = new QueryRunner();
String sql = "select user, balance from user"; MapListHandler handler = new MapListHandler();
List<Map<String, Object>> list = runner.query(conn, sql, handler); list.forEach(System.out::println);
} catch (SQLException throwables) {
throwables.printStackTrace();
} finally {
JDBCUtils.resourceClose(conn, null);
}
}
ScalarHandler用于特殊查询
    @Test
public void test4() {
Connection conn = null;
try {
conn = JDBCUtils.getConnection();
QueryRunner runner = new QueryRunner();
String sql = "select count(*) from user"; ScalarHandler handler = new ScalarHandler();
Long count = (Long) runner.query(conn, sql, handler); System.out.println(count); } catch (SQLException throwables) {
throwables.printStackTrace();
} finally {
JDBCUtils.resourceClose(conn, null);
}
}

这里只能用Long

【Java Se】JDBC的相关教程结束。

《【Java Se】JDBC.doc》

下载本文的Word格式文档,以方便收藏与打印。