一、前言
最近在编写一个自己的框架,想借此机会,通过自己的框架使用效果,检测自己对 Spring、JPA 等主流框架的源码的理解是否正确,而不是简单造轮子。
写了几天,有了点雏形,源码已经放到 GitHub 上了:
https://github.com/larger5/simpleSpring/tree/master/src/main/java/core/Dao
本文分享模拟 SringIOC、JPA 底层设计的一种简单的思路,有如下几个要点:
- 实现 IOC 容器
- 使用 dom4j 轻松读取 xml 中的配置信息
- 使用工厂模式构建 IOC 容器
- 使用反射实现从 IOC 容器中获取 bean
- 实现 JPA 操作数据
- 使用反射构成数据库关系映射
- 使用 IOC 容器分离数据源配置到 xml 文件中
- 使用 IOC 注册 Jpa bean
框架的实现使用了 lombok、dom4j,其实是可以使用原生代替代的,但是徒增逻辑
lombok
通过 @Data 等省略 setter、getter、toString 等。
dom4j
通过 xpath 语法清晰获取 xml 中信息。
二、代码
一切尽在代码的注释中
建议通过网页右边的目录来查看本文
1、注解
用来标识一些字段,其实也可以不用,因为 Hibernate 和 sun 公司已经制定了一套 Java Persistence API,位于 javax.persistence.*
,如:
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Table;
为了使框架的一切都在自己的掌控之中,笔者还是自己编写几个注解。
package core.Dao.tableAnnotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.TYPE}) // 类
@Retention(RetentionPolicy.RUNTIME) // 生命周期:源文件SOURCE、编译CLASS、运行RUNTIME
public @interface Table {
String value();
}
package core.Dao.tableAnnotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.FIELD}) // 作用域:属性
@Retention(RetentionPolicy.RUNTIME) // 生命周期:源文件SOURCE、编译CLASS、运行RUNTIME
public @interface Id {
}
package core.Dao.tableAnnotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.FIELD}) // 作用域:属性
@Retention(RetentionPolicy.RUNTIME) // 生命周期:源文件SOURCE、编译CLASS、运行RUNTIME
public @interface Column {
String value();
}
2、生成 sql 语句
如 queryByEntity
:
①先判断字段是否有,@Column
、@Id
、@Table
②若有,判断字段的值是否为空
③若非空,作为查询的条件
④拼接 sql
package core.Dao.BaseCrud;
import core.Dao.tableAnnotation.Column;
import core.Dao.tableAnnotation.Id;
import core.Dao.tableAnnotation.Table;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class BaseSql {
public String queryByEntity(Object entity) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
StringBuilder sql = new StringBuilder();
// 获取 class
Class c = entity.getClass();
// 该类是否为 @Table 旗下的
boolean annotationPresent = c.isAnnotationPresent(Table.class);
if (!annotationPresent) {
return null;
}
// 获取 @Table 中的信息
Table t = (Table) c.getAnnotation(Table.class);
String tableName = t.value();
// 1=1 防止下面的 sql 拼装 and 开头报错
sql.append("select * from ").append(tableName).append(" where 1=1");
// 遍历所有字段
Field[] fields = c.getDeclaredFields();
for (Field field : fields) {
// 该属性是否为 @Column 旗下的
boolean fExists = field.isAnnotationPresent(Column.class);
if (!fExists) {
continue;
}
// 获取 @Column 中的信息
Column column = field.getAnnotation(Column.class);
String columnName = column.value();
// 获取方法名
String fieldName = field.getName();
// getXxx 格式,即属性第一个字母大写
String getMethodName = "get" + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);
// 获取 getter
Method getMethod = c.getMethod(getMethodName); // sql 拼接错就报 NoSuchMethodException
// 执行 getter,fieldValue 可能为 Integer、String 类型,还是 Object 稳点
Object fieldValue = getMethod.invoke(entity);
// 去掉 @Column 但为 null 的属性
if (fieldValue == null) {
continue;
}
// 拼装 sql 注意 ' 不能省略,否则 MySQLSyntaxErrorException:Unknown column
sql.append(" and ").append(columnName).append("=").append("'").append(fieldValue).append("'");
}
return sql.toString();
}
public String countByEntity(Object entity) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
StringBuilder sql = new StringBuilder();
// 获取 class
Class c = entity.getClass();
// 该类是否为 @Table 旗下的
boolean annotationPresent = c.isAnnotationPresent(Table.class);
if (!annotationPresent) {
return null;
}
// 获取 @Table 中的信息
Table t = (Table) c.getAnnotation(Table.class);
String tableName = t.value();
// 1=1 防止下面的 sql 拼装 and 开头报错
sql.append("select count(*) from ").append(tableName).append(" where 1=1");
// 遍历所有字段
Field[] fields = c.getDeclaredFields();
for (Field field : fields) {
// 该属性是否为 @Column 旗下的
boolean fExists = field.isAnnotationPresent(Column.class);
if (!fExists) {
continue;
}
// 获取 @Column 中的信息
Column column = field.getAnnotation(Column.class);
String columnName = column.value();
// 获取方法名
String fieldName = field.getName();
// getXxx 格式,即属性第一个字母大写
String getMethodName = "get" + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);
// 获取 getter
Method getMethod = c.getMethod(getMethodName); // sql 拼接错就报 NoSuchMethodException
// 执行 getter,fieldValue 可能为 Integer、String 类型,还是 Object 稳点
Object fieldValue = getMethod.invoke(entity);
// 去掉 @Column 但为 null 的属性
if (fieldValue == null) {
continue;
}
// 拼装 sql 注意 ' 不能省略,否则 MySQLSyntaxErrorException:Unknown column
sql.append(" and ").append(columnName).append("=").append("'").append(fieldValue).append("'");
}
return sql.toString();
}
public String queryByEntityAndPage(Object entity, Integer page, Integer size) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
StringBuilder sql = new StringBuilder();
// 获取 class
Class c = entity.getClass();
// 该类是否为 @Table 旗下的
boolean annotationPresent = c.isAnnotationPresent(Table.class);
if (!annotationPresent) {
return null;
}
// 获取 @Table 中的信息
Table t = (Table) c.getAnnotation(Table.class);
String tableName = t.value();
// 1=1 防止下面的 sql 拼装 and 开头报错
sql.append("select * from ").append(tableName).append(" where 1=1");
// 遍历所有字段
Field[] fields = c.getDeclaredFields();
for (Field field : fields) {
// 该属性是否为 @Column 旗下的
boolean fExists = field.isAnnotationPresent(Column.class);
if (!fExists) {
continue;
}
// 获取 @Column 中的信息
Column column = field.getAnnotation(Column.class);
String columnName = column.value();
// 获取方法名
String fieldName = field.getName();
// getXxx 格式,即属性第一个字母大写
String getMethodName = "get" + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);
// 获取 getter
Method getMethod = c.getMethod(getMethodName); // sql 拼接错就报 NoSuchMethodException
// 执行 getter,fieldValue 可能为 Integer、String 类型,还是 Object 稳点
Object fieldValue = getMethod.invoke(entity);
// 去掉 @Column 但为 null 的属性
if (fieldValue == null) {
continue;
}
// 拼装 sql 注意 ' 不能省略,否则 MySQLSyntaxErrorException:Unknown column
sql.append(" and ").append(columnName).append("=").append("'").append(fieldValue).append("'");
}
// 分页
sql.append(" limit ").append((page-1)*size).append(",").append(size);
return sql.toString();
}
public String deleteByEntity(Object entity) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
StringBuilder sql = new StringBuilder();
// 获取 class
Class c = entity.getClass();
// 该类是否为 @Table 旗下的
boolean annotationPresent = c.isAnnotationPresent(Table.class);
if (!annotationPresent) {
return null;
}
// 获取 @Table 中的信息
Table t = (Table) c.getAnnotation(Table.class);
String tableName = t.value();
// 1=1 防止下面的 sql 拼装 and 开头报错
sql.append("delete from ").append(tableName).append(" where 1=1");
// 遍历所有字段
Field[] fields = c.getDeclaredFields();
for (Field field : fields) {
// 该属性是否为 @Column 旗下的
boolean fExists = field.isAnnotationPresent(Column.class);
if (!fExists) {
continue;
}
// 获取 @Column 中的信息
Column column = field.getAnnotation(Column.class);
String columnName = column.value();
// 获取方法名
String fieldName = field.getName();
// getXxx 格式,即属性第一个字母大写
String getMethodName = "get" + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);
// 获取 getter
Method getMethod = c.getMethod(getMethodName); // sql 拼接错就报 NoSuchMethodException
// 执行 getter,fieldValue 可能为 Integer、String 类型,还是 Object 稳点
Object fieldValue = getMethod.invoke(entity);
// 去掉 @Column 但为 null 的属性
if (fieldValue == null) {
continue;
}
// 拼装 sql 注意 ' 不能省略,否则 MySQLSyntaxErrorException:Unknown column
sql.append(" and ").append(columnName).append("=").append("'").append(fieldValue).append("'");
}
return sql.toString();
}
public String insertByEntity(Object entity) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
StringBuilder sql = new StringBuilder();
StringBuilder insertField = new StringBuilder();
StringBuilder insertValue = new StringBuilder();
// 获取 class
Class c = entity.getClass();
// 该类是否为 @Table 旗下的
boolean annotationPresent = c.isAnnotationPresent(Table.class);
if (!annotationPresent) {
return null;
}
// 获取 @Table 中的信息
Table t = (Table) c.getAnnotation(Table.class);
String tableName = t.value();
// 1=1 防止下面的 sql 拼装 and 开头报错
sql.append("insert into ").append(tableName);
// 遍历所有字段
Field[] fields = c.getDeclaredFields();
for (Field field : fields) {
// 该属性是否为 @Column 旗下的
boolean fExists = field.isAnnotationPresent(Column.class);
if (!fExists) {
continue;
}
// 获取 @Column 中的信息
Column column = field.getAnnotation(Column.class);
String columnName = column.value();
// 获取方法名
String fieldName = field.getName();
// getXxx 格式,即属性第一个字母大写
String getMethodName = "get" + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);
// 获取 getter
Method getMethod = c.getMethod(getMethodName); // sql 拼接错就报 NoSuchMethodException
// 执行 getter,fieldValue 可能为 Integer、String 类型,还是 Object 稳点
Object fieldValue = getMethod.invoke(entity);
// 去掉 @Column 但为 null 的属性
if (fieldValue == null) {
continue;
}
insertField.append(columnName).append(",");
insertValue.append("'").append(fieldValue).append("'").append(",");
}
sql.append("(")
.append(insertField.substring(0, insertField.length() - 1))
.append(")")
.append(" values(")
.append(insertValue.substring(0, insertValue.length() - 1))
.append(")");
return sql.toString();
}
public String updateByEntity(Object entity) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
StringBuilder sql = new StringBuilder();
StringBuilder condition = new StringBuilder();
// 获取 class
Class c = entity.getClass();
// 该类是否为 @Table 旗下的
boolean annotationPresent = c.isAnnotationPresent(Table.class);
if (!annotationPresent) {
return null;
}
// 获取 @Table 中的信息
Table t = (Table) c.getAnnotation(Table.class);
String tableName = t.value();
// 1=1 防止下面的 sql 拼装 and 开头报错
sql.append("update ").append(tableName);
// 遍历所有字段
Field[] fields = c.getDeclaredFields();
// sql 拼接中是否已经有 set 了
Boolean hasSetString = false;
for (Field field : fields) {
// 该属性是否为 @Column 旗下的
boolean fExists = field.isAnnotationPresent(Column.class);
if (!fExists) {
continue;
}
// 获取 @Column 中的信息
Column column = field.getAnnotation(Column.class);
String columnName = column.value();
// 获取方法名
String fieldName = field.getName();
// getXxx 格式,即属性第一个字母大写
String getMethodName = "get" + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);
// 获取 getter
Method getMethod = c.getMethod(getMethodName); // sql 拼接错就报 NoSuchMethodException
// 执行 getter,fieldValue 可能为 Integer、String 类型,还是 Object 稳点
Object fieldValue = getMethod.invoke(entity);
// 去掉 @Column 但为 null 的属性
if (fieldValue == null) {
continue;
}
// id为条件(缺陷:目前只能有一个主键)
if (field.isAnnotationPresent(Id.class)) {
condition.append(" where ").append(columnName).append("=").append("'").append(fieldValue).append("'");
continue;
}
// sql 中只出现一个 set
if (!hasSetString) {
sql.append(" set ");
hasSetString = true;
}
sql.append(columnName).append("=").append("'").append(fieldValue).append("'").append(",");
}
return sql.substring(0, sql.length() - 1) + condition;
}
// 想去除记录中的部分字段时调用
public String updateByEntityWithAllColumn(Object entity) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
StringBuilder sql = new StringBuilder();
StringBuilder condition = new StringBuilder();
// 获取 class
Class c = entity.getClass();
// 该类是否为 @Table 旗下的
boolean annotationPresent = c.isAnnotationPresent(Table.class);
if (!annotationPresent) {
return null;
}
// 获取 @Table 中的信息
Table t = (Table) c.getAnnotation(Table.class);
String tableName = t.value();
// 1=1 防止下面的 sql 拼装 and 开头报错
sql.append("update ").append(tableName);
// 遍历所有字段
Field[] fields = c.getDeclaredFields();
// sql 拼接中是否已经有 set 了
Boolean hasSetString = false;
for (Field field : fields) {
// 该属性是否为 @Column 旗下的
boolean fExists = field.isAnnotationPresent(Column.class);
if (!fExists) {
continue;
}
// 获取 @Column 中的信息
Column column = field.getAnnotation(Column.class);
String columnName = column.value();
// 获取方法名
String fieldName = field.getName();
// getXxx 格式,即属性第一个字母大写
String getMethodName = "get" + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);
// 获取 getter
Method getMethod = c.getMethod(getMethodName); // sql 拼接错就报 NoSuchMethodException
// 执行 getter,fieldValue 可能为 Integer、String 类型,还是 Object 稳点
Object fieldValue = getMethod.invoke(entity);
// id为条件(缺陷:目前只能有一个主键)
if (field.isAnnotationPresent(Id.class)) {
condition.append(" where ").append(columnName).append("=").append("'").append(fieldValue).append("'");
continue;
}
// sql 中只出现一个 set
if (!hasSetString) {
sql.append(" set ");
hasSetString = true;
}
// 允许置空
if (fieldValue == null) {
sql.append(columnName).append("=").append(fieldValue).append(",");//单引号的作用就在此体现了,其起到屏蔽关键字的作用
continue;
}
sql.append(columnName).append("=").append("'").append(fieldValue).append("'").append(",");
}
return sql.substring(0, sql.length() - 1) + condition;
}
}
3、执行 sql 语句
下面就简单了,都是 JDBC
,sql
已经明确了
package core.Dao.BaseCrud;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.lang.reflect.InvocationTargetException;
import java.sql.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* 优点:全是原生代码,出错时一切尽在掌握之内
* 缺点:
* ① 即使是 VO 对象也要 @Column、@Table、@Id,显然是不符合现实的,特别是 @Table 是不存在的
* ② 只能单表 CRUD
*/
@Data // getter、setter、toString
@NoArgsConstructor // 无参构造函数
public class BaseJdbc {
// 数据库驱动
private String driver;
// 数据库 url
private String url;
// 数据库库 用户名
private String user;
// 数据库 密码
private String password;
// 拼接 sql
private BaseSql baseSql=new BaseSql();
public List<Map<String, Object>> queryByEntity(Object entity) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException, SQLException, ClassNotFoundException {
String sql = baseSql.queryByEntity(entity);
System.out.println(sql);
Connection conn = getConnection();
PreparedStatement ps = conn.prepareStatement(sql);
ResultSet rs = ps.executeQuery();
ResultSetMetaData rsmd = ps.getMetaData();
// 取得结果集列数
int columnCount = rsmd.getColumnCount();
// 构造泛型结果集
List<Map<String, Object>> datas = new ArrayList<Map<String, Object>>();
Map<String, Object> data = null;
// 循环结果集
while (rs.next()) {
data = new HashMap<String, Object>();
// 每循环一条将列名和列值存入Map
for (int i = 1; i <= columnCount; i++) {
data.put(rsmd.getColumnLabel(i), rs.getObject(rsmd
.getColumnLabel(i)));
}
// 将整条数据的Map存入到List中
datas.add(data);
}
close(conn);
return datas;
}
public List<Map<String, Object>> queryByEntityAndPage(Object entity,Integer page,Integer size) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException, SQLException, ClassNotFoundException {
String sql = baseSql.queryByEntityAndPage(entity,page,size);
System.out.println(sql);
Connection conn = getConnection();
PreparedStatement ps = conn.prepareStatement(sql);
ResultSet rs = ps.executeQuery();
ResultSetMetaData rsmd = ps.getMetaData();
// 取得结果集列数
int columnCount = rsmd.getColumnCount();
// 构造泛型结果集
List<Map<String, Object>> datas = new ArrayList<Map<String, Object>>();
Map<String, Object> data = null;
// 循环结果集
while (rs.next()) {
data = new HashMap<String, Object>();
// 每循环一条将列名和列值存入Map
for (int i = 1; i <= columnCount; i++) {
data.put(rsmd.getColumnLabel(i), rs.getObject(rsmd
.getColumnLabel(i)));
}
// 将整条数据的Map存入到List中
datas.add(data);
}
close(conn);
return datas;
}
public Integer countByEntity(Object entity) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException, SQLException, ClassNotFoundException {
String sql = baseSql.countByEntity(entity);
System.out.println(sql);
Connection conn = getConnection();
PreparedStatement ps = conn.prepareStatement(sql);
ResultSet rs = ps.executeQuery();
Integer count=0;
if(rs.next()) {
count=rs.getInt(1);
}
close(conn);
return count;
}
public Boolean deleteByEntity(Object entity) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, SQLException, ClassNotFoundException {
String sql = baseSql.deleteByEntity(entity);
System.out.println(sql);
Connection conn = getConnection();
PreparedStatement ps = conn.prepareStatement(sql);
int rs = ps.executeUpdate();
close(conn);
if (rs < 0) {
return false;
}
return true;
}
public Boolean insertByEntity(Object entity) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, SQLException, ClassNotFoundException {
String sql = baseSql.insertByEntity(entity);
System.out.println(sql);
Connection conn = getConnection();
PreparedStatement ps = conn.prepareStatement(sql);
int columnCount = ps.executeUpdate();
if (columnCount < 1) {
return false;
}
return true;
}
public Boolean updateByEntity(Object entity) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, SQLException, ClassNotFoundException {
String sql = baseSql.updateByEntity(entity);
System.out.println(sql);
Connection conn = getConnection();
PreparedStatement ps = conn.prepareStatement(sql);
int columnCount = ps.executeUpdate();
close(conn);
if (columnCount < 1) {
return false;
}
return true;
}
public Boolean updateByEntityWithAllColumn(Object entity) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, SQLException, ClassNotFoundException {
String sql = baseSql.updateByEntityWithAllColumn(entity);
System.out.println(sql);
Connection conn = getConnection();
PreparedStatement ps = conn.prepareStatement(sql);
int columnCount = ps.executeUpdate();
close(conn);
if (columnCount < 1) {
return false;
}
return true;
}
/**
* 连接
*
* @return
* @throws ClassNotFoundException
* @throws SQLException
*/
public Connection getConnection() throws ClassNotFoundException, SQLException {
Class.forName(driver);
Connection conn = DriverManager.getConnection(url, user, password);
return conn;
}
/**
* 断开
*
* @param conn
* @throws SQLException
*/
public void close(Connection conn) throws SQLException {
if (conn!=null){
conn.close();
}
}
}
4、构建工厂
关键:① id
(标识)、② class
(反射出实例)
package core.factory;
import core.Dao.BaseCrud.BaseJdbc;
import core.Dao.ComplexCrud.ComplexJdbc;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
@Data // getter、setter、toString
@AllArgsConstructor // 有参构造函数
@NoArgsConstructor // 无参构造函数
public class XmlBeanFactory {
private String xmlPath;
public Object getBean(String bean) throws Exception {
SAXReader saxReader = new SAXReader();
// 在项目中相对的是以项目名为根路径
Document document = saxReader.read(xmlPath);
Element firstBean = (Element) document.selectSingleNode("//bean[@id='"+bean+"']");
// 获取类路径
String className = (String) firstBean.attribute("class").getData();
// 反射得类实例
return Class.forName(className).newInstance();
}
public BaseJdbc getBaseJdbc() {
SAXReader saxReader = new SAXReader();
// 在项目中相对的是以项目名为根路径
Document document = null;
try {
document = saxReader.read(xmlPath);
} catch (DocumentException e) {
e.printStackTrace();
}
Element driver = (Element) document.selectSingleNode("//jdbc/property[@name='driver']");
Element url = (Element) document.selectSingleNode("//jdbc/property[@name='url']");
Element username = (Element) document.selectSingleNode("//jdbc/property[@name='username']");
Element password = (Element) document.selectSingleNode("//jdbc/property[@name='password']");
String driverValue = (String) driver.attribute("value").getData();
String urlValue = (String) url.attribute("value").getData();
String usernameValue = (String) username.attribute("value").getData();
String passwordValue = (String) password.attribute("value").getData();
BaseJdbc baseJdbc = new BaseJdbc();
baseJdbc.setDriver(driverValue);
baseJdbc.setUrl(urlValue);
baseJdbc.setUser(usernameValue);
baseJdbc.setPassword(passwordValue);
return baseJdbc;
}
public ComplexJdbc getComplexJdbc() {
SAXReader saxReader = new SAXReader();
// 在项目中相对的是以项目名为根路径
Document document = null;
try {
document = saxReader.read(xmlPath);
} catch (DocumentException e) {
e.printStackTrace();
}
Element driver = (Element) document.selectSingleNode("//jdbc/property[@name='driver']");
Element url = (Element) document.selectSingleNode("//jdbc/property[@name='url']");
Element username = (Element) document.selectSingleNode("//jdbc/property[@name='username']");
Element password = (Element) document.selectSingleNode("//jdbc/property[@name='password']");
String driverValue = (String) driver.attribute("value").getData();
String urlValue = (String) url.attribute("value").getData();
String usernameValue = (String) username.attribute("value").getData();
String passwordValue = (String) password.attribute("value").getData();
ComplexJdbc complexJdbc = new ComplexJdbc();
complexJdbc.setDriver(driverValue);
complexJdbc.setUrl(urlValue);
complexJdbc.setUser(usernameValue);
complexJdbc.setPassword(passwordValue);
return complexJdbc;
}
}
5、相关配置
能做到把配置信息从 Java 代码中分离出来,是这几天的一个进展,后面会设法用 Java Bean 注解的方式取代 xml 配置
必备点:① id
(标识)、② class
(备反射)
<?xml version="1.0" encoding="UTF-8"?>
<beans>
<bean id="student" class="framekworkTest.iocTest.bean.impl.Student" />
<bean id="teacher" class="framekworkTest.iocTest.bean.impl.Teacher" />
<jdbc>
<property name="driver" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://120.79.197.133:3307/testspring?characterEncoding=utf-8" />
<property name="username" value="root"/>
<property name="password" value="123"/>
</jdbc>
</beans>
5、测试效果
①实体
实体对应数据库中的表
package framekworkTest.daoTest.entity;
import core.Dao.tableAnnotation.Column;
import core.Dao.tableAnnotation.Id;
import core.Dao.tableAnnotation.Table;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data // getter、setter
@AllArgsConstructor // 有参构造函数
@NoArgsConstructor // 无参构造函数
@Table("t_user")
public class User {
@Id
@Column("id")
private Integer id;
@Column("user_name") // sql中属性名
private String userName; // Java中属性名
@Column("password")
private String password;
@Column("department_id")
private Integer departmentId;
}
②测试
package framekworkTest.daoTest;
import core.Dao.BaseCrud.BaseJdbc;
import core.factory.XmlBeanFactory;
import framekworkTest.daoTest.entity.User;
import org.junit.jupiter.api.Test;
import java.lang.reflect.InvocationTargetException;
import java.sql.SQLException;
import java.util.List;
import java.util.Map;
public class BaseJdbcTest {
private BaseJdbc baseJdbc = new XmlBeanFactory("src/main/resources/spring.xml").getBaseJdbc();
/**
* 1.1、查
* 实体中有多少个属性,就有多少个查询的条件
* select * from t_user w
* 返回类型:listhere 1=1 and password='123'
*/
@Test
public void queryByEntity() throws SQLException, NoSuchMethodException, ClassNotFoundException, IllegalAccessException, InvocationTargetException {
User user1 = new User(null, "larger5", "123",null);
List<Map<String, Object>> maps = baseJdbc.queryByEntity(user1);
System.out.println(maps);
}
/**
* 1.2、查 + 分页
* 实体中有多少个属性,就有多少个查询的条件
* select * from t_user where 1=1 and password='123' limit 0,2
* 返回类型:list
*/
@Test
public void queryByEntityAndPage() throws SQLException, NoSuchMethodException, ClassNotFoundException, IllegalAccessException, InvocationTargetException {
User user1 = new User(null, null, "123",null);
List<Map<String, Object>> maps = baseJdbc.queryByEntityAndPage(user1, 1, 2);
System.out.println(maps);
}
/**
* 1.3、查数量
* 配合分页使用,返回给前端
* 实体中有多少个属性,就有多少个查询的条件
* select count(*) from t_user where 1=1 and user_name='larger5' and password='123'
* 返回类型:list
*/
@Test
public void countByEntity() throws SQLException, NoSuchMethodException, ClassNotFoundException, IllegalAccessException, InvocationTargetException {
User user1 = new User(null, "larger5", "123",null);
Integer count = baseJdbc.countByEntity(user1);
System.out.println(count);
}
/**
* 2、删
* 实体中有多少个属性,就有多少个删除的条件
* delete from t_user where 1=1 and id='6'
* 返回类型:成功 true、失败 false
*/
@Test
public void deleteByEntity() throws InvocationTargetException, NoSuchMethodException, ClassNotFoundException, SQLException, IllegalAccessException {
User user2 = new User(6, null, null,null);
Boolean aBoolean2 = baseJdbc.deleteByEntity(user2);
System.out.println(aBoolean2);
}
/**
* 3、增
* 允许实体中有任意个null属性
* 允许自定义id,但是别和原有的记录冲突
* insert into t_user(user_name,password) values('55','998')
* 返回类型:成功 true、失败 false
*/
@Test
public void insertByEntity() throws InvocationTargetException, NoSuchMethodException, ClassNotFoundException, SQLException, IllegalAccessException {
User user3 = new User(null, "55", "998",null);
Boolean aBoolean3 = baseJdbc.insertByEntity(user3);
System.out.println(aBoolean3);
}
/**
* 4.1、改
* 以第一个有 @Id 的为条件,其他的为改,实体中属性值为null的不改
* update t_user set user_name='larger',password='5555' where id='13'
* 返回类型:成功 true、失败 false
*/
@Test
public void updateByEntity() throws InvocationTargetException, NoSuchMethodException, ClassNotFoundException, SQLException, IllegalAccessException {
User user4 = new User(13, "larger", "5555",null);
Boolean aBoolean4 = baseJdbc.updateByEntity(user4);
System.out.println(aBoolean4);
}
/**
* 4.2、改
* 用于置空记录中的一些字段
* update t_user set user_name=null,password='123' where id='12'
*/
@Test
public void updateByEntityWithAllColumnTest() throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, SQLException, ClassNotFoundException {
User user5 = new User(12, null, "123",null);
Boolean aBoolean = baseJdbc.updateByEntityWithAllColumn(user5);
System.out.println(aBoolean);
}
}
三、小结
自从年初开始阅读 JavaEE
主流框架的阅读源码,有了一些经验:
如果直接买本书如《Spring 源码深度解析》,从头到尾的读下去,可能最后还在云里雾里
这里提供阅读源码的一种思路,就是先自己手动实现,尽量思考,做出一定效果出来,这时会有一定的成就感,
同时,最终可能会遇到瓶颈,或比较费时费力的地方,这时再去参考其他资料,源码,才会恍然大悟。
而那些从头到尾看源码并且看懂的,没有自己敲代码实现框架部分功能的,恐怕是自欺欺人吧