剖析 JDBC 操作问题
问题总结
- 数据库连贯创立、开释频繁造成系统资源节约,从而影响零碎性能。
- Sql 语句在代码中硬编码,造成代码不易保护,理论利用中 sql 变动的可能较大,sql 变动须要扭转 java 代码。
- 应用 preparedStatement 向占有位符号传参数存在硬编码,因为 sql 语句的 where 条件不肯定,可能 多也可能少,批改 sql 还要批改代码,零碎不易保护。
- 对后果集解析存在硬编码 (查问列名),sql 变动导致解析代码变动,零碎不易保护,如果能将数据 库 记录封装成 pojo 对象解析比拟不便
解决方案
- 应用数据库连接池初始化连贯资源
- 将数据库连贯配置、sql 语句抽取到 xml 配置文件中,使其满足开闭准则
- 应用反射、内省等底层技术,主动将实体与表进行属性与字段的主动映射
自定义框架设计思路
架构设计图
设计思路
应用端(提供配置)
- 应用 sqlMapperConfig.xml 提供全局配置信息(数据源等)
- 应用 Mapper.xml 提供 sql 语句文件信息
- 提供 SqlSession 接口类实现 select、insert、update、delete 操作
框架端(JDBC 封装)
-
读取配置文件
- 读取 sqlMapperconfig.xml
- 读取 Mapper.xml
-
解析配置文件
- 解析文件生成 Configuration、MapperStatement 对象
- 执行 JDBC 流程
自定义框架实现
应用端
创立配置文件
sqlMapperConfig.xml(全局配置文件,包含数据源配置,mapper 门路等)
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<!-- 数据源配置 -->
<datasource>
<property name="driverClass" value="com.mysql.cj.jdbc.Driver"/>
<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/test"/>
<property name="username" value="root"/>
<property name="password" value=""/>
</datasource>
<!-- 引入 mapper.xml 文件门路 -->
<mapper resource="mapper/userMapper.xml"/>
</configuration>
mapper.xml(namespace 命名空间、SQL 语句配置等)
<?xml version="1.0" encoding="utf-8" ?>
<mapper namespace="com.gigabytc.dao.UserDao">
<select id="findAll" resultType="com.gigabytc.entity.User" >
select * from user
</select>
<select id="findByCondition" resultType="com.gigabytc.entity.User" parameterType="com.gigabytc.entity.User">
select * from user where id = #{id} and username = #{username}
</select>
<insert id="addUser" parameterType="com.gigabytc.entity.User" resultType="int">
insert into user values(#{id}, #{username}, #{password}, #{birthday})
</insert>
<update id="updateUser" parameterType="com.gigabytc.entity.User" resultType="int" >
update user SET username = #{username}, password=#{password}, birthday=#{birthday} WHERE id = #{id}
</update>
<delete id="deleteUser" parameterType="com.gigabytc.entity.User" resultType="int" >
delete from user where id=#{id}
</delete>
</mapper>
应用 sqlSession 操作接口(读取配置文件构建 SqlSession 数据库操作)
InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapperConfig.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
sqlSession = sqlSessionFactory.openSession();
List<User> list = sqlSession.selectList("user.findAll");
框架端
解析配置文件
Configuration(全局配置)
@Data
public class Configuration {
/**
- 数据源
*/
private DataSource dataSource;
/**
- SQL statement 汇合
*/
private Map<String, MapperStatement> mapperStatementMap = new HashMap<>();}
MapperStatement(SQL 语句配置)
@Data
public class MapperStatement {
/**
statement-id 标识(namespace+id)*/
private String id;
/**
- 参数类型
*/
private String parameterType;
/**
- 返回类型
*/
private String resultType;
/**
- sql 语句
*/
private String sql;
/**
- sql 类型
*/
private SqlType sqlType;
}
XmlConfigBuilder(全局配置解析)
public class XMLConfigBuilder {public Configuration parseConfiguration(InputStream inputStream) throws DocumentException, PropertyVetoException {Configuration configuration = new Configuration();
// 解析数据源配置
Document document = new SAXReader().read(inputStream);
Element rootElement = document.getRootElement();
List<Element> list = rootElement.selectNodes("//datasource/property");
Properties properties = new Properties();
for (Element element : list) {String name = element.attributeValue("name");
String value = element.attributeValue("value");
properties.setProperty(name,value);
}
// 构建连接池
ComboPooledDataSource comboPooledDataSource = new ComboPooledDataSource();
comboPooledDataSource.setDriverClass(properties.getProperty("driverClass"));
comboPooledDataSource.setJdbcUrl(properties.getProperty("jdbcUrl"));
comboPooledDataSource.setUser(properties.getProperty("username"));
comboPooledDataSource.setPassword(properties.getProperty("password"));
configuration.setDataSource(comboPooledDataSource);
// 读取 mapper.xml 文件门路,解析 mapper.xml
List<Element> mapperList = rootElement.selectNodes("//mapper");
for (Element element : mapperList) {String resource = element.attributeValue("resource");
XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(configuration);
xmlMapperBuilder.parseMapper(Resources.getResourceAsStream(resource));
}
return configuration;
}
}
XmlMapperStatementBuilder(SQL 语句解析)
public class XMLMapperBuilder {
private Configuration configuration;
public XMLMapperBuilder(Configuration configuration) {this.configuration = configuration;}
public void parseMapper(InputStream inputStream) throws DocumentException {
// 解析 SQL statement 并封装到汇合
Document document = new SAXReader().read(inputStream);
Element rootElement = document.getRootElement();
// 解析 sql 语句封装 MapperStatement
buildStatement(rootElement, new String[]{"select", "update", "insert", "delete"});
}
private void parseStatement(String namespace, Element element) {String id = element.attributeValue("id");
String parameterType = element.attributeValue("parameterType");
String resultType = element.attributeValue("resultType");
String sql = element.getTextTrim();
String elementName = element.getName();
SqlType sqlType = SqlType.valueOf(elementName.toUpperCase());
MapperStatement mapperStatement = new MapperStatement();
mapperStatement.setId(id);
mapperStatement.setParameterType(parameterType);
mapperStatement.setResultType(resultType);
mapperStatement.setSql(sql);
mapperStatement.setSqlType(sqlType);
String statementId = namespace + "." + id;
configuration.getMapperStatementMap().put(statementId, mapperStatement);
}
private void buildStatement(Element rootElement, String[] nodes) {String namespace = rootElement.attributeValue("namespace");
for (String node : nodes) {List<Element> list = rootElement.selectNodes("//" + node);
for (Element element : list) {parseStatement(namespace, element);
}
}
}
}
SqlSession 相干接口实现
SqlSessionFactoryBuilder
public class SqlSessionFactoryBuilder {public SqlSessionFactory build(InputStream inputStream) throws PropertyVetoException, DocumentException {XMLConfigBuilder xmlConfigBuilder = new XMLConfigBuilder();
Configuration configuration = xmlConfigBuilder.parseConfiguration(inputStream);
return new DefaultSqlSessionFactory(configuration);
}
}
DefaultSqlSessionFactory
public class DefaultSqlSessionFactory implements SqlSessionFactory{
private final Configuration configuration;
public DefaultSqlSessionFactory(Configuration configuration) {this.configuration = configuration;}
@Override
public SqlSession openSession() {return new DefaultSqlSession(configuration);
}
}
DefaultSqlSession
public class DefaultSqlSession implements SqlSession{public DefaultSqlSession(Configuration configuration) {this.configuration = configuration;}
private Configuration configuration;
@Override
public <E> List<E> selectList(String statementId, Object... params) throws Exception {Executor selectExecutor = new SelectExecutor();
MapperStatement mapperStatement = configuration.getMapperStatementMap().get(statementId);
return selectExecutor.query(configuration, mapperStatement, params);
}
@Override
public <T> T selectOne(String statementId, Object... params) throws Exception {List<Object> objects = selectList(statementId, params);
if (objects.size() == 1) {return (T) objects.get(0);
}else {throw new RuntimeException("查问后果过多或不存在");
}
}
@Override
public int insert(String statementId, Object... params) throws Exception {return this.update(statementId,params);
}
@Override
public int update(String statementId, Object... params) throws Exception {Executor updateExecutor = new UpdateExecutor();
MapperStatement mapperStatement = configuration.getMapperStatementMap().get(statementId);
return updateExecutor.update(configuration,mapperStatement,params);
}
@Override
public int delete(String statementId, Object... params) throws Exception {return this.update(statementId,params);
}
@Override
public <T> T getMapper(Class<?> c) {
Object o = Proxy
.newProxyInstance(DefaultSqlSession.class.getClassLoader(), new Class[]{c}, new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {Class<?> declaringClass = method.getDeclaringClass();
String statementId = declaringClass.getName() + "." + method.getName();
MapperStatement mapperStatement = configuration.getMapperStatementMap().get(statementId);
SqlType sqlType = mapperStatement.getSqlType();
switch (sqlType) {
case SELECT:
Type genericReturnType = method.getGenericReturnType();
// 判断是否进行了 泛型类型参数化
if(genericReturnType instanceof ParameterizedType){return selectList(statementId, args);
}
return selectOne(statementId,args);
case INSERT:
return insert(statementId,args);
case UPDATE:
return update(statementId,args);
case DELETE:
return delete(statementId,args);
default :
break;
}
return null;
}
});
return (T) o;
}
}
参考代码
lov3r/lagou_mybatis
本文由博客一文多发平台 OpenWrite 公布!