在程序中不需要直接去访问实现了 Driver 接口的类,而是由驱动程序管理器类(java.sql.DriverManager)去调用这些Driver实现。
Class.forName(“com.mysql.jdbc.Driver”);
DriverManager.registerDriver(com.mysql.jdbc.Driver)
user,password可以用“属性名=属性值”方式告诉数据库
@Test
public void testConnection5() throws Exception {
//1.加载配置文件
InputStream is =
ConnectionTest.class.getClassLoader().getResourceAsStream("jdbc.properties");
Properties pros = new Properties();
pros.load(is);
//2.读取配置信息
String user = pros.getProperty("user");
String password = pros.getProperty("password");
String url = pros.getProperty("url");
String driverClass = pros.getProperty("driverClass");
//3.加载驱动
Class.forName(driverClass);
//4.获取连接
Connection conn = DriverManager.getConnection(url,user,password);
System.out.println(conn);
}
使用Statement操作数据表存在弊端:
// 通用的增、删、改操作(体现一:增、删、改;体现二:针对于不同的表)
public void Update(String sql, Object ...args){
Connection conn = null;
PreparedStatement ps = null;
try {
// 1.获取数据库的连接
conn = JDBCUtils.getConnection(); // JDBCUtils里面封装了与数据库的连接操作和对资源的关闭操作
// 2.预编译sql语句,返回PreparedStatement的实例
ps = conn.prepareStatement(sql);
// 3.填充占位符
for (int i = 0; i < args.length; i++){
ps.setObject(i+1, args[i]);
}
// 4.SQL的执行
ps.execute();
} catch (Exception e) {
e.printStackTrace();
} finally {
// 5.资源的关闭
JDBCUtils.closeResource(conn,ps);
}
}
// 针对于不同表的通用查询操作,返回一个对象
public <T> T queryForAll(Class<T> clazz, String sql, Object ...args){ //泛型方法
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
// 1.建立与数据库的连接
conn = JDBCUtils.getConnection1();
// 2.预编译SQL, 得到PrepareStatement对象
ps = conn.prepareStatement(sql);
// 3.填充占位符
for (int i = 0; i < args.length; i++){
ps.setObject(i+1, args[i]);
}
// 4.执行,并返回结果集: ResultSet
rs = ps.executeQuery();
// 5.1 通过rsmd得到columnCount, columnLabel;通过rs得到列值
ResultSetMetaData rsmd = rs.getMetaData(); // 获取结果集的元数据
int columnCount = rsmd.getColumnCount(); // 获取结果集中的列数
if (rs.next()) {
T t = clazz.newInstance();
// 处理结果集一行数据中的每一个列
for (int i = 0; i < columnCount; i++){
Object columnValue = rs.getObject(i+1); // 获取每个列值
String columnLabel = rsmd.getColumnLabel(i+1); // 获取每个列名
// 5.2 通过反射给book对象指定的columnLabel属性, 赋值为columnValue
Field field = clazz.getDeclaredField(columnLabel);
field.setAccessible(true);
field.set(t, columnValue);
}
return t;
}
} catch (Exception e) {
e.printStackTrace();
} finally {
// 6. 关闭资源
JDBCUtils.closeResource(conn, ps, rs);
}
return null;
}
// 针对于不同表的通用查询操作,返回多个对象
public <T> List<T> queryForList(Class<T> clazz, String sql, Object ...args){
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
// 1.建立与数据库的连接
conn = JDBCUtils.getConnection();
// 2.预编译SQL
ps = conn.prepareStatement(sql);
// 3.填充占位符
for (int i = 0; i < args.length; i++){
ps.setObject(i+1, args[i]);
}
// 4.执行,并返回结果集: ResultSet
rs = ps.executeQuery();
// 5.1 通过rsmd得到columnCount, columnLabel;通过rs得到列值
ResultSetMetaData rsmd = rs.getMetaData(); // 获取结果集的元数据
int columnCount = rsmd.getColumnCount(); // 获取结果集中的列数
ArrayList<T> arrayList = new ArrayList<T>();
while (rs.next()) {
T t = clazz.newInstance();
// 处理结果集一行数据中的每一个列
for (int i = 0; i < columnCount; i++){
Object columnValue = rs.getObject(i+1); // 获取每个列值
String columnLabel = rsmd.getColumnLabel(i+1); // 获取每个列名
// 5.2 通过反射给book对象指定的columnLabel属性, 赋值为columnValue
Field field = clazz.getDeclaredField(columnLabel);
field.setAccessible(true);
field.set(t, columnValue);
}
arrayList.add(t);
}
return arrayList;
} catch (Exception e) {
e.printStackTrace();
} finally {
// 6.资源的关闭
JDBCUtils.closeResource(conn, ps, rs);
}
return null;
}
注意:Java与数据库交互涉及到的相关Java API中的索引都从1开始。
两种思想
两种技术
举例:
@Test
public void batchInsert() throws Exception {
Connection conn = JDBCUtils.getConnection();
conn.setAutoCommit(false); // 1. 设置为不自动提交数据
String sql = "INSERT INTO goods(g_name) VALUES(?)";
PreparedStatement ps = conn.prepareStatement(sql);
for (int i = 0; i < 20000; i++){
ps.setString(1,"name_" + i);
// 2. 攒sql
ps.addBatch();
if (i % 500 == 0){
// 3. 执行
ps.executeBatch();
// 4. 清空
ps.clearBatch();
}
}
conn.commit();
JDBCUtils.closeResource(conn,ps);
}
}
DAO:Data Access Object访问数据信息的类和接口,包括了对数据的CRUD(Create、Retrival、Update、Delete),而不包含任何业务相关的信息。有时也称作:BaseDAO
作用:为了实现功能的模块化,更有利于代码的维护和升级。
举例如下:
BaseDao:抽象类,封装了针对于数据表的通用操作,比如增删改的通用操作、查询的通用操作等。
BooksDao:接口,此接口用于规范针对于Books表的常用操作。
BooksDaoImpl:与BooksDao成对出现,定义针对于Books表的具体的操作;继承抽象类BaseDao,实现接口BooksDao。
BooksDaoImplTest:针对于BooksDaoImpl类中实现的方法进行单元测试。
在使用开发基于数据库的web程序时,传统的模式基本是按以下步骤:
这种开发模式,存在的问题如下:
Druid是阿里巴巴开源平台上一个数据库连接池实现。它结合了C3P0、DBCP、Proxool等DB池的优点,同时加入了日志监控,可以很好的监控DB池连接和SQL的执行情况,可以说是针对监控而生的DB连接池,可以说是目前最好的连接池之一。
注意:
将Druid数据库连接池相关操作定义在JDBCUtils里面:
private static DataSource dataSource;
static { // 连接池获取一次即可,因此不应该放入某一个类中(每次都要创建一个类的实例),而是直接放在静态代码块中
try {
// 加载配置文件druid.properties,避免写死在代码中
Properties pros = new Properties();
InputStream is = ClassLoader.getSystemClassLoader().getResourceAsStream("druid.properties");
pros.load(is);
// 获取Druid数据库连接池
dataSource = DruidDataSourceFactory.createDataSource(pros);
} catch (Exception e) {
e.printStackTrace();
}
}
Druid数据库连接池应用举例:
public class DruidTest {
@Test
public void getConnection() throws Exception {
// 加载配置文件druid.properties
Properties pros = new Properties();
InputStream is = ClassLoader.getSystemClassLoader().getResourceAsStream("druid.properties");
pros.load(is);
// 创建Druid数据库连接池,并获取连接
DataSource dataSource = DruidDataSourceFactory.createDataSource(pros);
Connection conn = dataSource.getConnection();
System.out.println(conn);
}
}
其中,src下的配置文件为:【druid.properties】
url=jdbc:mysql://localhost:3306/test01_library
username=root
password=root
driverClassName=com.mysql.jdbc.Driver
initialSize=10
maxActive=20
commons-dbutils 是 Apache 组织提供的一个开源 JDBC工具类库,它是对JDBC的简单封装,学习成本极低,并且使用dbutils能极大简化jdbc编码的工作量,同时也不会影响程序的性能。
DbUtils :提供如关闭连接、装载JDBC驱动程序等常规工作的工具类,里面的所有方法都是静态的。
DbUtils类提供了三个重载的关闭方法close()。这些方法检查所提供的参数是不是NULL,如果不是的话,它们就关闭Connection、Statement和ResultSet。
public static void close(…) throws java.sql.SQLException
该类简单化了SQL查询,它与ResultSetHandler组合在一起使用可以完成大部分的数据库操作,能够大大减少编码量。
QueryRunner类的主要方法
public int update(Connection conn, String sql, Object... params) throws SQLException
public Object query(Connection conn, String sql, ResultSetHandler rsh,Object... params) throws SQLException:
public int[] batch(Connection conn,String sql,Object[][] params)throws SQLException
举例:针对一条记录的插入操作
@Test // 针对一条记录的插入操作
public void testInsert(){
Connection conn = null;
try {
QueryRunner query = new QueryRunner();
conn = JDBCUtils.getConnection1();
String sql = "INSERT INTO books(id,name,authors) VALUES(?,?,?)";
int insertCount = query.update(conn, sql, 11, "sheep", "richard");
System.out.println("添加了" + insertCount + "条记录");
} catch (SQLException e) {
e.printStackTrace();
} finally {
JDBCUtils.closeResource(conn,null);
}
}
该接口用于处理 java.sql.ResultSet,将数据按要求转换为另一种形式。
接口的主要实现类:
举例:针对多条记录的查询操作
@Test // 针对多条记录的查询操作
public void testQuery2(){
List<Books> list = null;
Connection conn = null;
try {
QueryRunner runner = new QueryRunner();
conn = JDBCUtils.getConnection1(); //利用Druid数据库连接池获取连接
String sql = "SELECT * FROM books WHERE id=?";
// BeanListHandler是ResultSetHandler接口的实现类,用于封装类中的多条记录构成的集合
BeanListHandler<Books> handlerBooks = new BeanListHandler<>(Books.class);
list = runner.query(conn, sql, handlerBooks, 9);
} catch (SQLException e) {
e.printStackTrace();
} finally {
// 打印集合中的每一个元素
list.forEach(System.out::println);
JDBCUtils.closeResource(conn,null);
}
}