JDBC
jdbc是一个组件,能都被多种数据库访问,由java语言编写的类和接口组成
为什么要学习JDBC规范
JDBC是java连接数据库的一个标准,由数据库各个厂商来完成接口的实现
JDBC执行规范
- 注册驱动,加载JDBC驱动
- 获取连接对象
- 获取预编译语句对象preparedStatement
- 执行SQL语句
- 释放资源
1.Class.forName(com.mysql.jdbc.Driver);
2.Connection connection =DriverManager.getConnection("jdbc:mysql://localhost:3306/数据库名称","root(用户名)","密码")
//如果是本机端口为3306,可以省略
Connection connection =DriverManager.getConnection("jdbc:mysql:///数据库名称","root(用户名)","密码")
// ? 使用的是占位符来进行拼接
3.String sql = "update t_student set name=? ,age=?,email=? where id=?"
//获取预编译对象,传入sql
4.PreparedStatement statement = connection.prepareStatement(sql);
//执行
statement.executeUpdate();
//释放资源
statement.close();
connection.close();
在使用PreparedStatement 调用executeUpdate()等操作时,最好不要传入sql语句
Statement 和PreparedStatement的区别
- PreparedStatement提供更好的性能(预编译)
- PreparedStatement更安全,可以防止SQL注入问题 ,使用 ?占位符,提前预编译到数据库中,语句结构固定下来
DAO思想
DAO(Data Acess Object ):数据访问对象 (面向对象的数据库接口),自己定义接口与实现实现类的设计思想,用于封装减少重复代码,DAO其实就是一个组件(可以重复使用)
定义DAO包
使用倒置域名定义DAO包
- cn.k.dao: //定义dao包,用来定义类的接口
dao包中接口的命名规范 IXxxDAO
- cn.k.dao.impl : //定义impl包,拿来装dao包中接口的实现类
impl包中的实现类命名规范 XXXDAOImpl
- cn.k.domain: //定义domain包,用来放类的对象
- cn.k.util :工具包,用来放工具类
- cn.k.test: 放置测试类
DAO查询操作
ResultSet接口 :通过执行DQL语句查询之后的结果对象
ResultSet对象具有指向其当前数据行的光标.next()方法将光标移动到下一行,移到下一行时(上一行的数据会被覆盖)如果Result对象没有下一行时返回false,所以可以使用while循环进行迭代
例子:
- 实体类
package cn.k.domain;
@Data
public class Student {
private Long id;
private String name;
private Integer age;
private String email;
}
- 定义接口
public interface IStudentDAO {
//操作数据库的增删改查方法
//添加
void insert(Student student);
//删除
void deleteById(Long id);
//修改
void update(Student student);
//查询
Student selectById(Long id);
//查询所有
List<Student> selectAll();
}
- 定义实现类(其中一部分)
public class StudentDAOImpl implements IStudentDAO {
@Override
public Student selectById(Long id) {
Student student = new Student();
String sql = "select*from t_student where id=?";
Connection connection = null;
PreparedStatement statement = null;
ResultSet resultSet = null;
try {
//加载驱动
Class.forName("com.mysql.jdbc.Driver");
//获取连接对象
connection = DriverManager.getConnection("jdbc:mysql:///product", "root", "123");
//获取预编译对象
statement = connection.prepareStatement(sql);
//设置参数
statement.setLong(1, id);
//发送 数据到数据库服务器
resultSet = statement.executeQuery();
//迭代结果集
while (resultSet.next()) {
long id1 = resultSet.getLong("id");
String name = resultSet.getString("name");
int age = resultSet.getInt("age");
String email = resultSet.getString("email");
student.setId(id1);
student.setName(name);
student.setAge(age);
student.setEmail(email);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
//关闭资源
if (statement != null) {
try {
statement.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (connection != null) {
try {
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
return student;
}
}
重构思想
对代码进行优化,即重构,从代码量来讲更加简约,将相同构造的代码抽取出来,进行分类
使用JDBC的规范来实现接口
代码的重构
1.对资源释放进行抽取; 2.对连接对象进行抽取 ; 3.对连接数据库的参数进行了抽取
- 扩展
4.对DML操作进行模板化实现,达到代码的复用性目的
5.对DQL操作进行模板化实现,达到代码的复用性目的
对teacher类定义增删改查方法查询数据库(重构)
- 定义工具类Util包
- JDBCUtil (封装关闭异常方法,加载数据库配置文件properties)
package cn.kent.util;
import java.sql.*;
import java.util.Properties;
public final class JDBCUtil {
private JDBCUtil(){}
private static Properties properties=new Properties();
//静态代码块
static {
try {
//加载配置文件
properties.load(Thread.currentThread().getContextClassLoader().getResourceAsStream("db.properties"));
//加载驱动,只加载一次
Class.forName("com.mysql.jdbc.Driver");
} catch (Exception 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 release(PreparedStatement statement, Connection connection, ResultSet resultSet){
if (statement != null) {
try {
statement.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (connection != null) {
try {
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (resultSet != null) {
try {
resultSet.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
//释放资源,重载关闭两个资源
public static void release(PreparedStatement statement, Connection connection){
release(statement,connection,null);
}
}
- DQLUtil类,(编写工具方法executeUpdate,executeQuery等)
public abstract class DQLUtil {
//编写DML方法,传入SQL语句,与可变参数
public static void executeUpdate(String sql,Object...objects){
Connection connection=null;
PreparedStatement statement=null;
try {
//获取连接对象,调用JDBCUtil中的getConnection()获取到连接对象
connection=JDBCUtil.getConnection();
//获取语句对象
statement=connection.prepareStatement(sql);
//设置参数
for (int i = 0; i < objects.length; i++) {
statement.setObject(i+1,objects[i]);
}
//执行sql的DML方法
statement.executeUpdate();
} catch (Exception e) {
e.printStackTrace();
}finally {
//释放资源
JDBCUtil.release(statement,connection);
}
}
//查询方法 ,定义泛型 ,提供字节码 ,自定义接口ResultSeHandler
public static <T> List<T> executeQuery(String sql, ResultSeHandler handler,Class<T> als, Object...objects) {
List<T> list =new ArrayList<>();
Connection connection=null;
PreparedStatement statement=null;
ResultSet resultSet=null;
try {
//获取连接对象
connection=JDBCUtil.getConnection();
//获取语句对象
statement=connection.prepareStatement(sql);
//设置参数
for (int i = 0; i < objects.length; i++) {
statement.setObject(i+1,objects[i]);
}
//执行sql
resultSet =statement.executeQuery();
//需要进行返回值,调用接口实现中的方法
List list1 = handler.handleResultSet(resultSet, als);
return list1;
} catch (Exception e) {
e.printStackTrace();
}finally {
JDBCUtil.release(statement,connection,resultSet);
}
return list;
}
}
- 定义ResultSeHandler< T > 接口
public interface ResultSeHandler<T> {
List<T> handleResultSet(ResultSet resultSet,Class<T> acl);
}
- ResultSetHandlerImpl< T >实现类
public class ResultSetHandlerImpl<T> implements ResultSeHandler<T> {
@Override
public List<T> handleResultSet(ResultSet resultSet, Class<T> acl) {
List<T> list =new ArrayList<>();
try {
BeanInfo beanInfo = Introspector.getBeanInfo(acl, Object.class);
PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
while (resultSet.next()) {
T t =acl.newInstance();
for (PropertyDescriptor propertyDescriptor : propertyDescriptors) {
//获取对象中的属性
String name = propertyDescriptor.getName();
//获取数据库中的列名
Object value = resultSet.getObject(name);
// System.out.println(value);
//获取set方法
Method writeMethod = propertyDescriptor.getWriteMethod();
//执行
writeMethod.invoke(t,value);
}
//装进集合
list.add(t);
}
} catch (Exception e) {
e.printStackTrace();
}
return list;
}
}
- domain包
- 定义Teacher类
@Data
public class Teacher {
private int id;
private String name;
private Integer age;
private String email;
}
- dao包
-
定义接口ITeacherDAO
public interface ITeacherDAO { //添加 void insert(Teacher teacher); //删除 void delete(int id); //改 void update(Teacher teacher); //查询指定id Teacher select(int id); //查询所有 List<Teacher> selectAll(); }
- impl包
- 定义TeacherDAOImpl实现类
public class TeacherDAOImpl implements ITeacherDAO { //插入 @Override public void insert(Teacher teacher) { String sql = "insert into t_teacher (name,age,email) values(?,?,?)"; DQLUtil.executeUpdate(sql,teacher.getName(),teacher.getAge(),teacher.getEmail()); } //删除 @Override public void delete(int id) { String sql ="delete from t_teacher where id =?"; DQLUtil.executeUpdate(sql,id); } //更新 @Override public void update(Teacher teacher) { String sql ="update t_teacher set name=?,age=?,email=? where id=?"; DQLUtil.executeUpdate(sql,teacher.getName(),teacher.getAge(),teacher.getEmail(),teacher.getId()); } //查询 DQL @Override public Teacher select(int id) { String sql = "select * from t_teacher where id=?"; List<Teacher> list = DQLUtil.executeQuery(sql, new ResultSetHandlerImpl(), Teacher.class, id); if (list!=null &&list.size() >0){ return list.get(0); } return null; } //查询整个集合 @Override public List<Teacher> selectAll() { String sql = "select*from t_teacher"; List<Teacher> list1 = DQLUtil.executeQuery(sql, new ResultSetHandlerImpl(), Teacher.class); return list1; } }
- test包
- 测试类
public class TeacherDAOImplTest {
private static ITeacherDAO teacherDAO =new TeacherDAOImpl();
//插入
@Test
public void insert() {
Teacher teacher = new Teacher();
teacher.setName("月月");
teacher.setAge(12);
teacher.setEmail("我爱小轩");
teacherDAO.insert(teacher);
}
//删除
@Test
public void delete() {
teacherDAO.delete(5);
}
//更新
@Test
public void update() {
Teacher teacher =new Teacher();
teacher.setName("臭猪");
teacher.setAge(18);
teacher.setEmail("fewgw");
teacher.setId(6);
teacherDAO.update(teacher);
}
//查询指定id
@Test
public void select() {
Teacher select = teacherDAO.select(1);
System.out.println(select);
}
//查询集合
@Test
public void selectAll() {
List<Teacher> list = teacherDAO.selectAll();
System.out.println(list);
}
}
JDBC事务操作
事务(Transaction ,简称tx)
让多个操作,捆绑在一起,多个操作中有一个失败,那么整个操作失败
在数据库中,事务是指一组逻辑操作单元,使数据从一种状态换成另外一种状态,保证了操作要么同时成功,要么同时失败
事务的ACID属性
- 原子性(Atomicity):指事务是一个不可分割的工作单位
- 一致性(Consistency):包装数据的完整性,事务必须使数据库从一个一致性状态变换到另外一个状态
- 隔离性(Isolation):指一个事务的执行不能被其他事务干扰
- 持久性(Durability):一旦一个事务被提交,它的数据库中数据的改变是永久性的
对应方法只要事务开启了手动执行, 连接要么提交,要么回滚 不能省略
连接池
javax.sql.DataSource 接口
用来存放多个连接对象,避免了资源浪费
基本四要素:driverClassName,url,username,password
其他属性:对连接对象被限制的配置
- 初始化连接
- 最多连接数
- 最少连接数
- 最长等待时间
- 最长超过时间
常见的DataSource实现
- DBCP:Spring框架推荐
- C3P0:Hibernate框架(基本不用了)
- druid :阿里巴巴连接池
使用DBCP连接池
DBCP与druid步骤基本一致
基本写法
首先导入相关数据池的jar包
//DBCP数据库连接池 ,改造前
public class DBCPDemo {
public static void main(String[] args) throws SQLException {
DataSource dataSource=setupDataSource();
Connection connection=dataSource.getConnection();
System.out.println("connection = " + connection);
}
public static DataSource setupDataSource() {
BasicDataSource ds = new BasicDataSource();
ds.setDriverClassName("com.mysql.jdbc.Driver");
ds.setUsername("root");
ds.setPassword("123");
ds.setUrl("jdbc:mysql:///product");
return ds;
}
}
但是这样去使用连接池,上述代码就会产生了硬编码,我们应该对其进行优化,即主流写法
首先使用DBCP连接池时,对应的数据库信息应该从properties配置文件中读取,使用连接池首先需要创建DataSource对象,其中DBCP已经提供创建该对象的工厂方法BasicDataSourceFactory.createDataSource(Properties properties)
,并且可以传入properties对象并读取该配置文件的内容,那么步骤就简化很多了
- 创建db.properties
//注意名字是有指定写法的,不能随意修改
driverClassName=com.mysql.jdbc.Driver
username=root
password=123
url=jdbc:mysql:///product
- 创建工具类 DBCPUtil
public final class DBCPUtil {
private DBCPUtil() {
}
private static DataSource ds = null;
//使用静态代码块,加载配置文件资源
static {
try {
InputStream inputStream = Thread.currentThread().getContextClassLoader().getResourceAsStream("db.properties");
Properties properties = new Properties();
//加载进properties内存中
properties.load(inputStream);
//创建datasource对象,并传入properties对象
ds = BasicDataSourceFactory.createDataSource(properties);
} catch (Exception e) {
e.printStackTrace();
}
}
//写一个工具方法 ,直接调用
public static DataSource getDataSource() {
return ds;
}
//为了更简便 ,直接把连接对象步骤写进该方法
public static Connection getConnection() {
try {
return ds.getConnection();
} catch (SQLException e) {
e.printStackTrace();
}
return null;
}
}
- 测试类
public static void main(String[] args) throws SQLException {
//直接使用工具类,调用即可,成功连接到数据库
Connection connection =DBCPUtil.getConnection();
System.out.println("connection = " + connection);
}