第四章 封装JDBCTools
配置文件:src/jdbc.properties
#key=value
driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/test
username=root
password=123456
initialSize=5
maxActive=10
maxWait=1000
JDBCTools工具类:
package com.atguigu.util;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Properties;
import javax.sql.DataSource;
import com.alibaba.druid.pool.DruidDataSourceFactory;
public class JDBCTools {
private static DataSource dataSource;
private static ThreadLocal<Connection> threadLocal = new ThreadLocal<>();
static {
try {
Properties pro = new Properties();
pro.load(JDBCTools2.class.getClassLoader().getResourceAsStream("druid.properties"));
dataSource = DruidDataSourceFactory.createDataSource(pro);
} catch (Exception e) {
e.printStackTrace();
}
}
public static Connection getConnection() {
Connection connection = threadLocal.get();
if (connection == null) {
try {
connection = dataSource.getConnection();
threadLocal.set(connection);
} catch (SQLException e) {
e.printStackTrace();
}
}
return connection;
}
public static void releaseConnection() {
Connection connection = threadLocal.get();
if (connection != null) {
try {
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}finally{
threadLocal.remove();
}
}
}
}
ThreadLocal:
JDK 1.2的版本中就提供java.lang.ThreadLocal,为解决多线程程序的并发问题提供了一种新的思路。使用这个工具类可以很简洁地编写出优美的多线程程序。通常用来在在多线程中管理共享数据库连接、Session等
ThreadLocal用于保存某个线程共享变量,原因是在Java中,每一个线程对象中都有一个ThreadLocalMap<ThreadLocal, Object>,其key就是一个ThreadLocal,而Object即为该线程的共享变量。而这个map是通过ThreadLocal的set和get方法操作的。对于同一个static ThreadLocal,不同线程只能从中get,set,remove自己的变量,而不会影响其他线程的变量。
1、ThreadLocal.get: 获取ThreadLocal中当前线程共享变量的值。
2、ThreadLocal.set: 设置ThreadLocal中当前线程共享变量的值。
3、ThreadLocal.remove: 移除ThreadLocal中当前线程共享变量的值。
第五章 封装BasicDAOImpl
package com.atguigu.test08.dao;
import java.lang.reflect.Field;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.util.ArrayList;
import com.atguigu.util.JDBCTools;
public class BasicDAOImpl {
protected int update(String sql,Object... args) throws SQLException{
Connection conn = JDBCTools.getConnection();
PreparedStatement pst = conn.prepareStatement(sql);
if(args!=null && args.length>0){
for (int i = 0; i < args.length; i++) {
pst.setObject(i+1, args[i]);
}
}
int len = pst.executeUpdate();
pst.close();
return len;
}
protected <T> ArrayList<T> getList(Class<T> clazz,String sql,Object... args) throws Exception{
Connection conn = JDBCTools.getConnection();
PreparedStatement pst = conn.prepareStatement(sql);
if(args!=null && args.length>0){
for (int i = 0; i < args.length; i++) {
pst.setObject(i+1, args[i]);
}
}
ResultSet rs = pst.executeQuery();
ArrayList<T> list = new ArrayList<>();
ResultSetMetaData metaData = rs.getMetaData();
int count = metaData.getColumnCount();
while(rs.next()){
T t = clazz.newInstance();
for (int i = 0; i < count; i++) {
String fieldName = metaData.getColumnLabel(i+1);
Field f = clazz.getDeclaredField(fieldName);
f.setAccessible(true);
f.set(t, rs.getObject(i+1));
}
list.add(t);
}
pst.close();
rs.close();
return list;
}
protected <T> T getBean(Class<T> clazz,String sql,Object... args) throws Exception{
return getList(clazz,sql,args).get(0);
}
}
使用BasicDAOImpl实现Employee的增删改查
示例代码:EmployeeDAO.java
public interface EmployeeDAO {
void addEmployee(Employee emp);
void updateEmployee(Employee emp);
void deleteByEid(int eid);
Employee getByEid(int eid);
ArrayList<Employee> getAll();
}
示例代码:EmployeeDAOImpl.java
package com.atguigu.test08.dao;
import java.sql.SQLException;
import java.util.ArrayList;
import com.atguigu.bean.Employee;
public class EmployeeDAOImpl extends BasicDAOImpl implements EmployeeDAO {
@Override
public void addEmployee(Employee emp) {
String sql = "insert into t_employee values(null,?,?,?,?,?,?,?,?,?,?,?,?,?)";
try {
update(sql, emp.getEname(),emp.getTel(),emp.getGender(),emp.getSalary(),emp.getCommissionPct(),
emp.getBirthday(),emp.getHiredate(),emp.getJobId(),emp.getEmail(),emp.getMid(),emp.getAddress(),
emp.getNativePlace(),emp.getDid());
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
@Override
public void updateEmployee(Employee emp) {
String sql = "update t_employee set `ename`=?,`tel`=?,`gender`=?,`salary`=?,`commission_pct`=?,`birthday`=?,`hiredate`=?,`job_id`=?,`email`=?,`mid`=?,`address`=?,`native_place`=?,`did`=? where eid=?";
try {
update(sql, emp.getEname(),emp.getTel(),emp.getGender(),emp.getSalary(),emp.getCommissionPct(),
emp.getBirthday(),emp.getHiredate(),emp.getJobId(),emp.getEmail(),emp.getMid(),emp.getAddress(),
emp.getNativePlace(),emp.getDid(),emp.getEid());
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
@Override
public void deleteByEid(int eid) {
String sql = "delete from t_employee where eid=?";
try {
update(sql, eid);
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
@Override
public Employee getByEid(int eid) {
String sql = "select `eid`,`ename`,`tel`,`gender`,`salary`,`commission_pct` commissionPct ,`birthday`,`hiredate`,`job_id` jobId,`email`,`mid`,`address`,`native_place` nativePlace,`did` from t_employee where eid=?";
Employee emp = null;
try {
emp = getBean(Employee.class, sql, eid);
} catch (Exception e) {
throw new RuntimeException(e);
}
return emp;
}
@Override
public ArrayList<Employee> getAll() {
String sql = "select `eid`,`ename`,`tel`,`gender`,`salary`,`commission_pct` commissionPct ,`birthday`,`hiredate`,`job_id` jobId,`email`,`mid`,`address`,`native_place` nativePlace,`did` from t_employee";
ArrayList<Employee> list = new ArrayList<Employee>();
try {
list = getList(Employee.class, sql);
} catch (Exception e) {
throw new RuntimeException(e);
}
return list;
}
}
第六章 Apache的DBUtils
commons-dbutils 是 Apache 组织提供的一个开源 JDBC工具类库,它是对JDBC的简单封装,学习成本极低,并且使用dbutils能极大简化jdbc编码的工作量,同时也不会影响程序的性能。
其中QueryRunner类封装了SQL的执行,是线程安全的。
(1)可以实现增、删、改、查、批处理、
(2)考虑了事务处理需要共用Connection。
(3)该类最主要的就是简单化了SQL查询,它与ResultSetHandler组合在一起使用可以完成大部分的数据库操作,能够大大减少编码量。
(1)更新
public int update(Connection conn, String sql, Object… params) throws SQLException:用来执行一个更新(插入、更新或删除)操作。
…
(2)插入
public T insert(Connection conn,String sql,ResultSetHandler rsh, Object… params) throws SQLException:只支持INSERT语句,其中 rsh - The handler used to create the result object from the ResultSet of auto-generated keys. 返回值: An object generated by the handler.即自动生成的键值
…
(3)批处理
public int[] batch(Connection conn,String sql,Object[][] params)throws SQLException: INSERT, UPDATE, or DELETE语句
public T insertBatch(Connection conn,String sql,ResultSetHandler rsh,Object[][] params)throws SQLException:只支持INSERT语句
…
(4)使用QueryRunner类实现查询
public Object query(Connection conn, String sql, ResultSetHandler rsh,Object… params) throws SQLException:执行一个查询操作,在这个查询中,对象数组中的每个元素值被用来作为查询语句的置换参数。该方法会自行处理 PreparedStatement 和 ResultSet 的创建和关闭。
…
***ResultSetHandler***接口用于处理 java.sql.ResultSet,将数据按要求转换为另一种形式。ResultSetHandler 接口提供了一个单独的方法:Object handle (java.sql.ResultSet rs)该方法的返回值将作为QueryRunner类的query()方法的返回值。
该接口有如下实现类可以使用:
- BeanHandler:将结果集中的第一行数据封装到一个对应的JavaBean实例中。
- BeanListHandler:将结果集中的每一行数据都封装到一个对应的JavaBean实例中,存放到List里。
- ScalarHandler:查询单个值对象
- MapHandler:将结果集中的第一行数据封装到一个Map里,key是列名,value就是对应的值。
- MapListHandler:将结果集中的每一行数据都封装到一个Map里,然后再存放到List
- ColumnListHandler:将结果集中某一列的数据存放到List中。
- KeyedHandler(name):将结果集中的每一行数据都封装到一个Map里,再把这些map再存到一个map里,其key为指定的key。
- ArrayHandler:把结果集中的第一行数据转成对象数组。
- ArrayListHandler:把结果集中的每一行数据都转成一个数组,再存放到List中。
示例代码:BasicDAOImpl.java
package com.atguigu.test09.dbutil;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.List;
import java.util.Map;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanHandler;
import org.apache.commons.dbutils.handlers.BeanListHandler;
import org.apache.commons.dbutils.handlers.MapListHandler;
import org.apache.commons.dbutils.handlers.ScalarHandler;
import com.atguigu.util.JDBCTools;
public class BasicDAOImpl4 {
private QueryRunner qr = new QueryRunner();
protected int update(String sql,Object... args) throws SQLException{
Connection conn = JDBCTools2.getConnection();
int len = qr.update(conn, sql, args);
return len;
}
protected <T> List<T> getList(Class<T> clazz,String sql,Object... args) throws Exception{
Connection conn = JDBCTools2.getConnection();
List<T> list = qr.query(conn, sql, new BeanListHandler<>(clazz), args);
return list;
}
protected <T> T getBean(Class<T> clazz,String sql,Object... args) throws Exception{
Connection conn = JDBCTools2.getConnection();
T t = qr.query(conn, sql, new BeanHandler<>(clazz), args);
return t;
}
protected Object getObject(String sql,Object... args) throws Exception{
Connection conn = JDBCTools2.getConnection();
Object obj = qr.query(conn, sql, new ScalarHandler<>(), args);
return obj;
}
protected List<Map<String, Object>> getMapList(String sql,Object... args) throws Exception{
Connection conn = JDBCTools2.getConnection();
List<Map<String, Object>> list = qr.query(conn, sql, new MapListHandler(), args);
return list;
}
}
示例代码:
public interface EmployeeDAO2 {
void addEmployee(Employee emp);
void updateEmployee(Employee emp);
void deleteByEid(int eid);
Employee getByEid(int eid);
List<Employee> getAll();
long empCount();
double avgSalary();
Map<Integer,Double> avgSalaryPerDepartment();
}
示例代码:
public class EmployeeDAOImpl2 extends BasicDAO2 implements EmployeeDAO2 {
@Override
public void addEmployee(Employee emp) {
String sql = "insert into t_employee values(null,?,?,?,?,?,?,?,?,?,?,?,?,?)";
try {
update(sql, emp.getEname(),emp.getTel(),emp.getGender(),emp.getSalary(),emp.getCommissionPct(),
emp.getBirthday(),emp.getHiredate(),emp.getJobId(),emp.getEmail(),emp.getMid(),emp.getAddress(),
emp.getNativePlace(),emp.getDid());
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
@Override
public void updateEmployee(Employee emp) {
String sql = "update t_employee set `ename`=?,`tel`=?,`gender`=?,`salary`=?,`commission_pct`=?,`birthday`=?,`hiredate`=?,`job_id`=?,`email`=?,`mid`=?,`address`=?,`native_place`=?,`did`=? where eid=?";
try {
update(sql, emp.getEname(),emp.getTel(),emp.getGender(),emp.getSalary(),emp.getCommissionPct(),
emp.getBirthday(),emp.getHiredate(),emp.getJobId(),emp.getEmail(),emp.getMid(),emp.getAddress(),
emp.getNativePlace(),emp.getDid(),emp.getEid());
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
@Override
public void deleteByEid(int eid) {
String sql = "delete from t_employee where eid=?";
try {
update(sql, eid);
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
@Override
public Employee getByEid(int eid) {
String sql = "select `eid`,`ename`,`tel`,`gender`,`salary`,`commission_pct` commissionPct ,`birthday`,`hiredate`,`job_id` jobId,`email`,`mid`,`address`,`native_place` nativePlace,`did` from t_employee where eid=?";
Employee emp = null;
try {
emp = getBean(Employee.class, sql, eid);
} catch (Exception e) {
throw new RuntimeException(e);
}
return emp;
}
@Override
public List<Employee> getAll() {
String sql = "select `eid`,`ename`,`tel`,`gender`,`salary`,`commission_pct` commissionPct ,`birthday`,`hiredate`,`job_id` jobId,`email`,`mid`,`address`,`native_place` nativePlace,`did` from t_employee";
List<Employee> list = new ArrayList<Employee>();
try {
list = getList(Employee.class, sql);
} catch (Exception e) {
throw new RuntimeException(e);
}
return list;
}
@Override
public long empCount() {
String sql = "select count(1) from t_employee";
Long count = 0L;
try {
Object obj = getObject(sql);
count = (Long) obj;
} catch (Exception e) {
throw new RuntimeException(e);
}
return count;
}
@Override
public double avgSalary() {
String sql = "select avg(salary) from t_employee";
Double avg = 0.0;
try {
avg = (Double) getObject(sql);
} catch (Exception e) {
throw new RuntimeException(e);
}
return avg;
}
@Override
public Map<Integer, Double> avgSalaryPerDepartment() {
String sql = "select did,avg(salary) from t_employee group by did";
Map<Integer, Double> map = new HashMap<>();
try {
List<Map<String, Object>> mapList = getMapList(sql);
for (Map<String, Object> map2 : mapList) {
map.put((Integer)map2.get("did"),(Double)map2.get("avg(salary)"));
}
} catch (Exception e) {
throw new RuntimeException(e);
}
return map;
}
}
示例代码:
package com.atguigu.test09.dbutil;
import java.util.List;
import java.util.Map;
import org.junit.Test;
import com.atguigu.bean.Employee;
public class TestEmployeeDAOImp2 {
@Test
public void test1(){
EmployeeDAOImpl2 ed2 = new EmployeeDAOImpl2();
List<Employee> all = ed2.getAll();
for (Employee employee : all) {
System.out.println(employee);
}
}
@Test
public void test2(){
EmployeeDAOImpl2 ed2 = new EmployeeDAOImpl2();
long count = ed2.empCount();
System.out.println(count);
}
@Test
public void test3(){
EmployeeDAOImpl2 ed2 = new EmployeeDAOImpl2();
double avgSalary = ed2.avgSalary();
System.out.println(avgSalary);
}
@Test
public void test4(){
EmployeeDAOImpl2 ed2 = new EmployeeDAOImpl2();
Map<Integer, Double> map = ed2.avgSalaryPerDepartment();
map.forEach((k,v) -> System.out.println(k+"->"+v));
}
}
经典错误
1、jar包版本不兼容
Exception in thread "main" com.mysql.jdbc.exceptions.jdbc4.MySQLNonTransientConnectionException: Could not create connection to database server.
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
at com.mysql.jdbc.Util.handleNewInstance(Util.java:408)
at com.mysql.jdbc.Util.getInstance(Util.java:383)
at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:1023)
at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:997)
at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:983)
at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:928)
at com.mysql.jdbc.ConnectionImpl.connectOneTryOnly(ConnectionImpl.java:2576)
at com.mysql.jdbc.ConnectionImpl.createNewIO(ConnectionImpl.java:2309)
at com.mysql.jdbc.ConnectionImpl.<init>(ConnectionImpl.java:834)
at com.mysql.jdbc.JDBC4Connection.<init>(JDBC4Connection.java:46)
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
at com.mysql.jdbc.Util.handleNewInstance(Util.java:408)
at com.mysql.jdbc.ConnectionImpl.getInstance(ConnectionImpl.java:419)
at com.mysql.jdbc.NonRegisteringDriver.connect(Loading class `com.mysql.jdbc.Driver'. This is deprecated. The new driver class is `com.mysql.cj.jdbc.Driver'. The driver is automatically registered via the SPI and manual loading of the driver class is generally unnecessary.
Exception in thread "main" java.sql.SQLException: The server time zone value '���??��' is unrecognized or represents more than one time zone. You must configure either the server or JDBC driver (via the serverTimezone configuration property) to use a more specifc time zone value if you want to utilize time zone support.
at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:129)
at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:97)
at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:89)
at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:63)
at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:73)
at com.mysql.cj.jdbc.exceptions.SQLExceptionsMapping.translateException(SQLExceptionsMapping.java:76)
at com.mysql.cj.jdbc.ConnectionImpl.createNewIO(ConnectionImpl.java:835)
at com.mysql.cj.jdbc.ConnectionImpl.<init>(ConnectionImpl.java:455)
at com.mysql.cj.jdbc.ConnectionImpl.getInstance(ConnectionImpl.java:240)
at com.mysql.cj.jdbc.NonRegisteringDriver.connect(NonRegisteringDriver.java:199)
at java.sql.DriverManager.getConnection(DriverManager.java:664)
at java.sql.DriverManager.getConnection(DriverManager.java:247)
.java:344)
at java.sql.DriverManager.getConnection(DriverManager.java:664)
at java.sql.DriverManager.getConnection(DriverManager.java:247)
看异常好像是无事务连接异常,无法创建连接。将MySQL驱动改为了最新的8.0版本的MySQL驱动。显示那个驱动类已经过时了,新的驱动类是“com.mysql.cj.jdbc.Driver”,而不是“com.mysql.jdbc.Driver”了,并且还说我没有配置时区,查了一下,原来从JDBC6.0开始驱动类使用了新的,并且url中必须要设置时区,否侧会报错。
第一步:使用最新的MySQL驱动jar包。
第二步:把驱动的类名改为:
static String driver="com.mysql.cj.jdbc.Driver";
第三步:在访问mysql的url后加入时区设置:
static String url="jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=UTF8&serverTimezone=UTC"
|