# JDBC

需要手动引入 Mysql 的 jar 包

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class JDBCDemo {  
public static void main(String[] args) throws ClassNotFoundException, SQLException {
// 加载驱动
Class.forName("com.mysql.jdbc.Driver");
// 建立连接
Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/test", "root", "12345678");
// 定义SQL语句
String sql = "select * from db_account where id = 4";
// 获取预处理prepareStatement
PreparedStatement preparedStatement = connection.prepareStatement(sql);
// 设置参数
// preparedStatement.setInt(1, 4);
// 执行查询 得到结果
ResultSet resultSet = preparedStatement.executeQuery(sql);
// 处理结果
while (resultSet.next()) {
System.out.println("id:" + resultSet.getInt("id"));
System.out.println("email:" + resultSet.getString("email"));
}
}
}

  1. 为什么要有 ORM 框架
  • 驱动 uri、数据库地址、账号密码,硬编码,不灵活
  • 重复的建立连接
  • 处理结果集麻烦

# 自定义

# 创建两个工程

  • IPersistence、IPersistence_Test

# IPersistence_Test 使用端

# IPersistence 自定义框架

# 根据配置文件的路径,将配置文件加载成字节输入流,存储在内存中

1
Resources.getResourceAsStream(String path)

  1. 获得 sqlSession 对象

sqlSession 通过 sqlSessionFatory.open 获得
sqlSessionFatory 通过 sqlSessionFatoryBuilder.build (configuration) 获得
build 需要获取数据库信息

  • 创建 SqlSessionFactoryBuilder
  • 通过 SqlSessionFatoryBuilder.build () 获得 SqlSessionFatory
  • 通过 DefaultSqlSessionFactory.open () 获得 SqlSession
  • 创建 DefaultSqlSession 实现基础方法 selectAll,selectList
  1. 执行 JDBC 逻辑

创建 Executor、Executor 实现类,执行 CURD

  1. 处理返回结果

通过反射或内省 + SQLID 上的 resultType 全路径,处理返参

  • 问题 1:数据库类型与实体类型不一致
    1
    Exception in thread "main" java.lang.IllegalArgumentException: argument type mismatch at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:498)
  • 问题 2:数据库版本与驱动版本不一致

无法获取数据库连接,报错信息和获取连接方法有关

使用 C3P0 连接池是报错:

1
java.sql.SQLException: Connections could not be acquired from the underlying database!

使用 DriverManager 直接连接时:

1
2
Client does not support authentication protocol requested by server; consider upgrading MySQL client

  1. 持久层实现

通过 mapper 接口,数据库的交互

SqlSession 中创建一个 getMapper 方法,获取 mapper 的代理类,执行被代理类的方法

# Mybatis

# 概念

基于 ORM 的 半自动 轻量级持久层框架。

# 缓存

底层数据结构: 就是一个 HashMap。

先去缓存中查,然后到数据库中,如果缓存中有,就直接返回,不再去数据库查询。

# 一级缓存 - SqlSession 级别

是否启用: 默认开启

cacheKey: org.apache.ibatis.executor.BaseExecutor#createCacheKey

增删改操作时,会刷新缓存(全部缓存

# 二级缓存 - NameSpace 级别

是否启用: 默认关闭,需要手动开启

  • [I] 二级缓存是在 SqlSession 事务提交时写入的

  • [!] 二级缓存在分布式的情况下,可能有问题。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@Test  
public void secondLevelCacheTest() throws IOException {
InputStream inputStream = Resources.getResourceAsStream("sqlMapConfig.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

SqlSession sqlSession1 = sqlSessionFactory.openSession();
SqlSession sqlSession2 = sqlSessionFactory.openSession();

IUserMapper mapper1 = sqlSession1.getMapper(IUserMapper.class);
IUserMapper mapper2 = sqlSession2.getMapper(IUserMapper.class);

User user1 = mapper1.selectByPrimaryKey(1);
// 这样是不会查到二级缓存的,需要事务提交或者关闭后才可以
// sqlSession1.commit();
// sqlSession1.close();

User user2 = mapper2.selectByPrimaryKey(1);

System.out.println(user1==user2);
}

结论: 节省了数据库的交互

Q:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
@Test  
public void firstLevelCacheTest3() throws IOException {
InputStream inputStream = Resources.getResourceAsStream("sqlMapConfig.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

SqlSession sqlSession = sqlSessionFactory.openSession(true);
SqlSession sqlSession2 = sqlSessionFactory.openSession(true);

IUserMapper mapper = sqlSession.getMapper(IUserMapper.class);
IUserMapper mapper2 = sqlSession2.getMapper(IUserMapper.class);

User user = mapper.selectByPrimaryKey(1);
System.out.println(user);
mapper2.updateUserByPrimaryKey(new User(1, "gaga"));
User user1 = mapper.selectByPrimaryKey(1);
System.out.println(user1);
System.out.println("user == user1: " + (user == user1));
sqlSession.close();
sqlSession2.close();
}


// User{id=1, name='haha'}
// User{id=1, name='haha'}
// user == user1: true

# 插件

  • [I] 需要在 SqlMapConfig.xml 中启用

1
2
3
<plugins>  
<plugin interceptor="com.li.plugin.MyPlugin"></plugin>
</plugins>

# 分页插件

拦截器实现

  • [*] com.github.pagehelper.PageHelper

  • [*] 入口: com.github.pagehelper.SqlUtil#_processPage

  • [*] 增加 COUNTSQL: com.github.pagehelper.MSUtils#processCountMappedStatement(MappedStatement ms, SqlSource sqlSource, Object[] args)

  • countSql 返回结果大于 0 时,执行分页,将总数设置到 page 对象中

  • 替换参数 com.github.pagehelper.MSUtils#processPageMappedStatement(MappedStatement ms, SqlSource sqlSource, Page page, Object[] args)

  • 创建新的 mapperStatement,执行分页 SQL

  • 设置分页参数: com.github.pagehelper.MSUtils#setPageParameter

# 通用 Mapper

# 架构原理

# 架构设计

# 接口
  • 通过 sqlSession.method (statementId) 或者 Mapper 代理类调用方法,执行主句的增删改查。
  • 调用接口修改配置信息等。
# 数据处理
  • 请求参数处理 (@Param):ParameterHandler
  • SQL 解析 (处理占位符、Mapper 标签):SqlSource
  • SQL 执行 (JDBC):Executor
  • 返回结果处理 (类型转换等):ResultSetHandler
# 框架支撑
  • 事务管理
  • 连接池管理
  • 缓存机制

# 主要构件

  • SqlSession:session 表示与数据库的连接

  • Executor:执行器

  • StatementHandler:

  • ParameterHandler:

  • BoundSql:

  • ResultSetHander:

  • TypeHandler:数据库类型与 JavaBean 类型的转换

  • MappedStatement:

  • SqlSource:

# 总体流程

  1. SqlSessionFactoryBuilder 获取 SqlSessionFactory
  2. SqlSessionFactory.openSession 获取 SqlSession 对象
  3. 通过 getMapper 获取 Mapper 代理对象
  4. 执行代理 Mapper 的方法
  5. => Executor Mybatis 的执行器
  6. => StatementHandler 与 JDBC Statement 的交互
  7. => ParameterHandler 处理方法中携带的参数,拼接到 Sql 中
  8. => 执行 JDBC 流程(加载驱动、建立连接、定义 Sql、获取预处理对象、处理参数、执行、处理返回结果)
  9. => 处理 Java 类型和数据库类型映射
更新于 阅读次数

请我喝[茶]~( ̄▽ ̄)~*

有李说不清 微信支付

微信支付

有李说不清 支付宝

支付宝

有李说不清 贝宝

贝宝