Preparação da análise de código-fonte Mybatis
dicas: mybatis
Diretório do artigo
1. Crie o ambiente de origem Mybatis
1. Requisitos ambientais:
JDK1.8, Maven, Eclipse ou IntelliJ IDEA
2. Obtenha o código fonte
- Código fonte do Mybatis:
https://github.com/mybatis/mybatis-3
- Código fonte integrado Mybatis e Spring:
https://github.com/mybatis/spring
- Código fonte da classe pai Mybatis:
https://github.com/mybatis/parent
3. Importe o código fonte para o IDE
-
Arquivo => Novo => Projeto… => Projeto vazio => Próximo => 命名 => Concluir
-
Arquivo => Novo => Módulo de fontes existentes… => Selecione três códigos-fonte e um código de teste, respectivamente, e importe-os separadamente
4. Introdução ao HSQLDB
- O modo de memória do HSQLDB incorporado no Mybatis é usado como um banco de dados para teste de unidade, portanto, não há necessidade de instalar bancos de dados como o Mysql.
- O nome do usuário e a senha do modo de memória podem ser especificados à vontade, mas se você precisar persistir localmente, precisará especificar o nome do usuário e a senha.
- Os testes de unidade não se importam, eles podem ser especificados à vontade. O nome de usuário padrão é sa e a senha está em branco.
- ScriptRunner é usado para executar scripts (XXX.sql) e SqlRunner é usado para executar instruções sql (instruções de adição, exclusão, modificação e consulta).
5. Entrada da análise do código fonte:
public class Example01 {
private Connection conn = null;
@Before
public void initData() {
try {
//Example02 example02 = new Example02();//調用方法的時候回提示,引入依賴,但是調用common裡面的文件的時候卻不用。
// 加载HSQLDB驱动
Class.forName("org.hsqldb.jdbcDriver");
// 获取Connection对象,用户名和密码可以随便指定
conn = DriverManager.getConnection("jdbc:hsqldb:mem:mybatis",
"satestlalla", "");
// 使用Mybatis的ScriptRunner工具类执行数据库脚本
ScriptRunner scriptRunner = new ScriptRunner(conn);
scriptRunner.setLogWriter(null);
//文件都放在mybatis-common module的resource下面
scriptRunner.runScript(Resources.getResourceAsReader("create-table.sql"));
scriptRunner.runScript(Resources.getResourceAsReader("init-data.sql"));
} catch (Exception e) {
e.printStackTrace();
}
}
@Test
public void testHsqldbQuery() {
// SqlRunner是Mybatis封装的操作数据库的工具类
SqlRunner sqlRunner = new SqlRunner(conn);
try {
//调用SqlRunner类的selectAll()方法查询数据
List<Map<String, Object>> results = sqlRunner.selectAll("select * from user");
results.forEach(System.out::println);//jdk8之后新特性
sqlRunner.closeConnection();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
Segundo, a especificação JDBC
1. documento de especificação do jdbc
- Endereço de download do documento de especificação JDBC4.2: https://download.oracle.com/otndocs/jcp/jdbc-4_2-mrel2-spec/index.html
2. Etapas para operar o banco de dados
- Estabelecer uma conexão de dados
- Executar instrução SQL
- Recuperar resultados de execução SQL
- Fechar conexão
3. Estabeleça uma conexão com o banco de dados
Três maneiras:
-
DriverManager - fornecido por JDBC1.0
Precisa mostrar a classe do driver de carregamento:
Class.forName("org.hsqldb.jdbcDriver");
-
DataSource - fornecido por JDBC2.0 (configuração mais recomendada e flexível)
- DataSource: a classe mais básica
- DataSourceFactory: classe de fábrica
- ConnectionPoolDataSource: suporta armazenamento em cache e reutilização de objetos Connection
- XADataSource: suporta transações distribuídas
O código fonte é o seguinte:
//第一种方式
// 加载驱动 到JDBC4之后不需要直接声明驱动,可以在META-INF.services目录下面创建一个跟驱动同名的文件即可,参考chapter02.SPIExample
/*原理如下
ServiceLoader<Driver> drivers = ServiceLoader.load(java.sql.Driver.class);
for (Driver driver : drivers ) {
System.out.println(driver.getClass().getName());
}
*/
Class.forName("org.hsqldb.jdbcDriver");
// 获取Connection对象
Connection connection = DriverManager.getConnection("jdbc:hsqldb:mem:mybatis",
"sa", "");
//第二种方式
// 创建DataSource实例
DataSource dataSource = new UnpooledDataSource("org.hsqldb.jdbcDriver","jdbc:hsqldb:mem:mybatis", "sa", "");
// 获取Connection对象
Connection connection = dataSource.getConnection();
//第三种方式
// 创建DataSource实例
DataSourceFactory dsf = new UnpooledDataSourceFactory();
Properties properties = new Properties();
InputStream configStream = Thread.currentThread().getContextClassLoader().getResourceAsStream("database.properties");
properties.load(configStream);
dsf.setProperties(properties);
DataSource dataSource = dsf.getDataSource();
// 获取Connection对象
Connection connection = dataSource.getConnection();
//插入操作
Statement statement = connection.createStatement();
statement.addBatch("insert into " +
"user(create_time, name, password, phone, nick_name) " +
"values('2010-10-24 10:20:30', 'User1', 'test', '18700001111', 'User1');");
statement.addBatch("insert into " +
"user (create_time, name, password, phone, nick_name) " +
"values('2010-10-24 10:20:30', 'User2', 'test', '18700002222', 'User2');");
statement.executeBatch();
//PreparedStatement可配置参数方式操作数据库
PreparedStatement stmt = connection.prepareStatement("insert into " +
"user(create_time, name, password, phone, nick_name) " +
"values(?,?,?,?,?);");
stmt.setString(1,"2010-10-24 10:20:30");
stmt.setString(2,"User1");
stmt.setString(3,"test");
stmt.setString(4,"18700001111");
stmt.setString(5,"User1");
ParameterMetaData pmd = stmt.getParameterMetaData();
for(int i = 1; i <= pmd.getParameterCount(); i++) {
String typeName = pmd.getParameterTypeName(i);
String className = pmd.getParameterClassName(i);
System.out.println("第" + i + "个参数," + "typeName:" + typeName + ", className:" + className);
}
stmt.execute();
//自增长主键按,可以获取数据库里面自增长的当前值
Statement stmt = conn.createStatement();
String sql = "insert into user(create_time, name, password, phone, nick_name) " +
"values('2010-10-24 10:20:30','User1','test','18700001111','User1');";
ResultSet genKeys = stmt.getGeneratedKeys();
stmt.executeUpdate(sql, Statement.RETURN_GENERATED_KEYS);
genKeys = stmt.getGeneratedKeys();
if(genKeys.next()) {
System.out.println("自增长主键:" + genKeys.getInt(1));
}
//数据库配置信息
DatabaseMetaData dmd = conn.getMetaData();
System.out.println("数据库URL:" + dmd.getURL());
System.out.println("数据库用户名:" + dmd.getUserName());
System.out.println("数据库产品名:" + dmd.getDatabaseProductName());
System.out.println("数据库产品版本:" + dmd.getDatabaseProductVersion());
System.out.println("驱动主版本:" + dmd.getDriverMajorVersion());
System.out.println("驱动副版本:" + dmd.getDriverMinorVersion());
System.out.println("数据库供应商用于schema的首选术语:" + dmd.getSchemaTerm());
System.out.println("数据库供应商用于catalog的首选术语:" + dmd.getCatalogTerm());
System.out.println("数据库供应商用于procedure的首选术语:" + dmd.getProcedureTerm());
System.out.println("null值是否高排序:" + dmd.nullsAreSortedHigh());
System.out.println("null值是否低排序:" + dmd.nullsAreSortedLow());
System.out.println("数据库是否将表存储在本地文件中:" + dmd.usesLocalFiles());
System.out.println("数据库是否为每个表使用一个文件:" + dmd.usesLocalFilePerTable());
System.out.println("数据库SQL关键字:" + dmd.getSQLKeywords());
//保存节点savepoint,可以回滚使用。
String sql1 = "insert into user(create_time, name, password, phone, nick_name) " +
"values('2010-10-24 10:20:30','User1','test','18700001111','User1')";
String sql2 = "insert into user(create_time, name, password, phone, nick_name) " +
"values('2010-10-24 10:20:30','User2','test','18700001111','User2')";
conn.setAutoCommit(false);
Statement stmt = conn.createStatement();
stmt.executeUpdate(sql1);
// 创建保存点
Savepoint savepoint = conn.setSavepoint("SP1");
stmt.executeUpdate(sql2);
// 回滚到保存点
conn.rollback(savepoint);
conn.commit();
//后续打印统一代码
ResultSet rs = conn.createStatement().executeQuery("select * from user ");
//上面是下面的简单缩写
//Statement statement = connection.createStatement();
//ResultSet resultSet = statement.executeQuery("select * from user");
// 遍历ResultSet
ResultSetMetaData metaData = resultSet.getMetaData();
int columCount = metaData.getColumnCount();
while (resultSet.next()) {
for (int i = 1; i <= columCount; i++) {
String columName = metaData.getColumnName(i);
String columVal = resultSet.getString(columName);
System.out.println(columName + ":" + columVal);
}
System.out.println("--------------------------------------");
}
// 关闭连接
IOUtils.closeQuietly(statement);
IOUtils.closeQuietly(connection);
Três, Mybatis ferramentas comumente usadas
1. Use a classe SQL para gerar instruções:
Tradicionalmente, a concatenação de strings requer atenção a detalhes como espaços, o que é muito problemático.Este problema pode ser resolvido pelo encapsulamento de SQL.
Duas maneiras de implementação:
-
new SQL().SELECT().SELECT().FROM()....
-
new SQL(){{SELECT();SELECT();FROM();....}}
O código específico é o seguinte:
String insertSql = new SQL().
INSERT_INTO("PERSON").
VALUES("ID, FIRST_NAME", "#{id}, #{firstName}").
VALUES("LAST_NAME", "#{lastName}").toString();
/*结果是:
INSERT INTO PERSON
(ID, FIRST_NAME, LAST_NAME)
VALUES (#{id}, #{firstName}, #{lastName})
*/
String sql_area = new SQL()
{
{
SELECT("P.ID, P.USERNAME, P.PASSWORD");
SELECT("P.FIRST_NAME, P.LAST_NAME");
FROM("PERSON P");
WHERE("P.ID = #{id}");
WHERE("P.FIRST_NAME = ${first_name}");
}
}.toString();
/*结果是:
SELECT P.ID, P.USERNAME, P.PASSWORD, P.FIRST_NAME, P.LAST_NAME
FROM PERSON P
WHERE (P.ID = #{id} AND P.FIRST_NAME = ${first_name})
*/
2. Use o ScriptRunner para executar scripts
Lógica de origem:
- Chame o método setAuoCommit () para definir se a transação é confirmada automaticamente com base no valor do atributo autoCommit .
- Determine o valor do atributo sendFullScript.Se for verdadeiro, chame o método executeFullScript () para ler o conteúdo do arquivo de uma só vez e, em seguida, chame o método execute () do objeto Statement para executar todas as instruções SQL de uma só vez.
- Se o valor da propriedade sendFullScript for falso, o método executeLineByLine () será chamado para ler o SQ L linha por linha, e a instrução SQL será executada linha por linha como o final de uma instrução SQL.
3. Use o SqlRunner para executar scripts
-
Resumo dos métodos da classe:
-
closeConnection: Feche a conexão conexão.
-
selectOne, selectAll: encontre um registro (retorne um), encontre todos os registros (retorne vários).
-
inserir, atualizar, excluir: inserir, atualizar, excluir operações
-
executar: executar uma instrução sql, de preferência DDL
-
Lógica de origem:
-
Depois de chamar o método PrepareStatement () do objeto Connection para obter o objeto PreparedStatement, chame o método setParameters () para atribuir valores aos espaços reservados de parâmetros no SQL e para determinar o tipo de cada parâmetro .
-
Chame o método executeQuery () de PreparedStatement para executar a operação de consulta
-
Chame o método getResult () para converter o objeto ResultSet em um objeto de lista , onde cada objeto de mapa da lista corresponde a um registro.
Quarto, envolvendo padrões de design
-
Construtor: SqlSessionFactoryBuilder, XMLConfigBuilder, XMLMapperBuilder, XMLStatementBuilder, CacheBuilder.
-
Modo de fábrica: por exemplo, SqlSessionFactory, ObjectFactory, MapperProxyFactory;
-
Modo Singleton: como ErrorContext e LogFactory;
-
Modo proxy: o núcleo da implementação do Mybatis, como MapperProxy, ConnectionLogger, proxy dinâmico jdk e o pacote executor.loader usa cglib ou javassist para obter o efeito de carregamento lento;
-
Modo de combinação: por exemplo, SqlNode e várias subclasses ChooseSqlNode, etc;
-
Padrão do método de modelo: como BaseExecutor e SimpleExecutor, assim como BaseTypeHandler e todas as subclasses como IntegerTypeHandler;
-
Modo do adaptador: Por exemplo, a interface Mybatis do Log e sua adaptação a várias estruturas de log, como jdbc e log4j;
-
Modo Decorador: Por exemplo, a implementação de vários decoradores no subpacote cache.decorators no pacote Cache;
-
Modo iterador: por exemplo, modo iterador PropertyTokenizer;
Cinco, lógica do código fonte
Análise XML = "Get Configuration =" Contém fontes de dados DateSource, statementId e MappedStatement correspondentes variáveis de mapa = "MappedStatement contém sqlSource, parâmetros e tipos de valor de retorno e outras informações de análise XML =" A interface sqlSouce implementa uma variedade de classes, incluindo a classe da interface SqlNode ( O XML analisa o SQL original. Somente quando executado, ele conhecerá o SQL específico, porque há julgamentos e valores de atributos específicos) e obterá o método BoundSql (o resultado do SQL original após a análise é encapsulado apenas em uma classe) => Implementação do Sqlnode A classe é uma lista.Os elementos da lista são uma estrutura em árvore, que armazena os loops de texto e rótulo sob o rótulo sql. Ao mesmo tempo, a classe de implementação SqlSouce possui um valor de atributo BoundSql, que é obtido após a análise do XML obtido e o boundSql é analisado após o resultado da análise de XML obtido durante a execução. = "Executar sql de acordo com o resultado da análise