@[TOC](Java 数据库)
JDBC
原理
Java Data Base Connectivity java数据库连接,一种执行SQL语句的API,屏蔽数据库之间的差异,是java访问数据库的一种规范
它由一组用java语言 编写的类和接口组成,开发者只需调用接口方法,具体的实现类需要由数据库产商来提供
JDBC 连接需要驱动,这个驱动就是数据库产商提供的实现类
java的mysql驱动类为java-connection
这里使用的是java-connection-5.1.37版本的
开发步骤
- 注册驱动 告诉JVM使用哪个数据库驱动
- 获得连接 基于TCP协议的,效率低
- 获得语句执行平台 获得执行者对象
- 执行sql语句
- 处理结果
- 释放资源 TCP 连接资源
try {
// 注册驱动
// DriverManager.registerDriver(new Driver());
// 推荐使用反射的方式注册驱动,源码已经注册了一次驱动
Class.forName("com.mysql.jdbc.Driver");
// 获得数据库连接 DriverManager类中的静态方法
// static Connection getConnection(String url, String user, String password)
// 返回值Connection接口的实现类com.mysql.jdbc.JDBC4Connection,在驱动包里,注意我们要用的只是sql包的连接接口
// url: jdbc:mysql://连接主机IP:端口号//数据库名
String url = "jdbc:mysql://localhost:3306/tp_wish";
String user = "root";
String password = "076339";
Connection con = DriverManager.getConnection(url, user, password);
System.out.println(con);
// 3. 获得语句执行平台 获得执行者对象
// Statement createStatement()
// 返回值又是Statement接口的实现类对象,获取该对象将sql语句发送到数据库
Statement stat = con.createStatement();
// 4. 执行sql语句
// int executeUpdate(String sql) 仅限于执行INSERT、UPDATE 或 DELETE 语句
// 返回值为操作成功多少行
int row = stat.executeUpdate("INSERT INTO hd_wish(username, content, time) VALUES ('java' ,'使用javaJDBC啦', "
+ System.currentTimeMillis() / 1000 + ")");
System.out.println(row);
// 6. 释放资源
stat.close();
con.close();
} catch (ClassNotFoundException | SQLException e) {
e.printStackTrace();
}
执行 INSERT INSERT、UPDATE 或 DELETE 语句
public static void function(Statement stat) throws SQLException {
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append("INSERT INTO hd_wish(username, content, time) VALUES ('java' ,'使用javaJDBC啦', ")
.append(System.currentTimeMillis() / 1000).append(")");
int row = stat.executeUpdate(stringBuilder.toString());
System.out.println(row);
}
执行并处理select语句
public static void function_1(Statement stat) throws SQLException {
// ResultSet executeQuery(String sql)
// 返回的还是ResultSet接口实现类 永远不会为null
StringBuilder sql = new StringBuilder("SELECT * FROM hd_wish");
ResultSet rs = stat.executeQuery(sql.toString());
System.out.println(rs);
// boolean next() 将光标从当前位置向后移一行。
while (rs.next()) {
// getXXX()方法获取结果集 建议采用列名字的形式获取
int id = rs.getInt("id");
String username = rs.getString("username");
String content = rs.getString("content");
int time = rs.getInt("time");
System.out.println(new StringBuffer().append(id).append(" ").append(username).append(" ")
.append(content).append(" ").append(time).toString());
}
rs.close();
}
如果不考虑结果集类型,可以直接用getObject和getString方法
预编译SQL语句
直接写sql语句在程序中字符串拼接是有SQL注入隐患的
因此java中提供了子接口 PrepareStatement接口预编译,防止注入,并高效执行sql语句
这个接口实现类,也在驱动中
PrepareStatement
- 调用connection对象创建PrepareStatement对象
- 拼接sql语句以问号占位符的方式出现
- 调用PrepareStatement的set方法设置占位符参数
- PrepareStatement对象的执行sql语句方法,此时不需要传递sql参数
public static void function_2(Connection con) throws SQLException {
// 获取PreparedStatement接口实现类
// PreparedStatement prepareStatement(String sql)
// 注意此时的sql语句不再拼接,要以问号形式出现
String sql = "SELECT * FROM hd_wish WHERE username=? AND id=?";
PreparedStatement pst = con.prepareStatement(sql);
// 调用PreparedStatement 的set方法设置占位符的参数
pst.setString(1, "java");
pst.setString(2, "23");
// 调用PreparedStatement 接口来执行sql语句,这时就不需要传递sql参数了
ResultSet rs = pst.executeQuery();
rs.close();
pst.close();
}
JDBC 工具类
读取配置文件,获取连接
创建一个properties属性配置文件,配置、mysql连接参数,这个文件最好放在src目录下
driverClass=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/tp_wish
username=root
password=076339
读取该属性文件获取参数
读取该文件时要注意文件路径,不能写死在src下,因为编译打包后就没有src目录了
因此要采用类加载的方式找到该文件
ClassLoader getClassLoader()
InputStream getResourceAsStream(String name)
public class JDBCUtil {
private JDBCUtil() {
}
private static Connection con;
static {
InputStream in = JDBCUtil.class.getClassLoader().getResourceAsStream("database.properties");
Properties pro = new Properties();
try {
pro.load(in);
String driverClass = pro.getProperty("driverClass");
String url = pro.getProperty("url");
String username = pro.getProperty("username");
String password = pro.getProperty("password");
Class.forName(driverClass);
con = DriverManager.getConnection(url, username, password);
} catch (IOException | ClassNotFoundException | SQLException e) {
e.printStackTrace();
}
}
public static Connection getCon() {
return con;
}
}
DBUtils 工具类
Apache Commons DbUtils 1.6 API 是Apache提供的开源数据库工具类,可以帮助我们简化快捷操作数据库的增删改查
QueryRunner
update
int update(Connection conn, String sql, Object... params)
用于插入数据
public static void insert() throws SQLException {
// 创建QueryRunner 对象
QueryRunner qr = new QueryRunner();
String sql = "INSERT INTO hd_wish(username, content, time) VALUES (?,?,?)";
Object[] params = { "dbUtils", "dbUtils学习", System.currentTimeMillis() / 1000 };
int row = qr.update(con, sql, params);
System.out.println(row);
}
用于更新数据
public static void update() throws SQLException {
QueryRunner qr = new QueryRunner();
String sql = "UPDATE hd_wish SET username=?, content=?, time=? WHERE id=?";
Object[] param = { "DbUtils", "DbUtils学习,更新数据", System.currentTimeMillis() / 1000, 24 };
int row = qr.update(con, sql, param);
System.out.println(row);
}
用于删除数据
private static void delete() throws SQLException {
QueryRunner qr = new QueryRunner();
String sql = "DELETE FROM hd_wish WHERE id=?";
int row = qr.update(con, sql, 20);
System.out.println(row);
DbUtils.closeQuietly(con);
}
ResultSetHandler
处理结果集,这个接口可以帮我们指定数据库查询出来的结果以怎样的方式来存储
这个接口用于 query方法查询中,
query(Connection conn, String sql, ResultSetHandler
rsh, Object... params)
其中rsh就是结果集处理方式,这个接口常用的有8大实现类,分别对应着将数据集转为
对象数组,对象数组列表,bean对象,bean对象列表,列列表,单值,map表,map列表
该方法的返回值是一个泛型,这个泛型会根据ResultSetHandler来定
ArrayHandler
把结果集用Object[]数组封装,线程安全类
拿到的是结果集的第一行数据,类似于php中的find
如果没有结果集,那么数组就为空
public static void arrayHandler() throws SQLException {
QueryRunner qr = new QueryRunner();
String sql = "SELECT * FROM hd_wish";
Object[] objs = qr.query(con, sql, new ArrayHandler());
for (Object obj : objs) {
System.out.print(obj + "\t");
}
}
ArrayListHandler
public static void arrayListHandler() throws SQLException {
QueryRunner qr = new QueryRunner();
String sql = "SELECT * FROM hd_wish";
List
BeanHandle
BeanHandle 的构造器不是一个空参的,它需要传递一个class文件对象,目的是为了反射创建出bean对象!
如果没有查到结果集,返回对象就为空
public static void beanHandler() throws SQLException {
QueryRunner qr = new QueryRunner();
String sql = "SELECT * FROM hd_wish";
Wish wish = qr.query(con, sql, new BeanHandler(Wish.class));
System.out.println(wish);
}
BeanListHandle
private static void beanListHandler() throws SQLException {
QueryRunner qr = new QueryRunner();
String sql = "SELECT * FROM hd_wish";
List list = qr.query(con, sql, new BeanListHandler(Wish.class));
for (Wish wish : list) {
System.out.println(wish);
}
}
ColumnListHandler
ColumnListHandler
将结果集中列名对应的数据放到Object集合List中
或者是泛型指定的类型
类似于php 的 getFiled,第二个参数为true
public static void columnListHandler() throws SQLException {
QueryRunner qr = new QueryRunner();
String sql = "SELECT * FROM hd_wish";
List users = qr.query(con, sql, new ColumnListHandler("username"));
for (String name : users) {
System.out.println(name);
}
}
sclarHandler
ScalarHandler
这个处理方式类似于php 的 getFiled
public static void ScalarHandler() throws SQLException {
QueryRunner qr = new QueryRunner();
String sql = "SELECT * FROM hd_wish";
String username = qr.query(con, sql, new ScalarHandler("username"));
System.out.println(username);
}
MapHandler
返回结果集的第一行,以map集合形式存储
private static void mapHandler() throws SQLException {
QueryRunner qr = new QueryRunner();
String sql = "SELECT * FROM hd_wish";
Map row = qr.query(con, sql, new MapHandler());
for (String key : row.keySet()) {
System.out.println(key + ": " + row.get(key));
}
}
MapListHandler
通过MapListHandler得到的集合,遍历的结果是有序的,其实这个返回的类型是它的内部类,这个类也继承自LinkedListMap
public static void mapListHandler() throws SQLException {
QueryRunner qr = new QueryRunner();
String sql = "SELECT * FROM hd_wish";
List
连接池
所谓的池技术,其实就是为了节约资源的连接和释放带来的性能损耗,线程池、连接池道理类似
频繁的连接释放数据库,对数据库和性能两方面都难以接受
需要连接时,从连接池拿一个连接对象,不需要的时候,调用close方法,此时不是释放资源,而是归还连接
DataSource
javax.sql.DataSource
DataSource 也是一个接口规范,用于给各个第三方供应商实现的连接池
常见的连接池有DBCP 和 C3P0
DBCP
连接池中的jar包中定义的BasicDataSource实现了数据源规范接口
通过创建该对象
设置好连接参数,即可获得连接
配置项
必须项
- deiverClassName
- url
- username
- password
可选项 - maxActive 最大连接数量
- minIdle 最小空闲数量
- maxIdle 最大空闲数量
- initialSize初始化连接数量
连接池工具类
public class DataSourceUtils {
private static BasicDataSource dataSource = new BasicDataSource();
static {
InputStream in = DataSourceUtils.class.getClassLoader().getResourceAsStream("database.properties");
Properties pro = new Properties();
try {
pro.load(in);
String driverClass = pro.getProperty("driverClass");
String url = pro.getProperty("url");
String username = pro.getProperty("username");
String password = pro.getProperty("password");
dataSource.setDriverClassName(driverClass);
dataSource.setUrl(url);
dataSource.setUsername(username);
dataSource.setPassword(password);
} catch (IOException e) {
e.printStackTrace();
}
}
public static DataSource getDataSource() {
return dataSource;
}
}
使用dataSource 来调用DbUtils
QueryRunner 类构造器可以传入一个连接池参数,如果传入了连接池参数,则后面的使用方法
update query 将不再需要传递连接对象
public static void mapListHandler() throws SQLException {
QueryRunner qr = new QueryRunner(DataSourceUtils.getDataSource());
String sql = "SELECT * FROM hd_wish";
List