b2c信息网

您现在的位置是:首页 > 昨日新闻 > 正文

昨日新闻

mybatis占位符源码(mybatis通配符和占位符)

hacker2022-07-15 00:41:41昨日新闻88
本文目录一览:1、初看Mybatis源码SQL是怎么执行的2、

本文目录一览:

初看Mybatis 源码 SQL是怎么执行的

一条sql语句到底是怎么执行的?我们知道Mybatis其实是对JDBC的一个封装。假如我执行

session.update("com.mybatis.dao.AuthUserDao.updateAuthUserEmailByName", test@email.com);

语句,追踪下来,Executor、 BaseStatementHandler等等。在 SimpleExecutor 中有如下代码:

public int doUpdate(MappedStatement ms, Object parameter) throws SQLException {

Statement stmt = null;

try {

Configuration configuration = ms.getConfiguration();

StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, RowBounds.DEFAULT, null, null);

stmt = prepareStatement(handler, ms.getStatementLog());

return handler.update(stmt);

} finally {

closeStatement(stmt);

}

}

1. 首先获取相关配置信息,这个在初始化时,从配置文件中解析而来

2. 新建了一个handler

3. 做了执行statement之前的准备工作。看看准备了些什么,跟踪代码,最后进入了DataSource类的doGetConnection方法,该方法做如下操作:

private Connection doGetConnection(Properties properties) throws SQLException {

initializeDriver();

Connection connection = DriverManager.getConnection(url, properties);

configureConnection(connection);

return connection;

}

private synchronized void initializeDriver() throws SQLException {

if (!registeredDrivers.containsKey(driver)) {

Class? driverType;

try {

if (driverClassLoader != null) {

driverType = Class.forName(driver, true, driverClassLoader);

} else {

driverType = Resources.classForName(driver);

}

// DriverManager requires the driver to be loaded via the system ClassLoader.

//

Driver driverInstance = (Driver)driverType.newInstance();

DriverManager.registerDriver(new DriverProxy(driverInstance));

registeredDrivers.put(driver, driverInstance);

} catch (Exception e) {

throw new SQLException("Error setting driver on UnpooledDataSource. Cause: " + e);

}

}

}

原来是通过prepareStatement 来执行了 我们初始化jdbc的操作。Class.forName DriverManager.getConnection. 这两步是在这里面完成的。

4. 将执行sql的部分交给handler

继续跟踪handler 可以看到SimpleStatementHandler 中。如下执行这个update语句

public int update(Statement statement)

throws SQLException {

String sql = boundSql.getSql();

Object parameterObject = boundSql.getParameterObject();

KeyGenerator keyGenerator = mappedStatement.getKeyGenerator();

int rows;

if (keyGenerator instanceof Jdbc3KeyGenerator) {

statement.execute(sql, Statement.RETURN_GENERATED_KEYS);

rows = statement.getUpdateCount();

keyGenerator.processAfter(executor, mappedStatement, statement, parameterObject);

} else if (keyGenerator instanceof SelectKeyGenerator) {

statement.execute(sql);

rows = statement.getUpdateCount();

keyGenerator.processAfter(executor, mappedStatement, statement, parameterObject);

} else {

statement.execute(sql);

rows = statement.getUpdateCount();

}

return rows;

}

这边就完成了statement的操作,整个过程就是我们Jdbc的过程。原来真的就是对JDBC的简单封装。

其实Mybatis的整个执行过程,理解起来分为如下几个过程:

1. 加载配置文件

2. 解析配置文件,从配置文件中解析出来 datasource、mapper文件、事务配置等等。将配置信息保存在对象内

3. 调用相关语句,执行sql。在执行的方法中分别完成JDBC的一系列操作。

mybatis中的sql语句中的#占位符和$占位符有什么区别

#{},和 ${}传参的区别如下:

使用#传入参数是,sql语句解析是会加上"",当成字符串来解析,这样相比于$的好处是比较明显对的吧,#{}传参能防止sql注入,如果你传入的参数为 单引号',那么如果使用${},这种方式 那么是会报错的

另外一种场景是,如果要做动态的排序,比如 order by column,这个时候务必要用${}

select * from table order by 'name' ,这样是没用

目前来看,能用#就不要用$,

怎么没有mybatis源码解析相关的文档

我们还记得是这样配置sqlSessionFactory的:

[java] view plain copy

bean id="sqlSessionFactory" class="b9de-e9b9-cd12-d8f6 org.mybatis.spring.SqlSessionFactoryBean"

property name="dataSource" ref="dataSource" /

property name="configLocation" value="classpath:configuration.xml"/property

property name="mapperLocations" value="classpath:com/xxx/mybatis/mapper/*.xml"/

property name="typeAliasesPackage" value="com.tiantian.mybatis.model" /

/bean

这里配置了一个mapperLocations属性,它是一个表达式,sqlSessionFactory会根据这个表达式读取包com.xxx.mybaits.mapper下面的所有xml格式文件,那么具体是怎么根据这个属性来读取配置文件的呢?

答案就在SqlSessionFactoryBean类中的buildSqlSessionFactory方法中:

[java] view plain copy

if (!isEmpty(this.mapperLocations)) {

for (Resource mapperLocation : this.mapperLocations) {

if (mapperLocation == null) {

continue;

}

try {

XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(mapperLocation.getInputStream(),

configuration, mapperLocation.toString(), configuration.getSqlFragments());

xmlMapperBuilder.parse();

} catch (Exception e) {

throw new NestedIOException("Failed to parse mapping resource: '" + mapperLocation + "'", e);

} finally {

ErrorContext.instance().reset();

}

if (logger.isDebugEnabled()) {

logger.debug("Parsed mapper file: '" + mapperLocation + "'");

}

}

}

mybatis使用XMLMapperBuilder类的实例来解析mapper配置文件。

[java] view plain copy

public XMLMapperBuilder(Reader reader, Configuration configuration, String resource, MapString, XNode sqlFragments) {

this(new XPathParser(reader, true, configuration.getVariables(), new XMLMapperEntityResolver()),

configuration, resource, sqlFragments);

}

[java] view plain copy

private XMLMapperBuilder(XPathParser parser, Configuration configuration, String resource, MapString, XNode sqlFragments) {

super(configuration);

this.builderAssistant = new MapperBuilderAssistant(configuration, resource);

this.parser = parser;

this.sqlFragments = sqlFragments;

this.resource = resource;

}

接着系统调用xmlMapperBuilder的parse方法解析mapper。

[java] view plain copy

public void parse() {

//如果configuration对象还没加载xml配置文件(避免重复加载,实际上是确认是否解析了mapper节点的属性及内容,

//为解析它的子节点如cache、sql、select、resultMap、parameterMap等做准备),

//则从输入流中解析mapper节点,然后再将resource的状态置为已加载

if (!configuration.isResourceLoaded(resource)) {

configurationElement(parser.evalNode("/mapper"));

configuration.addLoadedResource(resource);

bindMapperForNamespace();

}

//解析在configurationElement函数中处理resultMap时其extends属性指向的父对象还没被处理的resultMap节点

parsePendingResultMaps();

//解析在configurationElement函数中处理cache-ref时其指向的对象不存在的cache节点(如果cache-ref先于其指向的cache节点加载就会出现这种情况)

parsePendingChacheRefs();

//同上,如果cache没加载的话处理statement时也会抛出异常

parsePendingStatements();

}

mybatis解析mapper的xml文件的过程已经很明显了,接下来我们看看它是怎么解析mapper的:

[java] view plain copy

private void configurationElement(XNode context) {

try {

//获取mapper节点的namespace属性

String namespace = context.getStringAttribute("namespace");

if (namespace.equals("")) {

throw new BuilderException("Mapper's namespace cannot be empty");

}

//设置当前namespace

builderAssistant.setCurrentNamespace(namespace);

//解析mapper的cache-ref节点

cacheRefElement(context.evalNode("cache-ref"));

//解析mapper的cache节点

cacheElement(context.evalNode("cache"));

//解析mapper的parameterMap节点

parameterMapElement(context.evalNodes("/mapper/parameterMap"));

//解析mapper的resultMap节点

resultMapElements(context.evalNodes("/mapper/resultMap"));

//解析mapper的sql节点

sqlElement(context.evalNodes("/mapper/sql"));

//使用XMLStatementBuilder的对象解析mapper的select、insert、update、delete节点,

//mybaits会使用MappedStatement.Builder类build一个MappedStatement对象,

//所以mybaits中一个sql对应一个MappedStatement

buildStatementFromContext(context.evalNodes("select|insert|update|delete"));

} catch (Exception e) {

throw new BuilderException("Error parsing Mapper XML. Cause: " + e, e);

}

}

configurationElement函数几乎解析了mapper节点下所有子节点,至此mybaits解析了mapper中的所有节点,并将其加入到了Configuration对象中提供给sqlSessionFactory对象随时使用。这里我们需要补充讲一下mybaits是怎么使用XMLStatementBuilder类的对象的parseStatementNode函数借用MapperBuilderAssistant类对象builderAssistant的addMappedStatement解析MappedStatement并将其关联到Configuration类对象的:

[java] view plain copy

public void parseStatementNode() {

//ID属性

String id = context.getStringAttribute("id");

//databaseId属性

String databaseId = context.getStringAttribute("databaseId");

if (!databaseIdMatchesCurrent(id, databaseId, this.requiredDatabaseId)) {

return;

}

//fetchSize属性

Integer fetchSize = context.getIntAttribute("fetchSize");

//timeout属性

Integer timeout = context.getIntAttribute("timeout");

//parameterMap属性

String parameterMap = context.getStringAttribute("parameterMap");

//parameterType属性

String parameterType = context.getStringAttribute("parameterType");

Class? parameterTypeClass = resolveClass(parameterType);

//resultMap属性

String resultMap = context.getStringAttribute("resultMap");

//resultType属性

String resultType = context.getStringAttribute("resultType");

//lang属性

String lang = context.getStringAttribute("lang");

LanguageDriver langDriver = getLanguageDriver(lang);

Class? resultTypeClass = resolveClass(resultType);

//resultSetType属性

String resultSetType = context.getStringAttribute("resultSetType");

StatementType statementType = StatementType.valueOf(context.getStringAttribute("statementType", StatementType.PREPARED.toString()));

ResultSetType resultSetTypeEnum = resolveResultSetType(resultSetType);

String nodeName = context.getNode().getNodeName();

SqlCommandType sqlCommandType = SqlCommandType.valueOf(nodeName.toUpperCase(Locale.ENGLISH));

//是否是select节点

boolean isSelect = sqlCommandType == SqlCommandType.SELECT;

//flushCache属性

boolean flushCache = context.getBooleanAttribute("flushCache", !isSelect);

//useCache属性

boolean useCache = context.getBooleanAttribute("useCache", isSelect);

//resultOrdered属性

boolean resultOrdered = context.getBooleanAttribute("resultOrdered", false);

// Include Fragments before parsing

XMLIncludeTransformer includeParser = new XMLIncludeTransformer(configuration, builderAssistant);

includeParser.applyIncludes(context.getNode());

// Parse selectKey after includes and remove them.

processSelectKeyNodes(id, parameterTypeClass, langDriver);

// Parse the SQL (pre: selectKey and include were parsed and removed)

SqlSource sqlSource = langDriver.createSqlSource(configuration, context, parameterTypeClass);

//resultSets属性

String resultSets = context.getStringAttribute("resultSets");

//keyProperty属性

String keyProperty = context.getStringAttribute("keyProperty");

//keyColumn属性

String keyColumn = context.getStringAttribute("keyColumn");

KeyGenerator keyGenerator;

String keyStatementId = id + SelectKeyGenerator.SELECT_KEY_SUFFIX;

keyStatementId = builderAssistant.applyCurrentNamespace(keyStatementId, true);

if (configuration.hasKeyGenerator(keyStatementId)) {

keyGenerator = configuration.getKeyGenerator(keyStatementId);

} else {

//useGeneratedKeys属性

keyGenerator = context.getBooleanAttribute("useGeneratedKeys",

configuration.isUseGeneratedKeys() SqlCommandType.INSERT.equals(sqlCommandType))

? new Jdbc3KeyGenerator() : new NoKeyGenerator();

}

builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType,

fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass,

resultSetTypeEnum, flushCache, useCache, resultOrdered,

keyGenerator, keyProperty, keyColumn, databaseId, langDriver, resultSets);

}

由以上代码可以看出mybaits使用XPath解析mapper的配置文件后将其中的resultMap、parameterMap、cache、statement等节点使用关联的builder创建并将得到的对象关联到configuration对象中,而这个configuration对象可以从sqlSession中获取的,这就解释了我们在使用sqlSession对数据库进行操作时mybaits怎么获取到mapper并执行其中的sql语句的问题。

发表评论

评论列表

  • 孤央鸠魁(2022-07-15 06:16:01)回复取消回复

    ,这个在初始化时,从配置文件中解析而来2. 新建了一个handler3. 做了执行statement之前的准备工作。看看准备了些什么,跟踪代码,最后进入了DataSou

  • 馥妴二囍(2022-07-15 03:31:15)回复取消回复

    repareStatement(handler, ms.getStatementLog()); return handler.update(stmt); } finally {

  • 慵吋空宴(2022-07-15 02:26:32)回复取消回复

    throw new NestedIOException("Failed to parse mapping resource: '" + mapperLocation + "'", e);

  • 泪灼淤浪(2022-07-15 04:39:20)回复取消回复

    SQLException { initializeDriver(); Connection connection = DriverManager.getConnection(url, properties); configureConnection(

  • 孤鱼挽鹿(2022-07-15 02:31:32)回复取消回复

    leExecutor 中有如下代码: public int doUpdate(MappedStatement ms, Object parameter) throws SQLException