JDBC
JDBC概述
什么是JDBC
JDBC 是一种用于执行SQL语句的Java API
它由一组用Java语言编写的类和接口组成
JDBC提供了一种操作数据的标准
JDBC的目标是使Java程序员使用JDBC可以连接任何提供了JDBC驱动程序的数据库系统,
通过驱动连接
JDBC的API
java.sql包装的就是JDBC的API.
各大数据库厂商就会对JDBC的API提供实现类
驱动包 注意不要引错包,不用引用这个不要引入com.mysql..Xxx类
连接Mysql
添加驱动
1.在项目当中创建一个文件夹为lib
2.把Mysql驱动包复制到该文件夹下
3.builder path 编译路径
创建连接
//1.加载驱动
Class.forName("com.mysql.jdbc.Driver");
//2.获取连接对象
String url="jdbc:mysql://localhost:3306/mydb?useUnicode=true&characterEncoding=utf-8&serverTimezone=UTC&useSSL=false";//url数据库地址
String username="root";//数据库用户名
String password="123456";//数据库密码
Connection conn = DriverManager.getConnection(url, username, password);//获取链接对象
System.out.println(conn);
操作Mysql创建表
1.加载驱动
Class.forName("com.mysql.jdbc.Driver");
2.连接数据库
String url="jdbc:mysql://localhost:3306/mydb?useUnicode=true&characterEncoding=utf-8&serverTimezone=UTC&useSSL=false";//url数据库地址
String username="root";//数据库用户名
String password="123456";//数据库密码
Connection conn = DriverManager.getConnection(url, username, password);//获取链接对象
3.创建要执行的sql语句
Statement接口,用来执行静态SQL语句对象
把SQL语句发送到数据库中去执行。并返回执行的结果
对于DQL返回查询的结果集
对于DML返回受影响的行数
对于DDL返回0
executeUpate(String) 执行DML和DDL语句
String sql="create table stu(id int,name varchar(50),age int)";
Statement connStatement = conn.createStatement();
4.执行sql
int row = connStatement.executeUpdate(sql);
5.释放数据资源
为什么要释放资源
Connection连接就相当于Java和到Mysql之间建立管道
连接只连接到数据,Statement 就相当于从数据库又接了一个管道连接Mysql的执行程序
连接图
connStatement.close();
conn.close();
完整代码
//1.加载驱动
Class.forName("com.mysql.jdbc.Driver");
//2.获取连接对象
String url="jdbc:mysql://localhost:3306/mydb?useUnicode=true&characterEncoding=utf-8&serverTimezone=UTC&useSSL=false";//url数据库地址
String username="root";//数据库用户名
String password="123456";//数据库密码
Connection conn = DriverManager.getConnection(url, username, password);//获取链接对象
//3.创建要执行的sql语句
String sql="create table stu(id int,name varchar(50),age int)";
Statement connStatement = conn.createStatement();
//4.执行sql
int row = connStatement.executeUpdate(sql);
//5.释放数据资源
connStatement.close();
conn.close();
执行dml语句
结果集
ResultSet
表示数据库查询的结果的集合,在执行查询语句时就会得到一个这样的结果
常用方法:
boolean next():判断是否有下一行数据,若有,则向下移动一行指针.
getXxx(int columnIndex):获取当前行中,第几列.(从1开始):不推荐
getXxx(String columnName):获取当前行中的,指定列名的列的值.columnName是列名/列的别名
若列的类型是VARCHAR/CHAR/TEXT,都使用getString来获取列的值.
若列的类型是int/integer/-->getInt来获取列的值.
String url="jdbc:mysql://localhost:3306/mydb?useUnicode=true&characterEncoding=utf-8&serverTimezone=UTC&useSSL=false";//url数据库地址
String username="root";//数据库用户名
String password="123456";//数据库密码
Connection conn =null;
Statement connStatement=null;
try {
//加载驱动
Class.forName("com.mysql.jdbc.Driver");
//获取连接对象
conn = DriverManager.getConnection(url, username, password);//获取链接对象
//创建要执行的sql语句
String sql="insert into stu(id,name,age) values(1,'joker',20)";
connStatement = conn.createStatement();
//执行sql
int row = connStatement.executeUpdate(sql);
if(row>0){
System.out.println("添加成功");
}
}catch (Exception e){
try {
if(connStatement!=null){
//释放数据资源
connStatement.close();
}
}catch (SQLException e1){
e1.printStackTrace();
}
try {
if(conn!=null){
conn.close();
}
}catch (SQLException e1){
e1.printStackTrace();
}
}
查询操作
String url="jdbc:mysql://localhost:3306/mydb?useUnicode=true&characterEncoding=utf-8&serverTimezone=UTC&useSSL=false";//url数据库地址
String username="root";//数据库用户名
String password="123456";//数据库密码
Connection conn =null;
Statement connStatement=null;
try {
//加载驱动
Class.forName("com.mysql.jdbc.Driver");
//获取连接对象
conn = DriverManager.getConnection(url, username, password);//获取链接对象
//创建要执行的sql语句
String sql="select * from stu";
connStatement = conn.createStatement();
//执行sql
ResultSet resultSet = connStatement.executeQuery(sql);
while (resultSet.next()){
int id = resultSet.getInt(1);
String name = resultSet.getString(2);
int age=resultSet.getInt(3);
System.out.println("id:"+id);
System.out.println("姓名:"+name);
System.out.println("年龄:"+age);
}
}catch (Exception e){
try {
if(connStatement!=null){
//释放数据资源
connStatement.close();
}
}catch (SQLException e1){
e1.printStackTrace();
}
try {
if(conn!=null){
conn.close();
}
}catch (SQLException e1){
e1.printStackTrace();
}
}
DAO设计
没有使用DAO存在的问题
多个地方都要都同时做CRUD操作时,重复 的代码就会很多
什么是DAO
Data Access Object(数据存取对象)
位于业务逻辑和持久化数据之间
实现对持久化数据的访问
ORM
什么是ORM
对象关系映射 将关系数据库中表中的记录映射成为对象,以对象的形式展现
因此ORM的目的是为了方便开发人员以面向对象的思想来实现对数据库的操作。
开发步骤
1.创建表
create table student(
id int primary key auto_increment,
name varchar(20) ,
age int
);
2.建立domain包和domain类
public class student {
private int id;
private String name;
private int age;
@Override
public String toString() {
return "student{" +
"id=" + id +
", name='" + name + '\'' +
", age=" + age +
'}';
}
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 student() {
}
public student(int id, String name, int age) {
this.id = id;
this.name = name;
this.age = age;
}
}
3.建立dao包和dao接口
public interface StudentDao {
//保存学生信息
void saveStudent(Student student);
//删除学生信息
void deleteStudent(int id);
//获取学生信息
Student get(int id);
//获取所以学生信息
List<Student> getList();
}
4.建立dao.impl包和dao实现类
public class StudentDaoImpl implements IStudentDao {
String url="jdbc:mysql://localhost:3306/mydb?useUnicode=true&characterEncoding=utf-8&serverTimezone=UTC&useSSL=false";//url数据库地址
String username="root";//数据库用户名
String password="123456";//数据库密码
Connection conn =null;
Statement connStatement=null;
@Override
public void saveStudent(Student student){
try {
//1.加载驱动
Class.forName("com.mysql.jdbc.Driver");
//2.连接数据库
conn = DriverManager.getConnection(url, username, password);//获取链接对象
//3.创建语句
String sql="insert into student (name,age)values('"+student.getName()+"',"+student.getAge()+")";
//4.执行语句
Statement connStatement = conn.createStatement();
int i = connStatement.executeUpdate(sql);
if(i>0){
System.out.println("添加成功");
}else {
System.out.println("添加失败");
}
//5.释放资源
}catch (Exception e){
try {
if(connStatement!=null){
//释放数据资源
connStatement.close();
}
}catch (SQLException e1){
e1.printStackTrace();
}
try {
if(conn!=null){
conn.close();
}
}catch (SQLException e1){
e1.printStackTrace();
}
}
}
@Override
public void deleteStudent(int id) {
try {
//1.加载驱动
Class.forName("com.mysql.jdbc.Driver");
//2.连接数据库
conn = DriverManager.getConnection(url, username, password);//获取链接对象
//3.创建语句
String sql="delete from student where id="+id+"";
//4.执行语句
Statement connStatement = conn.createStatement();
int i = connStatement.executeUpdate(sql);
if(i>0){
System.out.println("删除成功");
}else {
System.out.println("删除失败");
}
//5.释放资源
}catch (Exception e){
try {
if(connStatement!=null){
//释放数据资源
connStatement.close();
}
}catch (SQLException e1){
e1.printStackTrace();
}
try {
if(conn!=null){
conn.close();
}
}catch (SQLException e1){
e1.printStackTrace();
}
}
}
@Override
public Student get(int id) {
try {
//1.加载驱动
Class.forName("com.mysql.jdbc.Driver");
//2.连接数据库
conn = DriverManager.getConnection(url, username, password);//获取链接对象
//3.创建语句
String sql="select * from student where id="+id+"";
//4.执行语句
Statement connStatement = conn.createStatement();
ResultSet resultSet = connStatement.executeQuery(sql);
Student s=null;
if (resultSet.next()){
s=new Student(resultSet.getInt("id"),resultSet.getString("name"),resultSet.getInt("age"));
return s;
}
//5.释放资源
}catch (Exception e){
try {
if(connStatement!=null){
//释放数据资源
connStatement.close();
}
}catch (SQLException e1){
e1.printStackTrace();
}
try {
if(conn!=null){
conn.close();
}
}catch (SQLException e1){
e1.printStackTrace();
}
}
return null;
}
@Override
public List<Student> getList() {
try {
//1.加载驱动
Class.forName("com.mysql.jdbc.Driver");
//2.连接数据库
conn = DriverManager.getConnection(url, username, password);//获取链接对象
//3.创建语句
String sql="select * from student ";
//4.执行语句
Statement connStatement = conn.createStatement();
ResultSet resultSet = connStatement.executeQuery(sql);
List<Student> list=new ArrayList<Student>();;
while (resultSet.next()){
Student s=new Student(resultSet.getInt("id"),resultSet.getString("name"),resultSet.getInt("age"));
list.add(s);
}
return list;
//5.释放资源
}catch (Exception e){
try {
if(connStatement!=null){
//释放数据资源
connStatement.close();
}
}catch (SQLException e1){
e1.printStackTrace();
}
try {
if(conn!=null){
conn.close();
}
}catch (SQLException e1){
e1.printStackTrace();
}
}
return null;
}
}
5.根据dao接口创建dao测试类
Student student=new Student(0,"李四",50);
IStudentDao stuDao=new StudentDaoImpl();
stuDao.saveStudent(student);
抽取JDBC工具类
public class JDBCUtil {
public static final String url="jdbc:mysql://localhost:3306/mydb?useUnicode=true&characterEncoding=utf-8&serverTimezone=UTC&useSSL=false";//url数据库地址
public static final String username="root";//数据库用户名
public static final String password="123456";//数据库密码
public static final String driverName="com.mysql.jdbc.Driver";//驱动名称
static {
try {
Class.forName(driverName);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
//获取连接对象
public static Connection getconn(){
Connection connection=null;
try {
connection = DriverManager.getConnection(url, username, password);//获取链接对象
}catch (Exception e){
e.printStackTrace();
}
return connection;
}
//释放资源的方法
public static void close(Connection connection, Statement statement, ResultSet resultSet){
try {
if(resultSet!=null){
resultSet.close();
}
}catch (SQLException e1){
e1.printStackTrace();
}
try {
if(statement!=null){
statement.close();
}
}catch (SQLException e1){
e1.printStackTrace();
}
try {
if(connection!=null){
connection.close();
}
}catch (SQLException e1){
e1.printStackTrace();
}
}
}
简化impl
public class StudentDaoImpl implements IStudentDao {
Connection conn =null;
Statement connStatement=null;
ResultSet resultSet=null;
@Override
public void saveStudent(Student student){
try {
//2.连接数据库
conn = JDBCUtil.getconn();//获取链接对象
//3.创建语句
String sql="insert into student (name,age)values('"+student.getName()+"',"+student.getAge()+")";
//4.执行语句
Statement connStatement = conn.createStatement();
int i = connStatement.executeUpdate(sql);
if(i>0){
System.out.println("添加成功");
}else {
System.out.println("添加失败");
}
//5.释放资源
}catch (Exception e){
e.printStackTrace();
}finally {
JDBCUtil.close(conn,connStatement,null);
}
}
@Override
public void deleteStudent(int id) {
try {
//2.连接数据库
conn = JDBCUtil.getconn();//获取链接对象
//3.创建语句
String sql="delete from student where id="+id+"";
//4.执行语句
Statement connStatement = conn.createStatement();
int i = connStatement.executeUpdate(sql);
if(i>0){
System.out.println("删除成功");
}else {
System.out.println("删除失败");
}
//5.释放资源
}catch (Exception e){
e.printStackTrace();
}finally {
JDBCUtil.close(conn,connStatement,null);
}
}
@Override
public Student get(int id) {
try {
//2.连接数据库
conn =JDBCUtil.getconn();//获取链接对象
//3.创建语句
String sql="select * from student where id="+id+"";
//4.执行语句
Statement connStatement = conn.createStatement();
resultSet = connStatement.executeQuery(sql);
Student s=null;
if (resultSet.next()){
s=new Student(resultSet.getInt("id"),resultSet.getString("name"),resultSet.getInt("age"));
return s;
}
//5.释放资源
}catch (Exception e){
e.printStackTrace();
}finally {
JDBCUtil.close(conn,connStatement,resultSet);
}
return null;
}
@Override
public List<Student> getList() {
try {
//2.连接数据库
conn = JDBCUtil.getconn();//获取链接对象
//3.创建语句
String sql="select * from student ";
//4.执行语句
Statement connStatement = conn.createStatement();
resultSet = connStatement.executeQuery(sql);
List<Student> list=new ArrayList<Student>();;
while (resultSet.next()){
Student s=new Student(resultSet.getInt("id"),resultSet.getString("name"),resultSet.getInt("age"));
list.add(s);
}
return list;
//5.释放资源
}catch (Exception e){
e.printStackTrace();
}finally {
JDBCUtil.close(conn,connStatement,resultSet);
}
return null;
}
}
Statement接口
Statement接口作用
用于进行Java程序和数据库之间的数据传输
具体类有3个实现
Statement
用于对数据库进行通用访问,使用的是静态sql
PreparedStatement
PreparedStatement 用于预编译模板SQL语句,在运行时接受sql输入参数
CallableStatement
要访问数据库存储过程时使用 也可以接受运行时输入参数。
预编译语句
没有预编译语句,所有的sql都是进行拼接
预编译语句
注意,它们都没有参数
PreparedStatement 用于预编译模板SQL语句
在性能和代码灵活性上有显著地提高 PreparedStatement
对象使用 ? 作为占位符,即参数标记;
使用 setXXX( index,value) 方法将值绑定到参数中
每个参数标记是其顺序位置引用,注意 index 从 1 开始;
PreparedStatement 对象执行SQL语句
executeQuery()
executeUpdate()
内部优化
支持优化情况
MySql不支持 Oracle支持
更安全
可以防SQL注入
什么是sql注入
就是通过把SQL命令插入到Web表单提交或输入域名或页面请求的查询字符串,最终达到欺骗服务器执行恶意的SQL命令
用户登录
为什么PreparedStatement就能够防注入
之所以PreparedStatement能防止注入, 是因为它把单引号转义了,变成了 /',
这样一来,就无法截断SQL语句,进而无法拼接SQL语句 基本上没有办法注入了。
PreparedStatement改写dao
public class StudentDaoImpl implements IStudentDao {
Connection conn =null;
Statement connStatement=null;
ResultSet resultSet=null;
PreparedStatement preparedStatement=null;
@Override
public void saveStudent(Student student){
try {
//2.连接数据库
conn = JDBCUtil.getconn();//获取链接对象
//3.创建语句
String sql="insert into student (name,age)values(?,?)";
//4.执行语句
PreparedStatement preparedStatement = conn.prepareStatement(sql);
preparedStatement.setString(1,student.getName());
preparedStatement.setInt(2,student.getAge());
int i = preparedStatement.executeUpdate();
if(i>0){
System.out.println("添加成功");
}else {
System.out.println("添加失败");
}
//5.释放资源
}catch (Exception e){
e.printStackTrace();
}finally {
JDBCUtil.close(conn,preparedStatement,null);
}
}
@Override
public void deleteStudent(int id) {
try {
//2.连接数据库
conn = JDBCUtil.getconn();//获取链接对象
//3.创建语句
String sql="delete from student where id=?";
//4.执行语句
preparedStatement = conn.prepareStatement(sql);
preparedStatement.setInt(1,id);
int i = preparedStatement.executeUpdate();
if(i>0){
System.out.println("删除成功");
}else {
System.out.println("删除失败");
}
//5.释放资源
}catch (Exception e){
e.printStackTrace();
}finally {
JDBCUtil.close(conn,preparedStatement,null);
}
}
@Override
public Student get(int id) {
try {
//2.连接数据库
conn =JDBCUtil.getconn();//获取链接对象
//3.创建语句
String sql="select * from student where id=?";
//4.执行语句
preparedStatement = conn.prepareStatement(sql);
preparedStatement.setInt(1,id);
ResultSet resultSet = preparedStatement.executeQuery();
Student s=null;
if (resultSet.next()){
s=new Student(resultSet.getInt("id"), resultSet.getString("name"), resultSet.getInt("age"));
return s;
}
//5.释放资源
}catch (Exception e){
e.printStackTrace();
}finally {
JDBCUtil.close(conn,preparedStatement,resultSet);
}
return null;
}
@Override
public List<Student> getList() {
try {
//2.连接数据库
conn = JDBCUtil.getconn();//获取链接对象
//3.创建语句
String sql="select * from student ";
//4.执行语句
Statement connStatement = conn.createStatement();
resultSet = connStatement.executeQuery(sql);
List<Student> list=new ArrayList<Student>();;
while (resultSet.next()){
Student s=new Student(resultSet.getInt("id"),resultSet.getString("name"),resultSet.getInt("age"));
list.add(s);
}
return list;
//5.释放资源
}catch (Exception e){
e.printStackTrace();
}finally {
JDBCUtil.close(conn,connStatement,resultSet);
}
return null;
}
}
调用存储过程
1.在数据库当中定义一个存储过程
CREATE PROCEDURE getname(in name varchar(255))
begin
select * from emp where ename=name;
end
2.JDBC调用一个参数的存储过程
public static void main(String[] args) throws SQLException {
Connection getconn = JDBCUtil.getconn();
CallableStatement callableStatement = getconn.prepareCall("{call getname(?)}");
callableStatement.setString(1,"李白");
ResultSet resultSet = callableStatement.executeQuery();
if (resultSet.next()) {
System.out.println(resultSet.getInt(1));
System.out.println(resultSet.getString(2));
}
}
3.编写输入参数和输出参数的存储过程
public static void main(String[] args) throws SQLException {
Connection getconn = JDBCUtil.getconn();
CallableStatement callableStatement = getconn.prepareCall("{call getoutsalary(?,?)}");
callableStatement.setString(1,"李白");
callableStatement.registerOutParameter(2, Types.VARBINARY);
callableStatement.execute();
String string = callableStatement.getString(2);
System.out.println(string);
JDBCUtil.close(getconn,callableStatement,null);
}
事务
事务问题
zs与ls进行转账
1.检查zs账户余额
2.减少zs账户1000
3.增加ls账户1000
出现异常情况
在第2步和第3步之间弄一个异常 int a = 1 / 0; 一个人的账户减少,另一个没有加
处理事务
默认情况下, 事务是自动提交的
要设置为手动提交
处理事务过程
关闭自动提交
conn.setAutoCommit(false);
没有问题时,提交事务
conn.commit();
出现异常时,进行回滚操作
conn.rollback()
回滚之后,事务结束。释放资源 出现异常,没有提交,也不会更新数据库,但是会占用资源 所以要出现异常时,进行回滚操作
只有增、册、改才需要事务,查询不需要事务 以后发现自己写的代码是正确的,测试也成功,但是数据库当中的数据不变 事务没有提交
InnoDB才支持外键和事务。MyISAM不支持外键和事务
示例:
设置事务的隔离级别
conn.setTransactionIsolation(Connection.TRANSACTION_REPATABLE_READ)
批处理
什么是批处理
一次性执行多条Sql语句,允许多条语句一次性提交给数据库批量处理
比单独提交处理要效率高
批处理方法
addBatch(String)//添加需要批处理的sql语句
executeBatch()/执批处理
支持情况
mysql默认情况下是不支持批处理的 从5.1.13开始,添加了一个rewriteBatchedStatement参数
之前插入10条数据要17.4秒 从5.1.13开始提高到了1.6秒
添加参数
存储图片
数据库当中的BLOB
存储图片、音频、视频等多媒体信息 以二进制流的形式
真正开发中, 不会把二进制的文件存放在数据库当中 把文件存储的路径保存在数据库当中,以后再取出路径,到该路径下读取信息
BLOB类型
类型 | 字节 |
---|---|
TINYBLOB | 255个字节 |
BLOB | 65535字节 |
MEDIUMBLOB | 16M |
LONGBLOB | 4G |
操作
1.在表中添加一个字段,类型选择为blob类型
2.通过代码存入一张图片到数据库当中
3.从数据库当中把存储的图片取出
获取自动生成的主键
在我们设置设计表时,会设置主键,自动增长的主键
有时候我们插入数据时,要想知道我们生成的主键是多少
需求场景
用户注册时,添加用户名,密码后 插入到数据库当中
跳转成完善个人信息页
完善信息更新到刚插入的那个记录当中,所以要获取刚插入的那个主键是多少
不能使用用户名来做条件,用户名有重名情况
所以插入完后,就应该获取刚插入的那个主键
获取方法
- Statement语句
创建语句时,设置可以获取主键
st.executeUpdate(sql,Statement.RETURN_GENERATED_KEYS)
通过语句对象的getGeneratedKeys获取主键
2. Preparement语句
在创建语句时,传入参数
Statement.RETURN_GENERATED_KEYS
通过语句对象的getGeneratedKeys获取主键
什么是连接池
没有连接池的情况
每次CRUD操作都要使用数据库的时候,都要创建一个数据库连接对象
普通的JDBC数据库连接使用 DriverManager 来获取
每次向数据库建立连接的时候都要将 Connection 加载到内存中
然后再验证用户名和密码花费时间0.05s~1s左右
每次CRUD操作就向数据库要要一个连接 执行完成后再断开连接。这样的方式将会消耗大量的资源和时间。
数据库的连接资源并没有得到很好的重复利用 若同时有几百人甚至几千人在线,
频繁的进行数据库连接操作将占用很多的系统资源,严重的甚至会造成服务器的崩溃。
数据库连接池
池:保存对象的容器
连接池:保存数据库连接对象的容器
作用:
- 初始化时创建一定数量的对象。需要时直接从池中取出一个空闲对象,
- 用完后并不直接释放掉对象,而是再放到对象池中以方便下一次对象请求可以直接复用。
- 池技术的优势是,可以消除对象创建所带来的延迟,从而提高系统的性能。
数据库连接池
- 数据库连接池的基本思想就是为数据库连接建立一个“缓冲池”。
- 预先在缓冲池中放入一定数量的连接,当需要建立数据库连接时,
- 只需从“缓冲池”中取出一个,使用完毕之后再放回去。
- 我们可以通过设定连接池最大连接数来防止系统无尽的与数据库连接
- 可以通过连接池的管理机制监视数据库的连接的数量﹑使用情况,为系统开发﹑测试及性能调整提供依据。
示意图
连接对象是以链表形式存放,已释放的连接,放到最后。
从最前面获取连接对象
连接池中的属性
合理的设置连接池的属性,会提高连接池的性能
1.连接数据库时需要的4个要素:驱动名称,数据库地址,用户名,密码
2.初始化连接数 :初始化时,连接池当中创建多少个Connection对象
3.最大连接数 : 连接池当中最多存储多少个Connection对象
4.最小连接数 : 连接池当中最少得存多个少Connection对象
5.最大的空闲时间: 如果一个获取了连接对象,在指定时间内没有任何动作,就会自动释放链接
6.最大等待时间 : 在指定时间内,尝试获取连接,如果超出了指定时间,就会提示获取失败
连接池使用
连接池是使用javax.sql.DataSource接口来表示连接池
DataSource和jdbc一样,也是只提供一个接口,由第三方组织来提供
常见连接池:
- DBCP: Spring推荐,Tomcat的数据源使用的就是DBCP
- C3P0:C3P0是一个开放源代码的JDBC连接池,它在lib目录中与Hibernate一起发布,从2007年就没有更新了,性能比较差。
- Druid:阿里巴巴提供的连接池-德鲁伊-号称最好的连接池,它里面除了这些, 还有更多的功能。
DataSource数据源和连接池Connection Pool是同一个东西,只是叫法不一样而已
使用连接池与不使用连接池区别
- 获取方式不同
传统:Connection conn = DriverManager.getConnection(url.userName,pwd);
连接池:Conneciton conn = DataSource对象.getConnection();
2.释放资源不同
传统:和数据库断开conn.close();
连接池:把数据库连接对象还给连接池,还可以给下一个人来使用
连接池操作
主要是学习如何创建DataSource对象,再从DataSource对象中获取Connection对象
这些都是第三方提供者给我们提供好的。直接使用就行。 获取连接对象之后,其余的操作和以前是一样的。
不同的数据库连接池,就是在创建DataSource上不一样。
创建DataSource
- 使用DBCP
1.导入相关的jar包
commons-dbcp-1.4.jar
commons-pool-1.5.6.jar
2.在项目中使用连接池来获取连接
获取连接连接池数据源 通过数据源获取连接对象
配置文件
什么是配置文件
资源文件,是以.properties作为扩展名的文件 在上面使用数据库过程当中,我们把库的连接地址,用户名,密码都写在了代码当中。
不便于后期的维护以后我们的项目是部署到别人的服务器(电脑)当中 别人电脑中的数据库用户名和密码不是你这一个root,和1234了
此时就要去到项目中找对应的代码去修改帐号和密码的地方 但是,部署项目是实施人员做的,
他们不懂Java代码。可能一不小心把你的Java代码删错了,为了安全操作,不能让它直接修改我的Java代码。
此时,我们专门为数据库提供一个配置文件,里面就专门存放连接数据库的相关信息
配置文件的书写
db.properties
内部是以key-value的形式存放
userName=root password=1234
在Java中读取配置文件
改写DBCP
创建db.properties配置文件
driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/jdbc_db
username=root
password=1234
通过配置文件方式获取数据源
使用Druid
开源地址:https://github.com/alibaba/druid
Druid是一个JDBC组件库,包括数据库连接池、SQL Parser等组件。DruidDataSource是最好的数据库连接池。
从DBCP到Druid:DruidDataSource的配置是兼容DBCP的。从DBCP迁移到DruidDataSource,只需要修改数据源的实现类就可以了。
使用
1.导包
2.编写代码
Dao代码重构
存在的问题
在DAO当中执行的保存方法,更新方法,删除这些DML操作有太多重复代码
重构代码原则
同一个类中
在一个类当中有多个方法当中有太多相同的代码
不同的地方通过参数传递进去
把它们抽到一个方法当中
不同类中
不同类当中有共同的代码给抽取到一个新类当中。
大家同时共享该类中的内容
抽取DML方法
1.设计一个方法
2.要求传入两个参数
一个sql语句 一个参数:第一个参数sql语句模板 第二个参数为可变参数,设置语句参数值
3.返回值
返回值为int,受影响的行数。
DAO重构示意图
调用示意图
抽取DQL
抽取之前的代码
抽取之后代码
遗留问题
查询的时候,只能查询学生::可以使用泛型解决
处理结果集的时候:
每一个都封装成了student对象, 这写死了
原因 不知道封装成什么类型的对象 就不应该处理结果集
解决办法:
把处理结果集的行为交给每个DAO 为了规范每个DAO的处理结果集,大家都叫同样的名字,这样在模板中就可以调用同一个名称
定义一个处理结果集的接口 代码实现
1.定义一个接口 声明一个处理结果集方法
2.在具体DAO当中实现接口 实现处理结果集方法
3.创建一个接口实现对象传给查询方法
4.在查询方法当中调用处理结果集方法
总流程图
处理结果集泛型
1.直接定义为T 处理所有结果集都必须得要返回List 有的时候没有必要返回List,想要查询的仅仅是一个结果整数,此时还必须得要从List当中出来
2.有调用者自己来决定返回什么内容 统一规定返回类型为T类型,调用者使用时去决定是什么类型
3.实现类当中实现接口时,要指定返回什么类型
4.声明方法时,声明返回值为一个泛型类型,具体是什么类型,有参数来指定
整体示意图
结果集处理器
存在的问题
该类只能将结果集中的一行数据封装成一个Student对象 我们如果有多个domain,就得提供多个结果集处理器
想法
自动的把一行行数据封装成对象,自动设置属性,结果集处理器 处理不同表时,干的事都一样
1.给我一个要封装的类
2.自动设置属性
规定
1.表中的列名必须和对象中的属性名相同
2.规定表中的类型必须和Java中的类型匹配
想要完成此项工作,就要使用内省机制
编写
1.创建一个类实现结果集处理器 实现的时候也不知道是什么类型,所以也可以指定为一个泛型
2.提供一个字段,表示要封装成对象的类型
3.提供一个构造器,在创建时,就指定是什么类型
4.在实现方法当中,通过内省机制获取所有属性名称,做为列名
5.通过传入的类字节码创建对象
6. 通过内省获取字节码中所有的信息
7.获取所有的属性描述器
8.遍历属性描述器,获取对象的属性名称 ,规定的属性名和列名相同
9.通过列名获取结果集中的值
10.调用属性的set方法把属性的值设置进去
内省
Class类型
什么是Class类型
字节码类型 所有的字节码把他们当做一个群体类型
通过字节码创建对象
Student stu = Student.class.newInstance();
内省
什么是内省
用于查看和操作JavaBean中的属性
类
Introspector
Introspector.getBeanInfo(A.class,B.class);
获取指定类当中的字节码属性信息 获取A类及A类父类当中的属性,但是不包括指定的类。 返回值BeanInfo
BeanInfo
getPropertyDescriptors()
获取JavaBean中的属性描述器
返回的是一个数组(PropertyDescriptor[])
每一个属性都有描述器
JavaBean中有多个属性,所以返回了一个数组
获取属性名
pd.getName();
获取get方法
pd.getReadMethod();
获取set方法
pd.getWriteMethod
动态调用方法
方法名.invoke(对象名称,“值”)
DBUtils
什么是DBUtils
DBUtils是Apache公司编写的数据库操作实用的工具,小巧,简单,实用 封装了对JDBC的操作,简化了JDBC操作
QueryRunner
QueryRunner(DataSource ds) 提供数据源连接池,会自动帮你创建连接
Update(String sql,Object...obj)执行更新数据
query(String sql, ResultSetHandler<T> rsh,Object...params)执行查询
ResultHandler
query(sql, new BeanHandler<Student>(Student.class), params)
把查询的结果封装成一个指定对象
query(sql, new BeanListHandler<Student>(Student.class)
把查询结果封装成一个指定对象集合
qr.query(sql, new ScalarHandler())
查询单个值,返回为一个Long类型
qr.query(sql, new MapListHandler())
把查询的结果封装成一个Map集合
query(sql, new ColumnListHandler("列名"))
查询指定的列