一、引言
1.1 如何操作数据库
使用客户端工具访问数据库,需要手工建立连接,输入用户名和密码登陆,编写SQL语句,点击执行,查看操作结果(结果集或受影响行数)
1.2 实际开发中,会采用客户端操作数据库吗?
在实际开发中,当用户的数据发生改变时,不可能通过客户端操作执行SQL语句,因为操作量过大,无法保证效率和正确性。
二、JDBC (Java Database Connectivity)
2.1 什么是JDBC?
JDBC (Java Database Connectivity) java连接数据库,可以使用java语言连接数据库完成CRUD操作。
2.2 JDBC核心sixiang
java中定义了访问数据库的接口,可以为多种关系型数据库提供统一的访问方式。由数据库厂商提供驱动实现类(Driver数据库驱动)。
2.2.1 MySQL数据库驱动
mysql-connector-java-5.1X 适用于5.X版本
mysql-connector-java-8.0X 适用于8.X版本
2.2.2 JDBC API
JDBC是由多个接口和类进行功能实现。
class java.sql.DriverManager 管理多个数据库驱动类,提供了获取数据库连接的方法
interface java.sql.Connection 代表一个数据库连接(当connection不是null,表示已连接数据库)
interface java.sql.Statement 发送SQL语句到数据库工具
interface java.sql.ResultSet 保存SQL查询语句的结果数据(结果集)
class java.sql.SQLException 处理数据库应用程序时所发生的异常
2.3 环境搭建
1.在项目下新建lib文件夹,用于存放jar文件
2.将mysql驱动mysql-connector-java-5.1X复制到项目的lib文件夹中
3.选中lib文件夹右键Add as Library,点击OK
三、JDBC开发步骤
3.1 注册驱动
使用Class.forName(“com.mysql.jdbc.Driver”);手动加载字节码文件到JVM中
Class.forName(‘com.mysql.jdbc.Driver’);//加载驱动
3.2 连接数据库
通过 DriverManager.getConnection(url,user,password) //获取数据库连接对象
URL:jdbc:mysql://localhost:3306/database
username:root
password:123456
Connection conn=DriverManager.getConnection(“jdbc:mysql://localhost:3306/database?useUnicode=true&characterEncoding=utf8”,“root”,“123456”);
URL统一资源定位符:由协议、IP、端口、SID(程序实例名称)组成。
3.3 获取发送SQL的对象
//通过Connection对象获得Statement对象,用于对数据库进行通用访问
Statement statement=conn.createStatement();
3.4 执行SQL语句
// 执行SQL语句并接收执行结果
String sql="INSERT INTO t_jobs(JOB_ID,JOB_TITLE,MIN_SALARY,MAX_SALARY) VALUES('JAVA_Le','JAVA_LEcturer',4000,10000);";
int result=statement.executeUpdate(sql);
注意:
在编写DML语句时,一定要注意字符串参数的符号是单引号’值’
DML语句:增删改时,返回受影响行数(int类型)
DQL语句:查询时,返回结果数据(ResultSet结果集)
增删改:executeUpdate()
3.5 处理结果
接受处理操作结果
if(result==1){
System.out.println("Success");
}
注意:
受影响函数:逻辑判断,方法返回
查询结果:迭代、依次获取
3.6 释放资源
遵循先开后关原则,释放所使用到的资源对象
statement.close();
conn.close();
四、Result(结果集)
在执行查询SQL后,存放查询到的结果集数据
4.1 接收结果集
ResultSet rs=statement.executeQuery(sql);
Result rs=statement.executeQuery("SELECT * FROM t_employee;");
4.2 遍历ResultSet中的数据
Result 以表(table)结构进行临时结果的存储,需要通过JDBC API将其中数据进行依次获取。
数据行指针:初始位置在第一行数据前,每调用一次boolean next()方法,ResultSet的指针向下移动一行,结果为true,表示当前行数据
rs.getXxx(整数):代表根据列的编号顺序获得,从1开始
rs.getXxx(“列名”):代表根据列名获得。
boolean next() throws SQLException //判断rs结果集中下一行是否存在数据
4.2.1 遍历方法
int getInt(int columnIndex) throws SQLException; //获得当前行第N列的int值
int getInt(String columnLable) throws SQLException; //获得当前行columnLable列的int值
double getDouble(int columnIndex) throws SQLException; //获得当前行第N列的double值
double getDouble(String columnLable) throws SQLException; //获得当前行columnLable列的double值
String getString(int columnIndex) throws SQLException; //获得当前行第N列的String值
String getString(String columnLable) throws SQLException; //获得当前行columnLable列的String值
注意:列的编号从1开始。
4.2.2 根据列的编号获取数据
while (resultSet.next()){
String job_id =resultSet.getString(1);
String job_title=resultSet.getString(2);
String min_salary=resultSet.getString(3);
String max_salary=resultSet.getString(4);
System.out.println(job_id+"\t"+job_title+"\t"+min_salary+"\t"+max_salary);
}
五、常见错误
java.lang.ClassNotFoundException; //找不到类(类名书写错误,没有导入jar包)
java.lang.SQLException; //与SQL语句相关的错误(约束错误、表名列名书写错误) 建议:在客户端工具中测试SQL语句之后再粘贴在代码中
com.mysql.jdbc.exception.jdbc4.MySQLSyntaxErrorException:Unknown column //原因:列值String类型没有加单引号
Duplicate entry ‘1’ for key ‘PRIMARY’ //原因:主键值已存在或混乱,更改主键值或清空表
com.mysql.jdbc.exception.jdbc4.MySQLSyntaxException:Unknow column ‘password’ in //原因:可能输入的值的类型不对,确定是否插入的元素时对应的值的类型正确
六、综合案例【登录】
6.1 创建表
创建一张用户表user
id,主键,自动增长
用户名,字符串类型,非空
密码,字符串类型,非空
手机号码,字符串类型
插入2条测试语句
6.2 实现登录
通过控制台用户输入用户名和密码
用户输入的用户名和密码作为条件,编写查询SQL语句
如果该用户存在,提示登录成功,反之提示失败
七、SQL注入问题
7.1 什么是SQL注入
用户输入的护具中有SQL关键字或语法并且参与了SQL语句的编译;导致SQL语句编译后的条件含义为true,一直得到正确的结果。这种现象称为SQL注入。
7.2 如何避免SQL注入
由于编写的SQL语句是在用户输入数据,整合后再进行编译。所以为了避免SQL注入的问题,我们要使SQL语句在用户输入数据前就已进行
编译成完整的SQL语句,再进行填充数据。
八、PreparedStatement【重点】
PreparedStatement继承了Statement接口,所以执行SQL语句的方法无异。
8.1 PreparedStatement的应用
作用:
1、预编译SQL语句,效率高
2、安全,避免SQL注入
3、可以动态的填充数据,执行多个同构的SQL语句
8.1.1 参数标记
//预编译SQL语句
PreparedStatement pstmt=conn.prepareStatement(“SELECT * FROM t_users WHERE username=? and password=?”);
注意:JDBC中的所有参数都由?符号占位,这被称为参数标记。在执行SQL语句之前,必须为每个参数提供值。
8.1.2 动态参数绑定
pstmt.setXxx(下标,值) 参数下标从1开始,为指定参数下标绑定值
//预编译SQL语句
PreparedStatement pstmt=conn.prepareStatement(“SELECT * FROM t_users WHERE username=? and password=?”);
//为参数下标赋值
pstmt.setString(1,username);
pstmt.setString(2,password);
九、封装工具类
在实际JDBC的使用中,存在着大量的重复代码:例如连接数据库,关闭数据库等这些操作
我们需要把传统的JDBC代码进行重构,抽取除通用的JDBC工具类!以后连接任何数据库、释放资源都可以使用这个工具类。
9.1 重用性方案
封装获取连接、释放资源两个方法。
提供public static Connection getConnection(){}方法
提供public static void closeAll(Connection conn,Statement sm,ResultSet rs){}方法
9.1.1 重用工具类实现
public class DBUtils {
static {
try {
Class.forName("com.mysql.jdbc.Driver");
}catch (ClassNotFoundException e){
e.printStackTrace();
}
}
//1获取连接
public static Connection getConnection(){
Connection connection=null;
try {
connection= DriverManager.getConnection("jdbc:mysql://localhost:3306/companydb","root","123456");
} catch (SQLException e) {
e.printStackTrace();
}
return connection;
}
//2释放资源
public static void closeAll(Connection connection, Statement statement, ResultSet resultSet){
try {
if(resultSet!=null){
resultSet.close();
}
if(statement!=null){
statement.close();
}
if(connection!=null){
connection.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}
9.2 跨平台方案
定义:public static final Properties prop=new Properties(); //读取配置文件的Map
定义:static{
//首次使用工具类时,加载驱动
InputStream is=JDBCUtil.class.getResourceAsStream(“路径”); //通过复用本类自带流,读取jdbc.properties配置文件。classPath=bin
prop.load(is); //通过prop对象将流中的配置信息分割成键值对
String driverName=prop.getProperty(“driver”); //通过driverName的键值获取对应的值(com.mysql.jdbc.Driver)
Class.forName(driverName); //加载驱动
}
9.2.1 跨平台工具类实现
在src目录下新建db.properties文件
driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/mudb3
user=root
password=123456
public class DBUtils {
private static final Properties PROPERTIES=new Properties();//存储配置文件的map
static {
InputStream is=DBUtils.class.getResourceAsStream("/db.properties");
try {
PROPERTIES.load(is);
Class.forName(PROPERTIES.getProperty("driver"));
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
public static Connection getConnection(){
Connection connection=null;
try {
connection= DriverManager.getConnection(PROPERTIES.getProperty("url"),PROPERTIES.getProperty("username"),PROPERTIES.getProperty("password"));
} catch (SQLException e) {
e.printStackTrace();
}
return connection;
}
public static void closeAll(Connection connection, Statement statement, ResultSet resultSet){
try {
if(resultSet!=null){
resultSet.close();
}
if(statement!=null){
statement.close();
}
if(connection!=null){
connection.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}
十、ORM
ORM(Object Relational Mapping)
从数据库查询到的结果集(ResultSet)在进行遍历时,逐行遍历,取出的都是零散的数据。在实际应用开发中,我们都需要将零散的数据进行封装整理。
10.1 实体类(entity):零散数据的载体
一行数据中,多个零散的数据进行整理
通过entity的规则对表中的数据进行对象的封装
表名=类名;列名=属性名;提供各个属性的get,set方法
提供无参构造方法(视情况添加有参构造)
10.1.1 ORM应用
public class T_Jobs {
private String job_id;
private String job_title;
private String min_salary;
private String max_salary;
public T_Jobs() {
}
public T_Jobs(String job_id, String job_title, String min_salary, String max_salary) {
this.job_id = job_id;
this.job_title = job_title;
this.min_salary = min_salary;
this.max_salary = max_salary;
}
public String getJob_id() {
return job_id;
}
public void setJob_id(String job_id) {
this.job_id = job_id;
}
public String getJob_title() {
return job_title;
}
public void setJob_title(String job_title) {
this.job_title = job_title;
}
public String getMin_salary() {
return min_salary;
}
public void setMin_salary(String min_salary) {
this.min_salary = min_salary;
}
public String getMax_salary() {
return max_salary;
}
public void setMax_salary(String max_salary) {
this.max_salary = max_salary;
}
@Override
public String toString() {
return "T_Jobs{" +
"job_id='" + job_id + '\'' +
", job_title='" + job_title + '\'' +
", min_salary='" + min_salary + '\'' +
", max_salary='" + max_salary + '\'' +
'}';
}
}
public class TestORM {
public static void main(String[] args) {
Connection connection=null;
PreparedStatement preparedStatement=null;
ResultSet resultSet=null;
List<T_Jobs> t_jobsList=new ArrayList<>();
try {
connection=DBUtils.getConnection();
preparedStatement=connection.prepareStatement("select * from t_jobs;");
resultSet=preparedStatement.executeQuery();
while (resultSet.next()){
String job_id=resultSet.getString("job_id");
String job_title=resultSet.getString("job_title");
String min_salary=resultSet.getString("min_salary");
String max_salary=resultSet.getString("max_salary");
T_Jobs t_jobs=new T_Jobs();
t_jobs.setJob_id(job_id);
t_jobs.setJob_title(job_title);
t_jobs.setMin_salary(min_salary);
t_jobs.setMax_salary(max_salary);
t_jobsList.add(t_jobs);
}
for (T_Jobs t : t_jobsList){
System.out.println(t);
}
} catch (SQLException e) {
e.printStackTrace();
}finally {
DBUtils.closeAll(connection,preparedStatement,resultSet);
}
}
}
十一、DAO数据访问对象(Data Access Object)
DAO 实现了业务逻辑与数据库访问相分离
对同一张表的所有操作封装在XxxDAOImpl对象中
根据增删改查的不同功能实现具体的方法(insert、update、delete、select、selectAll)
12.1 创建数据库
创建一张表Person,有以下列:
id:int,主键,自动增长
name:varchar(20) 非空
age:int 非空
bornDate:Date
email:字符串
address:字符串
12.2 封装实体类
创建entity实体类Person,编写属性私有化,构造方法,get/set方法
public class T_Person {
private int id;
private String name;
private int age;
private Date bornDate;
private String email;
private String address;
public T_Person() {
}
public T_Person(String name, int age, Date bornDate, String email, String address) {
this.name = name;
this.age = age;
this.bornDate = bornDate;
this.email = email;
this.address = address;
}
public T_Person(int id, String name, int age, Date bornDate, String email, String address) {
this.id = id;
this.name = name;
this.age = age;
this.bornDate = bornDate;
this.email = email;
this.address = address;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public Date getBornDate() {
return bornDate;
}
public void setBornDate(Date bornDate) {
this.bornDate = bornDate;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
@Override
public String toString() {
return "T_Person{" +
"id=" + id +
", name='" + name + '\'' +
", age=" + age +
", bornDate=" + bornDate +
", email='" + email + '\'' +
", address='" + address + '\'' +
'}';
}
}
public class PersonDaoImpl {
//新增
public int insert(T_Person person){
Connection connection=null;
PreparedStatement preparedStatement=null;
String sql="insert into t_person(name,age,borndate,email,address) values(?,?,?,?,?);";
try {
connection=DBUtils.getConnection();
preparedStatement=connection.prepareStatement(sql);
preparedStatement.setString(1,person.getName());
preparedStatement.setInt(2,person.getAge());
preparedStatement.setDate(3,null);
preparedStatement.setString(4,person.getEmail());
preparedStatement.setString(5,person.getAddress());
int result=preparedStatement.executeUpdate();
return result;
} catch (SQLException e) {
e.printStackTrace();
}finally {
DBUtils.closeAll(connection,preparedStatement,null);
}
return 0;
};
//修改
public int update(T_Person person){
Connection connection=null;
PreparedStatement preparedStatement=null;
String sql="update t_person set name=?,age=?,borndate=?,email=?,address=? where id=?;";
try {
connection=DBUtils.getConnection();
preparedStatement=connection.prepareStatement(sql);
preparedStatement.setString(1,person.getName());
preparedStatement.setInt(2,person.getAge());
preparedStatement.setDate(3,null);
preparedStatement.setString(4,person.getEmail());
preparedStatement.setString(5,person.getAddress());
preparedStatement.setInt(6,person.getId());
int result=preparedStatement.executeUpdate();
return result;
} catch (SQLException e) {
e.printStackTrace();
}finally {
DBUtils.closeAll(connection,preparedStatement,null);
}
return 0;
};
//删除
public int delete(int id){
Connection connection=null;
PreparedStatement preparedStatement=null;
String sql="delete from t_person where id=?;";
try {
connection=DBUtils.getConnection();
preparedStatement=connection.prepareStatement(sql);
preparedStatement.setInt(1,id);
int result=preparedStatement.executeUpdate();
return result;
} catch (SQLException e) {
e.printStackTrace();
}finally {
DBUtils.closeAll(connection,preparedStatement,null);
}
return 0;
};
//查单个
public T_Person select(int id){
Connection connection=null;
PreparedStatement preparedStatement=null;
ResultSet resultSet=null;
String sql="select * from t_person where id=?;";
T_Person person=null;
try {
connection=DBUtils.getConnection();
preparedStatement=connection.prepareStatement(sql);
preparedStatement.setInt(1,id);
resultSet=preparedStatement.executeQuery();
if(resultSet.next()){
person=new T_Person();
int pid=resultSet.getInt("id");
String name=resultSet.getString("name");
int age=resultSet.getInt("age");
Date borndate=resultSet.getDate("borndate");
String email=resultSet.getString("email");
String address=resultSet.getString("address");
person.setId(pid);
person.setName(name);
person.setAge(age);
person.setBornDate(borndate);
person.setEmail(email);
person.setAddress(address);
}
return person;
} catch (SQLException e) {
e.printStackTrace();
}finally {
DBUtils.closeAll(connection,preparedStatement,resultSet);
}
return null;
};
//查所有
public List<T_Person> selectAll(){
Connection connection=null;
PreparedStatement preparedStatement=null;
ResultSet resultSet=null;
T_Person person=null;
List<T_Person> personList=new ArrayList<>();
try {
connection=DBUtils.getConnection();
preparedStatement=connection.prepareStatement("select * from t_person;");
resultSet=preparedStatement.executeQuery();
while (resultSet.next()){
int pid=resultSet.getInt("id");
String name=resultSet.getString("name");
int age=resultSet.getInt("age");
Date borndate=resultSet.getDate("borndate");
String email=resultSet.getString("email");
String address=resultSet.getString("address");
person=new T_Person(pid,name,age,borndate,email,address);
personList.add(person);
}
return personList;
} catch (SQLException e) {
e.printStackTrace();
}
return null;
};
}
public class TestPerson {
public static void main(String[] args) {
PersonDaoImpl personDao=new PersonDaoImpl();
// 1.新增
// T_Person person=new T_Person("Gavin",18,null,"Gavin@163.com","北京海淀区");
// int result=personDao.insert(person);
// if(result==1){
// System.out.println("新增成功!");
// }else{
// System.out.println("新增失败!");
// }
// 2.修改
// T_Person person=new T_Person(2,"Aaron",20,null,"Aaron@163.com","北京昌平区");
// int result=personDao.update(person);
// if(result==1){
// System.out.println("修改成功!");
// }else{
// System.out.println("修改失败!");
// }
// 3.删除
// int result=personDao.delete(2);
// if(result==1){
// System.out.println("删除成功!");
// }else{
// System.out.println("删除失败!");
// }
// 4.查询单个
// T_Person person=personDao.select(3);
// System.out.println(person);
// 5.查询所有
// List<T_Person> personList=personDao.selectAll();
// for(T_Person p : personList){
// System.out.println(p);
// }
}
}
十二、Date工具类
现有问题:数据库存储的数据类型为java.sql.Date。而我们java应用层存储日期类型为java.util.Date。当我们用java应用程序插入带有日期的数据到数据库中时,需要进行转换。
12.1 java.util.Date
java语言常规应用层面的日期类型,可以通过字符串创建对应的时间对象。
无法直接通过JDBC插入到数据库
12.2 java.sql.Date
不可以通过字符串创建对应的时间对象,只能通过毫秒值创建对象(1970年至今的毫秒值)
可以直接通过JDBC插入到数据库
12.3 SimpleDateFormat
格式化和解析日期的具体类,允许进行格式化(日期->文本),解析(文本->日期)和规范化。
SimpleDateFormat sdf=new SimpleDateFromat(“yyyy-MM-dd”); //指定日期格式
java.util.Date date=sdf.parse(String dateStr); //将字符串解析成日期类型(java.util.Date)
String dates=sdf.format(data); //将日期格式化成字符串
java.sql.Date sqlDate=new java.sql.Date(date.getTime()); //只支持毫秒值创建对象
public class DateUtils {
private static SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd");
// 1.字符串转换为util.Date
public static java.util.Date strToUtil(String str){
try {
java.util.Date date=sdf.parse(str);
return date;
} catch (ParseException e) {
e.printStackTrace();
}
return null;
}
// 2.util.Date转换为sql.Date
public static java.sql.Date utilToSql(java.util.Date date) {
return new java.sql.Date(date.getTime());
}
// 3.util.Date转换为字符串行
public static String utilToStr(java.util.Date date){
return sdf.format(date);
}
}
十三、Service业务逻辑层
13.1 什么是业务?
代表用户完成的一个业务功能,可以由一个或多个DAO的调用组成。(软件所提供的一个功能都叫业务)
public void register(T_Person person){
PersonDaoImpl personDao=new PersonDaoImpl();
//1查询用户是否存在
T_Person persons=personDao.select(person.getName());
//2判断用户是否存在,不存在新增,存在提示
if(persons==null){
personDao.insert(person);
System.out.println("注册成功!");
}else{
System.out.println("该用户已存在!");
}
}
13.2 Service开发流程-实现转账功能
public void transMoney(String fromNo,String password,String toNo,double money) {
Connection connection=null;
try {
connection=DBUtils.getConnection();
connection.setAutoCommit(false);
AccountDaoImpl accountDao=new AccountDaoImpl();
Account account=accountDao.select(fromNo);
//1判断当前卡号是否存在
if(account==null){
throw new RuntimeException("当前卡号不存在!");
}
//2判断当前卡号密码是否正确
if(!account.getPassword().equals(password)){
throw new RuntimeException("当前卡号密码不正确!");
}
//3判断当前卡号的余额是否充足
if(account.getBalance()<money){
throw new RuntimeException("当前卡号余额不足!");
}
Account toAccount=accountDao.select(toNo);
//4判断对方卡号是否存在
if(toAccount==null){
throw new RuntimeException("对方卡号不存在!");
}
//5当前卡号扣钱
account.setBalance(account.getBalance()-money);
accountDao.update(account);
//6对方卡号加钱
toAccount.setBalance(toAccount.getBalance()+money);
accountDao.update(toAccount);
System.out.println("转账成功");
connection.commit();
} catch (RuntimeException | SQLException e) {
System.out.println("转账失败:"+e);
try {
connection.rollback();
} catch (SQLException ex) {
ex.printStackTrace();
}
}
}
十四、事务
14.1 service层控制事务
Connection connection=null;
connection=DBUtils.getConnection();
connection.setAutoCommit(false);
connection.commit();
connection.rollback();
14.2 解决方案1:传递Connection
为了解决线程中connection对象不同步问题,可以将connection对象通过service传递给各个DAO方法吗?
14.2.1 传递的问题
如果使用传递Connection,容易造成接口污染(BadSmell)
定义接口是为了更容易实现,而将Connection定义在接口中,会造成污染当前接口。
14.3 解决方案2:ThreadLocal
可以将整个线程中(单线程)中,存储一个共享值。
线程拥有一个类似Map的属性,键值对结构<ThreadLocal对象,值>
14.4 ThreadLocal应用
一个线程共享一个ThreadLocal,在整个流程中任一环节可以存值或取值。
14.5 事务封装工具类
public class DBUtils {
private static final Properties PROPERTIES=new Properties();//存储配置文件的map
private static ThreadLocal<Connection> threadLocal=new ThreadLocal<>();
static {
InputStream is= com.qf.JDBC2.DBUtils.class.getResourceAsStream("/db.properties");
try {
PROPERTIES.load(is);
Class.forName(PROPERTIES.getProperty("driver"));
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
public static void begin(){
try {
Connection connection=getConnection();
connection.setAutoCommit(false);
} catch (SQLException e) {
e.printStackTrace();
}
}
public static void commit(){
Connection connection=null;
try {
connection=getConnection();
connection.commit();
} catch (SQLException e) {
e.printStackTrace();
}finally {
closeAll(connection,null,null);
}
}
public static void rollback(){
Connection connection=null;
try {
connection=getConnection();
connection.commit();
} catch (SQLException e) {
e.printStackTrace();
}finally {
closeAll(connection,null,null);
}
}
public static Connection getConnection(){
Connection connection=threadLocal.get();
try {
if(connection==null){
connection= DriverManager.getConnection(PROPERTIES.getProperty("url"),PROPERTIES.getProperty("user"),PROPERTIES.getProperty("password"));
threadLocal.set(connection);
}
} catch (SQLException e) {
e.printStackTrace();
}
return connection;
}
public static void closeAll(Connection connection, Statement statement, ResultSet resultSet){
try {
if(resultSet!=null){
resultSet.close();
}
if(statement!=null){
statement.close();
}
if(connection!=null){
connection.close();
threadLocal.remove();//移除connection对象
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}
十五、三层架构
15.1 什么是三层?
1.表示层:
命名:XXXView
职责:收集用户的数据和需求、展示数据。
2.业务逻辑层:
命名:XXXServiceImpl
职责:数据加工处理、调用DAO完成业务实现、控制事务。
3.数据访问层:
命名:XXXDaoImpl
职责:向业务层提供数据,将业务层加工后的数据同步到数据库。
15.2 三层架构项目搭建(按开发步骤)
utils:存放工具类(DBUtils)
entity:存放实体类(Person)
dao:存放DAO接口(PersonDao)
impl:存放DAO接口实现类(PersonDaoImpl)
service:存放service接口(PersonService)
impl:存放service接口实现类(PersonServiceImpl)
View:存放程序启动类(main)
注意:程序设计时,考虑易修改、易扩展,为service层和DAO层设计接口,便于未来更换实现类
十六、DaoUtils
在DAO层中,对数据库表的增删改查操作存在代码冗余,可对其进行抽取封装DaoUtil工具类实现复用。
public class DaoUtils<T> {
public int commonsUpdate(String sql,Object... args){
Connection connection=null;
PreparedStatement preparedStatement=null;
connection=DButils.getConnection();
try {
preparedStatement=connection.prepareStatement(sql);
for(int i=0;i<args.length;i++){
preparedStatement.setObject(i+1,args[i]);
}
int result=preparedStatement.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
}finally {
DButils.closeAll(null,null,null);
}
return 0;
}
public List<T> commonSelect(String sql, RowMapper<T> rowMapper, Object... args){
Connection connection=null;
PreparedStatement preparedStatement=null;
ResultSet resultSet=null;
List<T> list=new ArrayList<>();
connection=DButils.getConnection();
try {
preparedStatement=connection.prepareStatement(sql);
for(int i=0;i<args.length;i++){
preparedStatement.setObject(i+1,args[i]);
}
resultSet=preparedStatement.executeQuery();
while (resultSet.next()){
T t=rowMapper.getRow(resultSet);//回调——>调用者提供的一个封装方法ORM
list.add(t);
}
return list;
} catch (SQLException e) {
e.printStackTrace();
}
return list;
}
}
十七、Druid连接
在程序初始化时,预先创建指定数量的数据库连接对象存储在池中。当需要连接数据库时,从连接池中取出现有连接;使用完毕后,也不会进行关闭,而是放回池中,实现复用,节省资源。
17.1 Druid连接池使用步骤
创建database.properties配置文件。
引入druid-1.1.5.jar文件
17.1.1 database.properties配置文件
//连接设置
driverClassName=com.jdbc.Driver
url=jdbc:mysql://localhost:3306/school
username=root
password=123456
//初始化连接
initialSize=10
//最大连接数
maxActive=50
//最小空闲连接
minIdle=5
//超时等待时间以毫秒为单位 60000毫秒/1000等于60秒
maxWait=5000
17.1.2连接池工具类
public class DBUtils {
//声明连接池对象
private static DruidDataSource ds;
static {
Properties properties=new Properties();
InputStream is=DBUtils.class.getResourceAsStream("/database.properties");
try {
properties.load(is);
ds=(DruidDataSource) DruidDataSourceFactory.createDataSource(properties);
} catch (IOException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
public static Connection getConnection(){
try {
return ds.getConnection();
} catch (SQLException e) {
e.printStackTrace();
}
return null;
}
}
十八、Apache的DbUtils使用
Commons DbUtils是Apache组织提供的一个对JDBC进行简单封装的开源工具类库,使用它能简化JDBC应用程序的开发!同时,不会影响程序的性能。
18.1 DbUtils简介
DbUtils是Java编程中数据库操作实用小工具,小巧、简单、实用
对于数据表的查询操作,可以把结果转换为List、Array、Set等集合。便于操作
对于数据表的DML操作,也变得很简单(只需要写SQL语句)
18.1.1 DbUtils主要包含
ResultSetHandler接口:转换类型接口
BeanHandler类:实现类,把一条记录转换成对象。
BeanListHandler类:实现类,把多条记录转换成List集合。
ScalarHandler类:实现类,适合获取一行一列的数据。
QueryRunner:执行sql语句的类
增、删、改:update()
查询:query()
18.2 DbUtils的使用步骤
导入jar包
mysql连接驱动jar包
druid-1.1.5.jar
database.properties
commons-dbutils-1.6.jar
18.2.1 代码实现
DBUtils工具类
public class DBUtils {
//声明连接池对象
private static DruidDataSource ds;
static {
Properties properties=new Properties();
InputStream is=DBUtils.class.getResourceAsStream("/database.properties");
try {
properties.load(is);
ds=(DruidDataSource) DruidDataSourceFactory.createDataSource(properties);
} catch (IOException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
public static Connection getConnection(){
try {
return ds.getConnection();
} catch (SQLException e) {
e.printStackTrace();
}
return null;
}
public static DataSource getDateSource(){
return ds;
}
}
public class TestPerson {
private QueryRunner queryRunner=new QueryRunner(DBUtils.getDateSource());
public int insert(Object person){
Object[] params={person.getUserId(),person.getName(),person.getAge()};
try {
int result=queryRunner.update("insert into person(userid,name,age) values(?,?,?)",params);
} catch (SQLException e) {
e.printStackTrace();
}
}
public int select(int id){
try {
Person person=queryRunner.query("select * from user where userId=?",new BeanHandler<Person>(Person.class),id);
} catch (SQLException e) {
e.printStackTrace();
}
}
public int selectAll(){
try {
Person person=queryRunner.query("select * from user",new BeanListHandler<Person>(Person.class));
} catch (SQLException e) {
e.printStackTrace();
}
}
public long selectUserNums(){
try {
return queryRunner.query("select count(*) from user;",new ScalarHandler<>());
} catch (SQLException e) {
e.printStackTrace();
}
}
}