1. O histórico de desenvolvimento da estrutura ORM
1. Operação JDBC
1.1 Recursos da Operação JDBC
最初的时候我们肯定是直接通过jdbc来直接操作数据库的,本地数据库我们有一张t_user表,那么我们的操作流程是
复制代码
// 注册 JDBC 驱动
Class.forName("com.mysql.cj.jdbc.Driver");
// 打开连接
conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/mybatisdb?characterEncoding=utf-8&serverTimezone=UTC", "root", "123456");
// 执行查询
stmt = conn.createStatement();
String sql = "SELECT id,user_name,real_name,password,age,d_id from t_user where id = 1";
ResultSet rs = stmt.executeQuery(sql);
// 获取结果集
while (rs.next()) {
Integer id = rs.getInt("id");
String userName = rs.getString("user_name");
String realName = rs.getString("real_name");
String password = rs.getString("password");
Integer did = rs.getInt("d_id");
user.setId(id);
user.setUserName(userName);
user.setRealName(realName);
user.setPassword(password);
user.setDId(did);
System.out.println(user);
}
复制代码
As etapas específicas da operação são primeiro introduzir a dependência do driver MySQL no pom.xml, prestar atenção à versão do banco de dados MySQL
- Driver registrado Class.forName
- Obter um objeto de conexão
- Criar um objeto Statement
- O método execute() executa a instrução SQL e obtém o conjunto de resultados ResultSet
- Atribuir valores às propriedades POJO através do conjunto de resultados ResultSet
- Finalmente fechar recursos relacionados
这种实现方式首先给我们的感觉就是操作步骤比较繁琐,在复杂的业务场景中会更麻烦。尤其是我们需要自己来维护管理资源的连接,如果忘记了,就很可能造成数据库服务连接耗尽。同时我们还能看到具体业务的SQL语句直接在代码中写死耦合性增强。每个连接都会经历这几个步骤,重复代码很多,总结上面的操作的特点:
复制代码
- duplicação de código
- Gestão de recursos
- processamento do conjunto de resultados
- Acoplamento SQL
Podemos tentar resolver esses problemas por nós mesmos
1.2 Otimização JDBC 1.0
针对常规jdbc操作的特点,我们可以先从代码重复和资源管理方面来优化,我们可以创建一个工具类来专门处理这个问题
复制代码
public class DBUtils {
private static final String JDBC_URL = "jdbc:mysql://localhost:3306/mybatisdb?characterEncoding=utf-8&serverTimezone=UTC";
private static final String JDBC_NAME = "root";
private static final String JDBC_PASSWORD = "123456";
private static Connection conn;
/**
* 对外提供获取数据库连接的方法
* @return
* @throws Exception
*/
public static Connection getConnection() throws Exception {
if(conn == null){
try{
conn = DriverManager.getConnection(JDBC_URL,JDBC_NAME,JDBC_PASSWORD);
}catch (Exception e){
e.printStackTrace();
throw new Exception();
}
}
return conn;
}
/**
* 关闭资源
* @param conn
*/
public static void close(Connection conn ){
close(conn,null);
}
public static void close(Connection conn, Statement sts ){
close(conn,sts,null);
}
public static void close(Connection conn, Statement sts , ResultSet rs){
if(rs != null){
try {
rs.close();
}catch (Exception e){
e.printStackTrace();
}
}
if(sts != null){
try {
sts.close();
}catch (Exception e){
e.printStackTrace();
}
}
if(conn != null){
try {
conn.close();
}catch (Exception e){
e.printStackTrace();
}
}
}
}
复制代码
O código de operação jdbc correspondente pode ser simplificado da seguinte maneira
/**
*
* 通过JDBC查询用户信息
*/
public void queryUser(){
Connection conn = null;
Statement stmt = null;
User user = new User();
ResultSet rs = null;
try {
// 注册 JDBC 驱动
// Class.forName("com.mysql.cj.jdbc.Driver");
// 打开连接
conn = DBUtils.getConnection();
// 执行查询
stmt = conn.createStatement();
String sql = "SELECT id,user_name,real_name,password,age,d_id from t_user where id = 1";
rs = stmt.executeQuery(sql);
// 获取结果集
while (rs.next()) {
Integer id = rs.getInt("id");
String userName = rs.getString("user_name");
String realName = rs.getString("real_name");
String password = rs.getString("password");
Integer did = rs.getInt("d_id");
user.setId(id);
user.setUserName(userName);
user.setRealName(realName);
user.setPassword(password);
user.setDId(did);
System.out.println(user);
}
} catch (SQLException se) {
se.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
} finally {
DBUtils.close(conn,stmt,rs);
}
}
/**
* 通过JDBC实现添加用户信息的操作
*/
public void addUser(){
Connection conn = null;
Statement stmt = null;
try {
// 打开连接
conn = DBUtils.getConnection();
// 执行查询
stmt = conn.createStatement();
String sql = "INSERT INTO T_USER(user_name,real_name,password,age,d_id)values('wangwu','王五','111',22,1001)";
int i = stmt.executeUpdate(sql);
System.out.println("影响的行数:" + i);
} catch (SQLException se) {
se.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
} finally {
DBUtils.close(conn,stmt);
}
}
复制代码
Mas as etapas gerais da operação ainda parecerão mais complicadas, então podemos otimizar ainda mais
1.3 Otimização JDBC 2.0
Podemos otimizar o método de operação DML, primeiro resolver o problema de acoplamento SQL e encapsular o método de operação DML em DBUtils
/**
* 执行数据库的DML操作
* @return
*/
public static Integer update(String sql,Object ... paramter) throws Exception{
conn = getConnection();
PreparedStatement ps = conn.prepareStatement(sql);
if(paramter != null && paramter.length > 0){
for (int i = 0; i < paramter.length; i++) {
ps.setObject(i+1,paramter[i]);
}
}
int i = ps.executeUpdate();
close(conn,ps);
return i;
}
复制代码
Então, na operação DML, podemos simplificá-la nas seguintes etapas
/**
* 通过JDBC实现添加用户信息的操作
*/
public void addUser(){
String sql = "INSERT INTO T_USER(user_name,real_name,password,age,d_id)values(?,?,?,?,?)";
try {
DBUtils.update(sql,"wangwu","王五","111",22,1001);
} catch (Exception e) {
e.printStackTrace();
}
}
复制代码
Obviamente, esse método será muito mais simples do que o uso inicial, mas ainda não resolvemos o problema do processamento do conjunto de resultados do ResultSet durante o processamento da consulta, portanto, precisamos continuar otimizando
1.4 Otimização JDBC 3.0
针对ResultSet的优化我们需要从反射和元数据两方面入手,具体如下
复制代码
/**
* 查询方法的简易封装
* @param sql
* @param clazz
* @param parameter
* @param <T>
* @return
* @throws Exception
*/
public static <T> List<T> query(String sql, Class clazz, Object ... parameter) throws Exception{
conn = getConnection();
PreparedStatement ps = conn.prepareStatement(sql);
if(parameter != null && parameter.length > 0){
for (int i = 0; i < parameter.length; i++) {
ps.setObject(i+1,parameter[i]);
}
}
ResultSet rs = ps.executeQuery();
// 获取对应的表结构的元数据
ResultSetMetaData metaData = ps.getMetaData();
List<T> list = new ArrayList<>();
while(rs.next()){
// 根据 字段名称获取对应的值 然后将数据要封装到对应的对象中
int columnCount = metaData.getColumnCount();
Object o = clazz.newInstance();
for (int i = 1; i < columnCount+1; i++) {
// 根据每列的名称获取对应的值
String columnName = metaData.getColumnName(i);
Object columnValue = rs.getObject(columnName);
setFieldValueForColumn(o,columnName,columnValue);
}
list.add((T) o);
}
return list;
}
/**
* 根据字段名称设置 对象的属性
* @param o
* @param columnName
*/
private static void setFieldValueForColumn(Object o, String columnName,Object columnValue) {
Class<?> clazz = o.getClass();
try {
// 根据字段获取属性
Field field = clazz.getDeclaredField(columnName);
// 私有属性放开权限
field.setAccessible(true);
field.set(o,columnValue);
field.setAccessible(false);
}catch (Exception e){
// 说明不存在 那就将 _ 转换为 驼峰命名法
if(columnName.contains("_")){
Pattern linePattern = Pattern.compile("_(\\w)");
columnName = columnName.toLowerCase();
Matcher matcher = linePattern.matcher(columnName);
StringBuffer sb = new StringBuffer();
while (matcher.find()) {
matcher.appendReplacement(sb, matcher.group(1).toUpperCase());
}
matcher.appendTail(sb);
// 再次调用复制操作
setFieldValueForColumn(o,sb.toString(),columnValue);
}
}
}
复制代码
Após encapsular os métodos acima, nossa operação de consulta pode ser simplificada como
/**
*
* 通过JDBC查询用户信息
*/
public void queryUser(){
try {
String sql = "SELECT id,user_name,real_name,password,age,d_id from t_user where id = ?";
List<User> list = DBUtils.query(sql, User.class,2);
System.out.println(list);
} catch (Exception e) {
e.printStackTrace();
}
}
复制代码
Dessa forma, só precisamos nos concentrar nas operações principais do SQL ao operar os dados no banco de dados. Claro, o design acima ainda é relativamente rudimentar. Neste momento, o DbUtils no Apache é uma boa escolha.
Trabalhem juntos para criar e crescer juntos! Este é o 24º dia da minha participação no "Nuggets Daily New Plan · August Update Challenge", clique para ver os detalhes do evento .