mybatis
1. 自定义持久层框架
1.1 JDBC
public static void main(String[] args) {
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
// 加载数据库驱动
Class.forName("com.mysql.jdbc.Driver");
// 通过驱动管理类获取数据库链接
conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/mybatis? characterEncoding=utf-8", "root", "root");
String sql = "select * from user where username = ?";
ps = conn.prepareStatement(sql);
ps.setString(1, "tom");
rs = ps.executeQuery();
while (rs.next()) {
int id = rs.getInt("id");
String username = rs.getString("username");
user.setId(id);
user.setUsername(username);
}
System.out.println(user);
} catch (Exception e) {
e.printStackTrace();
} finally { // 释放资源
if (rs != null) {
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (ps != null) {
try {
ps.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
//dom4j解析xml
Document document = new SAXReader().read(inputStream);
Element rootElement = document.getRootElement();
//获取所有property标签
List<Element> list = rootElement.selectNodes("//property");
Properties properties = new Properties();
for (Element element : list) {
String name = element.attributeValue("name");
String value = element.attributeValue("value");
properties.setProperty(name,value);
}
JDBC问题总结:
原始jdbc开发存在的问题如下:
1、 数据库连接创建、释放频繁造成系统资源浪费,从而影响系统性能。
2、 Sql语句在代码中硬编码,造成代码不易维护,实际应用中sql变化的可能较大,sql变动需要改变 java代码。
3、 使用preparedStatement向占有位符号传参数存在硬编码,因为sql语句的where条件不一定,可能 多也可能少,修改sql还要修改代码,系统不易维护。
4、 对结果集解析存在硬编码(查询列名),sql变化导致解析代码变化,系统不易维护,如果能将数据 库 记录封装成pojo对象解析比较方便
1.2 连接池
ComboPooledDataSource comboPooledDataSource = new ComboPooledDataSource();
try {
comboPooledDataSource.setDriverClass("com.mysql.jdbc.Driver");
comboPooledDataSource.setJdbcUrl("jdbc:mysql://localhost:3307/test");
comboPooledDataSource.setUser("root");
comboPooledDataSource.setPassword("root");
Connection connection = comboPooledDataSource.getConnection();
PreparedStatement preparedStatement = connection.prepareStatement("select * from a");
ResultSet resultSet = preparedStatement.executeQuery();
while (resultSet.next()) {
System.out.println(resultSet.getInt("id"));
}
} catch (Exception e) {
e.printStackTrace();
}
1.2.1 连接池封装版
ComboPooledDataSource comboPooledDataSource = new ComboPooledDataSource();
//@Value后续
private String className = "com.mysql.jdbc.Driver";
private String url = "jdbc:mysql://localhost:3307/test";
private String userName = "root";
private String password = "root";
private void initPool() throws PropertyVetoException {
if(comboPooledDataSource.getUser() == null) {
comboPooledDataSource.setDriverClass(className);
comboPooledDataSource.setJdbcUrl(url);
comboPooledDataSource.setUser(userName);
comboPooledDataSource.setPassword(password);
}
}
PreparedStatement getPrepareStatement(String sql) throws SQLException, PropertyVetoException {
initPool();
return comboPooledDataSource.getConnection().prepareStatement(sql);
}
public void excute(String sql ,Object ... params) {
try {
PreparedStatement preparedStatement = getPrepareStatement(sql);
for (int i = 0; i < params.length; i++) {
preparedStatement.setObject(i+1,params[i]);
}
ResultSet resultSet = preparedStatement.executeQuery();
while (resultSet.next()) {
System.out.println(resultSet.getInt("id"));
}
} catch (Exception e) {
e.printStackTrace();
}
}
1.2.2 返回值使用 反射或者内省 封装类
ResultSet resultSet = preparedStatement.executeQuery();
String resultType = mappedStatement.getResultType();
Class<?> resultTypeClass = getClassType(resultType);
ArrayList<Object> objects = new ArrayList<>();
// 6. 封装返回结果集
while (resultSet.next()){
Object o =resultTypeClass.newInstance();
//元数据
ResultSetMetaData metaData = resultSet.getMetaData();
for (int i = 1; i <= metaData.getColumnCount(); i++) {
// 字段名
String columnName = metaData.getColumnName(i);
// 字段的值
Object value = resultSet.getObject(columnName);
//使用反射或者内省,根据数据库表和实体的对应关系,完成封装
PropertyDescriptor propertyDescriptor = new PropertyDescriptor(columnName, resultTypeClass);
Method writeMethod = propertyDescriptor.getWriteMethod();
writeMethod.invoke(o,value);
}
objects.add(o);
}
return (List<E>) objects;
1.3 反射获取类变量值
try {
Class<?> aClass = Class.forName("类的全路径");
//反射
Field declaredField = aClass.getDeclaredField("变量名称");
//暴力访问
declaredField.setAccessible(true);
Object o = declaredField.get("对应类的对象");
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
1.4 自定义框架设计
使用端:
提供核心配置文件:
sqlMapConfig.xml : 存放数据源信息,引入mapper.xml Mapper.xml : sql语句的配置文件信息
框架端:
1.读取配置文件
读取完成以后以流的形式存在,我们不能将读取到的配置信息以流的形式存放在内存中,不好操作,可 以创建javaBean来存储
(1)Configuration : 存放数据库基本信息、Map<唯一标识,Mapper> 唯一标识:namespace + “.” +
id
(2)MappedStatement:sql语句、statement类型、输入参数java类型、输出参数java类型
2.解析配置文件
创建sqlSessionFactoryBuilder类:
方法:sqlSessionFactory build():
第一:使用dom4j解析配置文件,将解析出来的内容封装到Configuration和MappedStatement中 第二:创建SqlSessionFactory的实现类DefaultSqlSession
3.创建SqlSessionFactory:
方法:openSession() : 获取sqlSession接口的实现类实例对象
4.创建sqlSession接口及实现类:主要封装crud方法
方法:selectList(String statementId,Object param):查询所有 selectOne(String statementId,Object param):查询单个
具体实现:封装JDBC完成对数据库表的查询操作 涉及到的设计模式:
Builder构建者设计模式、工厂模式、代理模式
2. Mybatis基础回顾和高级应用
2.1 Mybatis相关概念
2.1.1 ORM : Object/Relation Mapping
对象-关系映射,其实就是实体类和数据库表产生映射关系,从而达到操作实体类等用于操作数据库
2.1.2 Mybatis 简介
Mybatis是一款基于ORM的半自动轻量级持久层框架
2.2 Mybatis常用配置
2.2 Mybatis复杂映射
2.2.1 一对一
2.2.1.1 一对一(注解)
概述:用户和订单,一个订单只对应一个用户,查询订单并查询关联的用户,就是一对一
2.2.2 一对多
2.2.2.1 一对多(注解)
概述:用户和订单,一个用户对应多个订单,查询用户并查询关联的订单,就是一对多
2.2.3 多对多
2.2.3.1 多对多(注解)
2.3 Mybatis缓存
缓存就是内存中的数据,常常来自对数据库查询结果的保存,使用缓存,我们可以避免频繁的与数据库 进行交互,进而提高响应速度
2.3.1 一级缓存
一级缓存到底是什么?
一级缓存底层实现是hashmap
一级缓存什么时候被创建、一级缓存的工作流程是怎样的?
一级缓存在执行查询语句前被创建,在语句执行前进行get,有缓存就使用缓存,没有缓存就查询数据库,然后将数据放进缓存中,缓存会在增删改的操作中进行重置,避免脏读
2.3.2 二级缓存
原理和一级缓存一样,区别于一级缓存作用于sqlsession,而二级缓存作用于mapper文件的namespace,也就是说多个sqlsession可以共享一个mapper中的二级缓存区域
注解版:
2.3.2.1 二级缓存整合redis
上面介绍了 mybatis自带的二级缓存,但是这个缓存是单服务器工作,无法实现分布式缓存。 那么 什么是分布式缓存呢?假设现在有两个服务器1和2,用户访问的时候访问了 1服务器,查询后的缓 存就会 放在1服务器上,假设现在有个用户访问的是2服务器,那么他在2服务器上就无法获取刚刚那个 缓存
2.4 Mybatis插件
2.4.1 pageHelper分页插件Mybatis插件
2.4.2 通用mapper
通用Mapper就是为了解决单表增删改查,基于Mybatis的插件机制。开发人员不需要编写SQL,不需要 在DAO中增加方法,只要写好实体类,就能支持相应的增删改查方法
3 设计模式
3.1 Builder构建者模式
Builder模式定义
"将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表 示。”
它属于创建类模式,一般来说,如果一个对象的构建比较复杂,超出了构造函数所能包含的范 围,就可以使用工厂模式和Builder模式,相对于工厂模式会产出一个完整的产品,Builder应用于更加 复杂的对象的构建,甚至只会构建产品的一个部分
直白来说,就是使用多个简单的对象一步一步构建 成一个复杂的对象
3.2 工厂模式
在Mybatis中比如SqlSessionFactory使用的是工厂模式,该工厂没有那么复杂的逻辑,是一个简单工厂 模式。
简单工厂模式(Simple Factory Pattern):又称为静态工厂方法(Static Factory Method)模式,它属于创 建型模式。
在简单工厂模式中,可以根据参数的不同返回不同类的实例。简单工厂模式专门定义一个类来负责创建 其他类的实例,被创建的实例通常都具有共同的父类
例子:生产电脑
假设有一个电脑的代工生产商,它目前已经可以代工生产联想电脑了,随着业务的拓展,这个代工生产 商还要生产惠普的电脑,我们就需要用一个单独的类来专门生产电脑,这就用到了简单工厂模式。
3.3 代理模式
代理模式(Proxy Pattern):给某一个对象提供一个代理,并由代理对象控制对原对象的引用。 它是一种对象结构型模式,代理模式分为静态代理和动态代理
简单的说就是,我们在访问实际对象时,是通过代理对象来访问的,代理模式就是在访问实际对象时引入一定程度的间接性,因为这种间接性,可以附加多种用途。
静态代理:由程序员创建或特定工具自动生成源代码,也就是在编译时就已经将接口、被代理类、代理类等确定下来。在程序运行之前,代理类的.class文件就已经生成。