建立连接
public static Connection getConnection() throws Exception {
//1、反射去加载jar包中com.mysql.jdbc.Drive这个类中得 DriverManagerDriver(new Driver())
Class.forName("com.mysql.jdbc.Driver");
//2、获取连接对象
Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3307/summer_camp2023?characterEncoding=utf8", "root", "123456");
System.out.println(connection);
return connection;
}
Driver类是java连接数据库的驱动类,DriverManager是驱动管理类。
java注册数据库驱动的方式是通过反射来创建实例注册的。
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//
package com.mysql.jdbc;
import java.sql.DriverManager;
import java.sql.SQLException;
public class Driver extends NonRegisteringDriver implements java.sql.Driver {
public Driver() throws SQLException {
}
static {
try {
DriverManager.registerDriver(new Driver());
} catch (SQLException var1) {
throw new RuntimeException("Can't register driver!");
}
}
}
DriverManager.getConnection方法得参数:
- url : 连接数据库得url地址,
协议名:子协议://主机名:端口号/数据库库名?连接参数
- 协议名:java使用jdbc协议来约束与各个数据库之间得连接
- 子协议:一般是指连接什么类型得数据库,可以是mysql、Oracle等等
- 主机名:数据库连接所在的主机地址
- 端口号:数据库连接端口
- 数据库名:需要操作的数据库名
- 连接参数:配置连接属性,例如字符编码、时区等等
- username:连接数据库所需要的用户名
- password: 连接数据库所需要的密码
增删改查
我们已经能够获取连接对象了,但是想要对数据库中表进行CRUD,他却不能够为我们直接所用。
我们需要通过Connection 对象来获取一个**statement
或者是PrepareStatement
**对象。这个对象才是我们能够操作数据表的对象。
- statement:该对象在操作数据库时,sql语句写死的,也就是说并不能动态的设置参数,有可能导致sql注入,因为他如果想要添加sql参数,只能通过拼接字符串的形式。
- PrepareStatement:该对象操作数据库时的sql可以被添加参数,比如根据条件查询,那么可以写成
select * from table where id = ?
,这个?就是一个占位符,用来表示用于填充参数的。使用preparestatement不仅可以让我们不需要去写那么多死的sql,而且还会解决Sql注入问题。PrepareStatement还可以提高执行效率。
增删改
//增删改查
public static int update(String sql, Connection con,Object ...params) throws Exception {
PreparedStatement prepareStatement = con.prepareStatement(sql);
for (int i = 0; i < params.length; i++) {
prepareStatement.setObject(i+1,params[i]);
}
int i = prepareStatement.executeUpdate();
close(con,prepareStatement);
return i;
}
利用PrepareStatement对象操作数据表
@Test
public void testUpdate(){
String sql = "delete from t_emps where id = ?";
try {
//JDBCUtil.getConnection() 获取数据库连接对象
int i = JDBCUtil.update(sql, JDBCUtil.getConnection(),"e0006");
System.out.println(i);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
增删改可以写成一个方法,因为它们只需要返回一个int的类型的数据,表示受影响的行数。
JDBCUtil.getConnection()是通过工具类获取数据库连接,减少代码冗余
查询
多行查询
多行查询是查询中最为复杂的,
//查询多行
public static <T> List<T> queryForList(Class<T> type,String querySql, Connection con,Object ...params) throws Exception {
PreparedStatement prepareStatement = con.prepareStatement(querySql);
for (int i = 0; i < params.length; i++) {
prepareStatement.setObject(i+1,params[i]);
}
ResultSet resultSet = prepareStatement.executeQuery();
//获取结果集的行
ResultSetMetaData metaData = resultSet.getMetaData();
//获取列数
int columnCount = metaData.getColumnCount();
List<T> results = new ArrayList<>();
while (resultSet.next()){
//反射获取实例
T t = type.newInstance();
for (int i = 0; i < columnCount; i++) {
Object value = resultSet.getObject(i+1);
if (value != null){
//根据数据表的列索引获取列名
String columnName = metaData.getColumnName(i+1);
//反射获取该类型指定的属性名
Field field = type.getDeclaredField(columnName);
//允许暴力注入,即使是private修饰的属性
field.setAccessible(true);
//注入属性值
field.set(t,value);
}
}
results.add(t);
}
//关闭资源
close(con,prepareStatement);
return results;
}
这里我们使用到了两个比较陌生的知识——反射和ResultSetMetaData
- 反射:运行时动态获取类信息以及动态调用类中的成分的能力称为Java语言的反射机制。以上代码就是运用了反射的机制,调用者需要传入封装在list集合中的对象类型的Class类。再配合泛型使用,这样我们才知道应该封装什么类型的类到集合中,并且返回相应泛型的集合。
- ResultSetMetaData:该对象是由结果集ResultSet对象获取的,这个对象是我在这个案例中才得知的,它里面封装了结果集的列数,和根据索引获取列名的方法。
我们为PrepareStatement传入参数的方式是,通过形参获取,但是我们并没有写死,而是用**可变参数…**来获取,这样表示调用者可以传入多个参数,并且还可以选择不传。
可变参数只能够再形参上使用,实际上是把所有的参数都封装成数组。
for (int i = 0; i < params.length; i++) {
prepareStatement.setObject(i+1,params[i]);
}
所以我们可以循环来获取这些参数
单行查询
当行查询和多行查询一样,只是返回结果由list变为一个对象,而这个对象就是通过反射获取
public static <T> T queryForObject(Class<T> type ,String sql,Connection con,Object ...params) throws Exception {
PreparedStatement prepareStatement = con.prepareStatement(sql);
for (int i = 0; i < params.length; i++) {
prepareStatement.setObject(i+1,params[i]);
}
//获取实例
T t = null;
ResultSet resultSet = prepareStatement.executeQuery();
ResultSetMetaData metaData = resultSet.getMetaData();
int columnCount = metaData.getColumnCount();
if (resultSet.next()){
t = type.newInstance();
for (int i = 0; i < columnCount; i++) {
Object value = resultSet.getObject(i+1);
if (value != null){
String columnName = metaData.getColumnName(i+1);
Field field = type.getDeclaredField(columnName);
field.setAccessible(true);
field.set(t,value);
}
}
}
//关闭资源
close(con,prepareStatement);
return t;
}
单列查询
//查询单列数据
public static <T> List<T> queryForColumn(Class<T> type,String sql,Connection con,Object ...params) throws Exception {
List<T> results = new ArrayList<>();
PreparedStatement prepareStatement = con.prepareStatement(sql);
for (int i = 0; i < params.length; i++) {
prepareStatement.setObject(i + 1, params[i]);
}
ResultSet resultSet = prepareStatement.executeQuery();
while (resultSet.next()){
T t = null;
t = (T)resultSet.getObject(1);
results.add(t);
}
close(con,prepareStatement);
return results;
}
单个查询
public static <T> T queryForOne(Class<T> type,String sql, Connection con,Object ...params) throws Exception {
PreparedStatement prepareStatement = con.prepareStatement(sql);
for (int i = 0; i < params.length; i++) {
prepareStatement.setObject(i+1,params[i]);
}
ResultSet resultSet = prepareStatement.executeQuery();
//获取结果集的行
ResultSetMetaData metaData = resultSet.getMetaData();
//获取列数
int columnCount = metaData.getColumnCount();
T t = null;
if (resultSet.next()){
t = (T) resultSet.getObject(1);
}
close(con,prepareStatement);
return t;
}