当前位置: 首页 > news >正文

抖音服务商/河南seo推广

抖音服务商,河南seo推广,长沙推广网站,营销方案图片1、全局配置文件 前面我们看到的Mybatis全局文件并没有全部列举出来&#xff0c;所以这一章我们来详细的介绍一遍&#xff0c;Mybatis的全局配置文件并不是很复杂&#xff0c;它的所有元素和代码如下所示&#xff1a; <?xml version"1.0" encoding"UTF-8&…

1、全局配置文件

前面我们看到的Mybatis全局文件并没有全部列举出来,所以这一章我们来详细的介绍一遍,Mybatis的全局配置文件并不是很复杂,它的所有元素和代码如下所示:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"><configuration> <!--配置--><properties/> <!--属性--><settings/> <!--全局配置参数--><typeAliases/> <!--类型别名--><typeHandlers/> <!--类型处理器--><objectFactory/><!--对象工厂--><plugins/><!--创建--><environments default=""><!--环境配置--><environment id=""><!--环境变量--><transactionManager type=""/><!--事务管理器--><dataSource type=""/><!--数据源--></environment></environments><databaseIdProvider type=""/><!--数据库厂商标识--><mappers/><!--映射器-->
</configuration>

注意:Mybatis的配置文件的顺序是严格按照从上至下的顺序声明,不颠倒顺序,如果颠倒了它们的顺序,那么Mybatis在启动阶段就会产生异常,导致程序无法运行

2、properties属性

properties 的作用是引用java属性文件中的配置信息,比如:加载连接数据库的各种属性的配置文件。

mybatis提供了三种方式使用properties属性:

  • property子元素(不推荐):就是在properties属性中增加子属性property,从而设置一些配置的key-value。
  • properties文件:就是直接使用properties引入外部配置文件,相当于将子属性抽取成一个独立的外部文件引入,例如db.properties。
  • 程序代码传递参数:就是通过代码的方式设置该配置相关的信息,如数据库配置文件中的用户名和密码一般是密文,但是连接数据库时需要对配置进行解密,此时就只能通过程序代码的方式配置了。

2.1、property子元素(不推荐)

以上一章的例子为基础,使用property子元素将数据库的连接配置信息进行改写,如下所示:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"><configuration><properties><!--property子元素定义--><property name="database.driver" value="com.mysql.cj.jdbc.Driver"/><property name="database.url" value="jdbc:mysql://localhost:3306/user?serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=utf-8"/><property name="database.username" value="root"/><property name="database.password" value="root"/></properties><environments default="development"><environment id="development"><transactionManager type="JDBC"></transactionManager><dataSource type="POOLED"><!--配置连接数据库的4个基本信息--><property name="driver" value="${database.driver}"/><property name="url" value="${database.url}"/><property name="username" value="${database.username}"/><property name="password" value="${database.password}"/></dataSource></environment></environments>
</configuration>

这种配置方式时有缺点的,虽然这样定义一次可以到处引用,但是如果配置项很多,那么就会让配置文件显得很庞大,所以使用这种方式显然不是一个很好的选择,为了解决这个缺点,我们可以使用下面的配置方式,也就是使用properties文件的方式。

2.2、properties文件

使用properties文件的方式在我们的开发中是比较常用,主要的这种方式简单,方便日后的维护和修改。首先将上述配置中的所有property属性提取到一个叫做 databse.properties 的配置文件中,

如下代码所示:

#数据库连接配置
database.driver=com.mysql.cj.jdbc.Driver
database.url=jdbc:mysql://localhost:3306/user?serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=utf-8
database.username=root
database.password=root

然后在Mybatis配置文件中使用元素的resource属性来引入properties文件。

<properties resource="database.properties" />

这样就相当于将 database.properties 中的所有配置都加载到MyBatis的配置文件中了,然后再按照 ${database.username} 的方式引入properties文件的属性参数即可。但是这种使用方式也存在它的缺点,当外部配置文件中的值需要加密时,如连接数据库的用户名和密码,无法在配置文件中进行解密,所以只能通过程序代码传递的方式,就是要介绍的第三种,如下。

2.3、程序代码传递参数

在真实的开发环境中,数据库的用户名和密码对开发人员和其他人员是保密的。而运维人员为了数据保密,一般都会把数据库的用户名和密码进行加密处理,会把加密后的数据配置到properties文件中。所以开发人员就必须用解密后的用户名和密码连接数据库,不可能用加密的数据进行连接。此时就需要使用到此种方式来对配置文件进行解密。其实这种方式一般会和第二种配合使用,作用对特殊配置进行覆盖或重写,以上面的database.properties为例,在使用到数据库配置信息时对配置中的用户名和密码进行解密。这里举个MyBatis中获取SqlSessionFactory的例子,代码如下:

public static SqlSessionFactory getSqlSessionFactoryByXml() {synchronized (Lock) {if (null != sqlSessionFactory) {return sqlSessionFactory;}String resource = "mybatis-config.xml";InputStream inputStream;InputStream is = null;try {// 加载数据库配置文件is = Resources.getResourceAsStream("database.properties");Properties properties = new Properties();properties.load(is);// 获取加密信息String username= properties.getProperty("database.username");String password= properties.getProperty("database.password");// 解密用户名和密码,并重置属性properties.setProperty("database.username", CyperTool.decodeByBase64(username));properties.setProperty("database.password", CyperTool.decodeByBase64(password));// 读取mybatis配置文件inputStream = Resources.getResourceAsStream(resource);// 通过SqlSessionFactoryBuilder类的builder方法进行构建,并使用程序传递的方式覆盖原有属性sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream, properties);} catch (IOException e) {e.printStackTrace();return null;}return sqlSessionFactory;}}

我们为了保证数据的准确性,加了synchronized锁。首先使用Resources对象读取了database.properties配置文件,然后获取了它原来配置的用户和密码,进行解密操作,最后使用SqlSessionFactoryBuilder的build方法,传递多个properties参数来完成。这将覆盖之前加密的配置,这样就可以连接数据库了,同时也能满足因为人员对数据库的用户名和密码的安全要求。

3、settings属性

settings是MyBatis中最复杂的配置,它能深刻影响MyBatis底层的运行,但是大部分情况下使用默认值便可以运行,所以在大部分情况下不需要大量配置,只需要修改一些常用的规则即可。常用规则有自动映射、驼峰命名映射、级联规则、是否启动缓存、执行器类型等。

所有配置可参考MyBatis官方文档:https://mybatis.org/mybatis-3/zh/configuration.html#settings。可以看到settings的配置项非常之多,但是我们真正使用的并不会太多,我们只需把常用的搞清楚就可以了。比如关于缓存的CacheEnabled,关于级联的lazyLoadingEnabled和aggressiveLazyLoading,关于自动映射的autoMappingBehavior和mapUnderscoreToCamelCase,关于执行器类型的defaultExecutorType等。本文列出重要的几个配置项及意义,并挑几个常用配置加以说明:

<settings><!--缓存配置的全局开关:如果这里设置成false,那么即便在映射器中配置开启也无济于事 --><setting name="cacheEnabled" value="true" /><!--延时加载的全局开关 --><setting name="lazyLoadingEnabled" value="false" /><!-- 是否允许单一语句返回多结果集 --><setting name="multipleResultSetsEnabled" value="true" /><!-- 使用列标签代替列名,需要兼容驱动 --><setting name="useColumnLabel" value="true" /><!-- 允许JDBC自动生成主键,需要驱动兼容。如果设置为true,则这个设置强制使用自动生成主键,尽管一些驱动不能兼容但仍能正常工作 --><setting name="useGeneratedKeys" value="false" /><!-- 指定MyBatis该如何自动映射列到字段或属性:NONE表示取消自动映射;PARTIAL表示只会自动映射,没有定义嵌套结果集和映射结果集;FULL会自动映射任意复杂的结果集,无论是否嵌套 --><setting name="autoMappingBehavior" value="PARTIAL" /><!-- 指定发现自动映射目标未知列(或未知属性类型)的行为。NONE: 不做任何反应WARNING: 输出警告日志FAILING: 映射失败 (抛出 SqlSessionException) --><setting name="autoMappingUnknownColumnBehavior" value="WARNING" /><!-- 配置默认的执行器:SIMPLE是普通的执行器;REUSE会重用预处理语句;BATCH会重用语句并执行批量更新 --><setting name="defaultExecutorType" value="SIMPLE" /><!--设置超时时间:它决定驱动等待数据库响应的秒数,任何正整数--><setting name="defaultStatementTimeout" value="25"/><!--设置数据库驱动程序默认返回的条数限制,此参数可以重新设置,任何正整数 --><setting name="defaultFetchSize" value="100" /><!-- 允许在嵌套语句中使用分页(RowBounds) --><setting name="safeRowBoundsEnabled" value="false" /><!-- 是否开启自动驼峰命名规则,即从a_example到aExample的映射 --><setting name="mapUnderscoreToCamelCase" value="true" /><!-- 本地缓存机制,防止循环引用和加速重复嵌套循环 --><setting name="localCacheScope" value="SESSION" /><!-- 当没有为参数提供特定JDBC类型时,为空值指定JDBC类型。某些驱动需要指定列的JDBC类型,多数情况直接用一般类型即可,如NULL/VARCHAR/OTHER --><setting name="jdbcTypeForNull" value="OTHER" /><!-- 指定触发延迟加载的方法,如equals/clone/hashCode/toString --><setting name="lazyLoadTriggerMethods" value="equals" />
</settings>

4、typeAlianses属性

typeAlianses属性就是起个别名,是为了在映射文件中更方便的编写输入参数类型和输出结果类型,因为平时的输入输出映射的全限定名显得很长,在使用过程中不是很方便,所以MyBatis中允许我们使用一种简写的方式来代替全限定名,这样可以提高我们的开发效率。

别名分为系统别名和自定义别名,系统别名就是系统默认给我们起的别名,例如我们在输入一个数值型的参数是,可以直接写parameterType=”int”,这是因为系统将Integer的Java类型起的别名为int。我们可以通过Mybatis的官方文档来查看:https://mybatis.org/mybatis-3/zh/configuration.html#typeAliases。而自定义别名是自己定义的名称,后面会介绍如何使用。

4.1、系统定义的别名

Mybatis本身给我们定义了大量的别名,包括有基本数据类型,包装类、对象型、集合和Map等等。系统定义的别名是通过TypeAliasRegistry类来定义的,所以我们既可以通过这个对象获取系统中已经定义好的别名,也能自定义别名,先通过一段代码来获取系统中都预定义了哪些别名。

/*** 获取系统别名配置*/
public static void getTypeAlias() {try {InputStream stream = getResourceAsStream("mybatis-config.xml");SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(stream);SqlSession sqlSession = factory.openSession();//获取TypeAliasRegistry对象TypeAliasRegistry typeAliasRegistry = sqlSession.getConfiguration().getTypeAliasRegistry();Map<String, Class<?>> tarMap = typeAliasRegistry.getTypeAliases();int i =0;for (String key : tarMap.keySet()) {//这个++i统计数量System.out.println(++i+"*****"+tarMap.get(key).getSimpleName()+"*****"+key);}System.out.println("系统定义的别名个数为:"+i);} catch (IOException e) {e.printStackTrace();}}public static void main(String[] args) {getTypeAlias();
}

输出结果就不贴出来了,有点长,可以自行运行。通过运行结果可以发现系统自定义的别名一共有72个(这72个别名的使用不区分大小写)。所以我们可以使用别名代替冗长的全限定名,比如在MyBatis的映射文件中,我们设置一个SQL语句的参数类型或返回类型的时候,如果这个类型是字符串,我们完全可以用string代替java.lang.String。但是这就会有一个问题,我怎么知道哪个类型的别名是什么呢?在不知道的情况下有两种方式可以知道:

保险的方法:将系统别名打印出来,或者找官方文档查询;
寻规律:其实从上面的结果可以发现一个规律,就是如果类名以大写开头,则只要将大写变为小写就是该类的别名;而如果类名本来就是小写,只需要在小写前面加上下划线即可。

4.2、自定义别名

我们在平时的开发中,系统中会有大量的类,比如User类,需要对其进行反复的使用,而这些类系统并没有给我们取别名,难道我们要反复的编写很长的全限定名吗?NO,Mybatis给我们提供了用户自定义别名的规则,我们可以通过配置文件、包扫描或者注解进行注册。下面来介绍一下如何使用:

①、使用配置文件的typeAliases属性

<!--配置别名-->
<typeAliases><!--对类单独进行别名设置  --><typeAlias alias="user" type="com.thr.pojo.User"></typeAlias><typeAlias alias="student" type="com.thr.pojo.Student"></typeAlias>
</typeAliases>

这样我们就为两个类定义好了别名,但是这种方式有个缺点,如果多个类需要配置别名时就显得很麻烦,所以这种方式显然不行。

②、通过package自动扫描

<!--配置别名-->
<typeAliases><!-- 对包进行扫描,可以批量进行别名设置,设置规则是:获取类名称,将其第一个字母变为小写 --><package name="com.thr.pojo1"/><package name="com.thr.pojo2"/><package name="com.thr.pojo3"/>
</typeAliases>

这种方式会为扫描到的包下的所有类起一个别名,别名的命名规则为,将类名的第一个字母变为小写作为别名,比如com.thr.pojo.User变为别名为user。但是使用这种方式还有缺点,就是如果两个不同的包下出现了同名的类,那么在扫描的时候就会出现异常(通常不会出现这种情况)。这个时候可以通过注解@Alians(“user1”)来进行区分。

③、通过注解

   这种方式比较简单,只要在对应包下的对应类上面使用注解@Alias("别名")即可,如下:
package com.thr.pojo;
import org.apache.ibatis.type.Alias;@Alias("user")
public class User {省略......
}

这样就能够避免因为避免重复而导致扫描失败的问题。

5、typeHandlers属性(了解)

typeHandlers叫类型处理器,在JDBC中,需要在PreparedStatement中设置预编译SQL所需的参数。在执行SQL后,会根据结果集ResultSet对象得到数据库的数据,需要将数据库中的类型和java中字段的类型进行转换一样,这些操作在MyBatis中通过typeHandler来实现。在typeHandler中,包含有javaType和jdbcType两种类型,其中javaType用来定义Java类型,jdbcType用来定义数据库类型,那么typeHandler的作用就是承担javaType和jdbcType两种类型的转换,如下图所示。

在这里插入图片描述

MyBatis中的typeHandlers存在系统定义的和自定义两种,MyBatis会根据javaType和jdbcType来决定采用哪个typeHandler来处理这些转换规则,而且系统定义的能满足大部分需求,但是有些情况是不够用的,比如我们的特殊转换规则,枚举类型,这时我们就需要自定义的typeHandlers了。下面分别介绍这两种typeHandler的使用。

5.1、系统定义的typeHandler

Mybatis内部定义了许多有用的typeHandler,我们可以参考Mybatis的官方文档查看:https://mybatis.org/mybatis-3/zh/configuration.html#typeHandlers,也可以自己通过程序代码进行打印,代码如下:

/*** 获取类型处理器*/
public static void getTypeHandlers() {//SqlSession代码省略......TypeHandlerRegistry typeHandlerRegistry = sqlSession.getConfiguration().getTypeHandlerRegistry();Collection<TypeHandler<?>> handlers =  typeHandlerRegistry.getTypeHandlers();System.out.println(handlers.size());int i = 0;for (TypeHandler<?> typeHandler : handlers) {System.out.println(++i+"*****"+typeHandler.getClass().getName());}
}

执行结果就不列出来了,Mybatis一共定义了39个类型处理器。在大部分情况下我们不需要显示的声明JavaType和jdbcType,因为Mybatis会自动探测到。

在Mybatis中typeHandler都需要实现接口org.apache.ibatis.type.TypeHandler,所以我们来看看这个接口长啥样。源代码如下:

public interface TypeHandler<T> {void setParameter(PreparedStatement var1, int var2, T var3, JdbcType var4) throws SQLException;T getResult(ResultSet var1, String var2) throws SQLException;T getResult(ResultSet var1, int var2) throws SQLException;T getResult(CallableStatement var1, int var2) throws SQLException;
}

简单介绍一下内部定义了内容:

其中T表示泛型,专指JavaType,比如我们需要String类型的参数,那么实现类就是可以写成implement TypeHandler。

setParameter方法,是使用typeHandler通过PreparedStatement对象进行设置SQL参数的时候使用的具体方法,其中i是请查收在SQL的下标,parameter是参数,jdbcType为数据库类型。

其中三个getResult的方法,它的作用是从JDBC结果集中获取数据进行转换,要么使用列名,要么使用下标来获取数据库的数据,其中最后一个方法是存储过程专用的方法。

既然学习了TypeHandler接口,那么接着来学习它的实现了BaseTypeHandler类。源代码如下(只贴出少量):

public abstract class BaseTypeHandler<T> extends TypeReference<T> implements TypeHandler<T> {......public void setParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException {......}public T getResult(ResultSet rs, String columnName) throws SQLException {......}public T getResult(ResultSet rs, int columnIndex) throws SQLException {......}public T getResult(CallableStatement cs, int columnIndex) throws SQLException {......}public abstract void setNonNullParameter(PreparedStatement var1, int var2, T var3, JdbcType var4) throws SQLException;public abstract T getNullableResult(ResultSet var1, String var2) throws SQLException;public abstract T getNullableResult(ResultSet var1, int var2) throws SQLException;public abstract T getNullableResult(CallableStatement var1, int var2) throws SQLException;}

简单分析一下:

  • setParameter方法,当参数parameter和jdbcType同时为空时,Mybatis将抛出异常。如果能目前jdbcType,则会继续空设置;如果参数不为空,那么他将曹勇setNonNullParameter方法设置参数。
  • getResult方法,非空结果集是通过getNullableResult方法获取的。如果判断为空,则返回null。
  • getNullableResult方法用于存储过程。
    在Mybatis中使用最多的typeHandler为StringTypeHandler。它用于字符串的转换,所以我们来学习一下。源代码如下:
public class StringTypeHandler extends BaseTypeHandler<String> {public StringTypeHandler() {}public void setNonNullParameter(PreparedStatement ps, int i, String parameter, JdbcType jdbcType) throws SQLException {ps.setString(i, parameter);}public String getNullableResult(ResultSet rs, String columnName) throws SQLException {return rs.getString(columnName);}public String getNullableResult(ResultSet rs, int columnIndex) throws SQLException {return rs.getString(columnIndex);}public String getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {return cs.getString(columnIndex);}
}

从上述代码可以看出它继承了BaseTypeHandler类,并且实现了BaseTypeHandler的4个抽象方法,方法如下:

  • setNonNullParameter:这个方法是用来将javaType转换成jdbcTpe。
  • getNullableResult:这个方法用来将从结果集根据列名称获取到的数据的jdbcType转换成javaType。
  • getNullableResult:这个方法用来将从结果集根据列索引获取到的数据的jdbcType转换成javaType。
  • getNullableResult:这个方法用在存储过程中。

这里Mybatis把JavaType和jdbcType进行互换,那么他们是怎么进行注册的呢?在Mybatis中采用TypeHandlerRegistry类对象的register方法来进行则。

public TypeHandlerRegistry(Configuration configuration) {......this.register((Class)Boolean.class, (TypeHandler)(new BooleanTypeHandler()));this.register((Class)Boolean.TYPE, (TypeHandler)(new BooleanTypeHandler()));this.register((JdbcType)JdbcType.BOOLEAN, (TypeHandler)(new BooleanTypeHandler()));this.register((JdbcType)JdbcType.BIT, (TypeHandler)(new BooleanTypeHandler()));......}

这样就是实现了用代码的形式注册typeHandler。但是注意,自定义的typeHandler一般不会使用代码进行注册,而是通过配置或者扫描,使用下面我们来学习如何自定义typeHandler。

5.2、自定义typeHandler

我们知道在大部分场景下,Mybatis的typeHandler都能应付,但是有时候也会不够用,比如枚举类型,这个时候就需要自定义typeHandler来进行处理了。从系统定义的typeHandler可以知道,要实现typeHandler就需要去实现接口typeHandler或者实现baseTypeHandler。

下面我们使用实现TypeHandler接口的方式创建一个MyTypeHandler,用来完成javaType中的String类型与jdbcType中的类型之间的转化。

public class MyTypeHandler implements TypeHandler<String> {Logger log = Logger.getLogger(MyTypeHandler.class);@Overridepublic void setParameter(PreparedStatement ps, int i, String parameter, JdbcType jdbcType) throws SQLException {log.info("设置string参数:"+parameter);ps.setString(i,parameter);}@Overridepublic String getResult(ResultSet rs, String columnName) throws SQLException {String result = rs.getString(columnName);log.info("读取string参数1:"+result);return result;}@Overridepublic String getResult(ResultSet rs, int columnIndex) throws SQLException {String result = rs.getString(columnIndex);log.info("读取string参数2:"+result);return result;}@Overridepublic String getResult(CallableStatement cs, int columnIndex) throws SQLException {String result = cs.getString(columnIndex);log.info("读取string参数3:"+result);return result;}
}

我们定义的泛型为String则表示我们要把数据库类型的数据转化为String类型,然后实现设置参数和获取结果集的方法。但是这个时候还没有启动typeHandler。还需要在配置文件中配置一下。

<typeHandlers><typeHandler jdbcType="VARCHAR" javaType="string"handler="com.typeHandler.MyTypeHandler"/>
</typeHandlers>

配置完成之后系统才会读取它,这样就注册完毕了,当JavaType和jdbcType能与MyTypeHandler对应的时候,它就会启动MyTypeHandler。我们有两种方式来使用自定义的typeHandler。

<!-- 模糊查询,根据username字段查询用户-->
<select id="selectUserByName" parameterType="string" resultType="user">select * from t_user where username like concat ('%',#{username,typeHandler=com.typeHandler.MyTypeHandler},'%');
</select>

或者:

<select id="selectUserByName" parameterType="string" resultType="user">select * from t_user where username like concat ('%',#{username,javaType=string,jdbcType=VARCHAR},'%');
</select>

注意,要么指定了与自定义typeHandler一致的jdbcType和JavaType,要么直接使用typeHandler指定的具体实现类。在一些因为数据库返回为空导致无法判定采用哪个typeHandler来处理,而又没有注册对应的JavaType的typeHandler是,Mybatis无法找到使用哪个typeHandler来转换数据。

在这里插入图片描述
有时候类很多的时候,我们还可以采用包扫描的方式。

<typeHandlers><package name="com.typeHandler"/>
</typeHandlers>

但是这样会无法指定jdbcType和JavaType,不过我们可以通过注解来处理它们,我们把MyTypeHandler类修改一些即可。

@MappedTypes(String.class)
@MappedJdbcTypes(JdbcType.VARCHAR)
public class MyTypeHandler implements TypeHandler<String> {......
}

最后:在我们的日常开发中,一般都不需要定义,使用默认的就可以,除非是像枚举这种特殊类型就需要自己实现。

6、objectFacotry属性(了解)

objectFacotry表示为对象工厂。对象工厂我们只需了解即可,因为到时候与spring整合后,都会由spring来管理。

我们在使用MyBatis执行查询语句的时候,通常都会有一个返回类型,这个是在mapper文件中给sql增加一个resultType(或resultMap)属性进行控制。resultType和resultMap都能控制返回类型,只要定义了这个配置就能自动返回我想要的结果,于是我就很纳闷这个自动过程的实现原理,想必大多数人刚开始的时候应该也有和我一样的困惑和好奇,那么今天我就把自己的研究分享一下。在JDBC中查询的结果会保存在一个结果集中,其实MyBatis也是这个原理,只不过MyBatis在创建结果集的时候,会使用其定义的对象工厂DefaultObjectFactory来完成对应的工作。

7、plugins属性(了解)

插件是Mybatis中最强大和灵活的组件,同时也是最复杂、最难使用的组件,并且它十分的危险,因为它将覆盖Mybatis底层对象的核心方法和属性。如果操作不当将产生非常严重的后果,甚至是摧毁Mybatis框架,所以我们在不了解Mybatis的底层结构的情况下,千万不要去碰这个插件属性。如果你想研究一下插件,那么前提是要清楚掌握Mybatis底层的结构和运行原理,否则将难以安全高效的使用它。而我们平时使用Mybatis的常规功能完全满足日常的开发,所以这里就不介绍了,有兴趣的可以自行去学习。

8、environments属性

environments属性表示的是运行环境,主要的作用是配置数据库的一些信息,我们可以配置多个数据库,但只能选择一个。它里面分为两个可配置的元素:事务管理器(transactionManager)、数据源(DataSource)。而在我们的日常开发中,这些都会交给Spring来管理,不用在全局配置中编写,这些会在后面Mybatis整合Spring中进行讲解。我们先来看看environments环境配置的配置代码吧。

<configuration><!-- 配置环境.--><environments default="development"><!-- id属性必须和上面的default一致 --><environment id="development"><!--配置事务的类型--><transactionManager type="JDBC"></transactionManager><!--dataSource 元素使用标准的 JDBC 数据源接口来配置 JDBC 连接对象源 --><dataSource type="POOLED"><!--配置连接数据库的4个基本信息--><property name="driver" value="${database.driver}"/><property name="url" value="${database.url}"/><property name="username" value="${database.username}"/><property name="password" value="${database.password}"/></dataSource></environment></environments>
</configuration>

下面我们主要来详细介绍 transactionManager 和 DataSource 这两个元素。

8.1、transactionManager(事务管理)

在MyBatis中,transactionManager提供了两个实现类,它们都需要实现接口Transaction,所以我们可以查看以下Transaction的源代码:

public interface Transaction {Connection getConnection() throws SQLException;void commit() throws SQLException;void rollback() throws SQLException;void close() throws SQLException;Integer getTimeout() throws SQLException;
}

从上面的方法可知,它主要的工作就是提交(commit)、回滚(rollback)、关闭(close)数据库的事务。MyBatis中为Transaction接口提供了两个实现类,分别是 JdbcTransaction 和 ManagedTransaction。如下图所示:

在这里插入图片描述在这里插入图片描述

并且分别对应着 JdbcTransactionFactory 和 ManagedTransactionFactory 两个工厂,这两个工厂实现了 TransactionFactory 这个接口,当我们在配置文件中通过 transactionManager 的type属性配置事务管理器类型的时候,Mybatis就会自动从对应的工厂获取实例。我们可以把事务管理器配置成为以下两种方式:

<transactionManager type="JDBC"/>
<transactionManager type="MANAGED"/>

下面说一下这两者的区别:

JDBC:使用JdbcTransactionFactory工厂生成的JdbcTransaction对象实现,以JDBC的方式进行数据库的提交、回滚等操作。
MANAGED:使用ManagedTransactionFactory工厂生成的ManagedTransaction对象实现,它的提交和回滚不需要任何操作,而是把事务交给容器进行处理,默认情况下会关闭连接,如果不希望默认关闭,只要将其中的closeConnection属性设置为false即可。

<transactionManager type="MANAGED"><property name="closeConnection" value="false"/>
</transactionManager>

在测试的过程中发现的最明显的区别就是,如果我使用JDBC的事务处理方式,当我向数据库中插入一条数据时,在调用完插入接口执行SQL之后,必须 执行sqlSession.commit();进行提交,否则虽然插入成功但是数据库中还是看不到刚才插入的数据;而使用MANAGED方式就不一样了,只需调用接口即可,无需手动提交。

当然除了使用默认的,我们还可以根据需要自定义一个事务管理器,需要以下三步:

第一步:创建一个自定义事务工厂MyTransactionFactory,需要实现TransactionFactory接口,代码如下:

/*** 创建自定义事务工厂*/
public class MyTransactionFactory implements TransactionFactory {@Overridepublic void setProperties(Properties props) {}@Overridepublic Transaction newTransaction(Connection connection) {//后面我们会创建这个类,它自定义的事务类return new MyTransaction(connection);}@Overridepublic Transaction newTransaction(DataSource dataSource, TransactionIsolationLevel level, boolean b) {return new MyTransaction(dataSource,level,b);}
}

这里就实现了TransactionFactory所有定义的工厂方法了,这时还要一个自定义的事务类,下面我们来创建。

第二步:创建一个自定义事务类MyTransaction,用来实现Transaction接口,代码如下。

MyTransaction
第三步:配置自定义事务管理器

<transactionManager type="com.transaction.MyTransactionFactory"/>

注意:这个地方配置的是自定义的工厂类,而不是事务管理类,因为mybatis是根据配置的工厂获取具体实例对象的。

8.2、DataSource(数据源)

在Mybatis中,数据库是通过PooledDataSourceFactory、UnpooledDataSourceFactory和JndiDataSourceFactory三个工厂类来提供,前两者分别产生PooledDataSource和UnpooledDataSource类对象,第三个则会根据JNDI的信息拿到外部容器实现的数据库连接对象,但是不管怎样,它们最后都会生成一个实现了DataSource接口的数据库连接对象。

因为有三种数据源,所以它们的配置信息如下:

<dataSource type="POOLED">
<dataSource type="UNPOOLED">
<dataSource type="JNDI">

下面介绍一下这三种数据源的意义:

①、UNPOOLED

UNPOOLED采用非数据库池的管理方式,每次请求都会新建一个连接,所以性能不是很高,使用这种数据源的时候,UNPOOLED类型的数据源可以配置以下属性:

  • driver:数据库驱动名
  • url:数据库连接URL
  • username:用户名
  • password:密码
  • defaultTransactionIsolationLevel:默认的事务隔离级别,如果要传递属性给驱动,则属性的前缀为driver

②、POOLED

POOLED采用连接池的概念将数据库链接对象Connection组织起来,可以在初始化时创建多个连接,使用时直接从连接池获取,避免了重复创建连接所需的初始化和认证时间,从而提升了效率,所以这种方式比较适合对性能要求高的应用中。除了UNPOOLED中的配置属性之外,还有下面几个针对池子的配置:

  • poolMaximumActiveConnections:任意时间都会存在的连接数,默认值为10
  • poolMaxmumIdleConnections:可以空闲存在的连接数
  • poolMaxmumCheckoutTime:在被强制返回之前,检查出存在空闲连接的等待时间。即如果有20个连接,只有一个空闲,在这个空闲连接被找到之前的等待时间就用这个属性配置。
    poolTimeToWait:等待一个数据库连接成功所需的时间,如果超出这个时间则尝试重新连接。
    还有其他的一些配置,不详述了。

③、JNDI

JNDI数据源JNDI的实现是为了能在如EJB或应用服务器这类容器中使用,容器可以集中或在外部配置数据源,然后放置一个JNDI上下文的引用。这种数据源只需配置两个属性:

  • initial_context:用来在InitialContext中寻找上下文。可选,如果忽略,data_source属性将会直接从InitialContext中寻找;
  • data_source:引用数据源实例位置上下文的路径。当提供initial_context配置时,data_source会在其返回的上下文进行查找,否则直接从InitialContext中查找。

除了上述三种数据源之外,Mybatis还提供第三方数据源,如DBCP,但是需要我们自定义数据源工厂并进行配置,这一点暂时不做研究。

9、databaseIdProvider属性(了解)

databaseIdProvider元素主要是为了支持不同厂商的数据库,这个元素不常用。比如有的公司内部开发使用的数据库都是MySQL,但是客户要求使用Oracle,那麻烦了,因为Mybatis的移植性不如Hibernate,但是Mybatis也不会那么蠢,在Mybatis中我们可以使用databaseIdProvider这个元素实现数据库兼容不同厂商,即配置多中数据库。

下面以Oracle和MySQL两种数据库来介绍它们,要配置的属性如下:

<!--数据库厂商标示 -->
<databaseIdProvider type="DB_VENDOR"><property name="Oracle" value="oracle"/><property name="MySQL" value="mysql"/><property name="DB2" value="d2"/>
</databaseIdProvider>

databaseIdProvider的type属性是必须的,不配置时会报错。上面这个属性值使用的是VendorDatabaseIdProvider类的别名。

property子元素是配置一个数据库,其中的name属性是数据库名称,value是我们自定义的别名,通过别名我们可以在SQL语句中标识适用于哪种数据库运行。如果不知道数据库名称,我们可以通过以下代码获取connection.getMetaData().getDatabaseProductName()来获取,代码如下:

/*** 获取数据库名称*/
public static void getDbInfo() {SqlSession sqlSession = null;Connection connection = null;try {InputStream stream = Resources.getResourceAsStream("mybatis-config.xml");SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(stream);sqlSession = sqlSessionFactory.openSession();connection = sqlSession.getConnection();String dbName = connection.getMetaData().getDatabaseProductName();String dbVersion = connection.getMetaData().getDatabaseProductVersion();System.out.println("数据库名称是:" + dbName + ";版本是:" + dbVersion);} catch (SQLException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();}
}

我的输出结果是:数据库名称是:MySQL;版本是:5.7.28-log

然后下面我们就可以在自己的sql语句中使用属性databaseId来标示数据库类型了。配置如下:

<!-- 查询所有用户 -->
<select id="selectAllUser" resultType="com.thr.User" databaseId="oracle">select * from t_user
</select>

注意:在上面的SQL中,我配置的databaseId是oracle,但是我的实际的数据库是mysql,结果最后肯定会报BindingException异常(但是我这个代码不知道为什么报的另一种异常,就很奇怪,百度了好久无果)。如果我们把databaseId=”oracle”换成mysql的,就能获取正确结果了。除上述方法之外,我们还可以不在SQL中配置databaseId,这样mybatis会使用默认的配置,也是可以成功运行的。

过上面的实践知道了:使用多数据库SQL时需要配置databaseIdProvider 属性。当databaseId属性被配置的时候,系统会优先获取和数据库配置一致的SQL,否则取没有配置databaseId的SQL,可以把它当默认值;如果还是取不到,就会抛出异常。

除了系统自定义的标识外,我们也可以自定义一个规则,需要实现MyBatis提供的DatabaseIdProvider接口,如下:

MyDatabaseIdProvider
然后在databaseIdProvider中做如下配置:

<!--数据库厂商标示 -->
<databaseIdProvider type="com.databaseidprovider.MyDatabaseIdProvider" />

property属性可以不做配置了,其它都一样。

10、mappers属性

mapper属性是用来加载映射文件的,也就是加载我们配置的SQL映射文件。它有四种方式加载:

用文件路径引入
使用URL方式引入
用类注册引入
用包名引入(推荐)

10.1、用文件路径引入

<mappers><mapper resource="com/thr/mapper/UserMapper.xml" /><mapper resource="com/thr/mapper/StudentMapper.xml" /><mapper resource="com/thr/mapper/TeacherMapper.xml" />
</mappers>

这种方式是相对路径,相对于项目目录下,所以得用 / 分开。

10.2、使用URL方式引入

<mappers><mapper url="D:/mappers/UserMapper.xml" /><mapper url="D:/mappers/StudentMapper.xml" />
</mappers>

这种方式是绝对路径,就是从我们的磁盘读取映射文件,一般不会使用这种方式。

10.3、用类注册引入

<mappers><mapper class="com.thr.mapper.UserMapper" /><mapper class="com.thr.mapper.StudentMapper" /><mapper class="com.thr.mapper.TeacherMapper" />
</mappers>

这种方式使用Mapper接口的全限定名,不用管路径问题,让Mybatis自己通过全限定名去找映射文件。但是前提是Mapper接口的名称必须与映射文件的名称相同,并且要在同一个包名下,否则会找不到。比如:UserMapper.java(接口)—UserMapper.xml(映射文件)。关于Mapper接口对应的Mapper映射文件后面会详细介绍。

10.4、用包名引入(推荐)

<mappers><package name="com.thr.mapper"/>
</mappers>

推荐使用这种方式,表示引入该包下的所有mapper接口,这里引入了com.thr.mapper包下的所有接口文件,然后让Mybatis自己通过全限定名去找映射文件。

注意:这种方式的要求同样是Mapper接口和Mapper的映射文件的名称要相同,并且要放在相同的包名下,否则会导致找不到。

相关文章:

Mybatis3详解 之 全局配置文件详解

1、全局配置文件 前面我们看到的Mybatis全局文件并没有全部列举出来&#xff0c;所以这一章我们来详细的介绍一遍&#xff0c;Mybatis的全局配置文件并不是很复杂&#xff0c;它的所有元素和代码如下所示&#xff1a; <?xml version"1.0" encoding"UTF-8&…...

力扣-345.反转字符串中的元音字母

Idea 将s中的元音字母存在字符串sv中&#xff0c;并且使用一个数组依次存储元音字母的下标。 然后将字符串sv进行反转&#xff0c;并遍历元音下标数组&#xff0c;将反转后的字符串sv依次插入到源字符串s中 AC Code class Solution { public:string reverseVowels(string s) {…...

643. 子数组最大平均数I(滑动窗口)

目录 一、题目 二、代码 一、题目 643. 子数组最大平均数 I - 力扣&#xff08;LeetCode&#xff09; 二、代码 class Solution { public:double findMaxAverage(vector<int>& nums, int k) {double Average INT_MIN;double sum nums[0];int left 0, right 0…...

Java 21 新特性:虚拟线程(Virtual Threads)

I often take exercise. Why only yesterday I had breakfast in bed. 在Java 21中&#xff0c;引入了虚拟线程&#xff08;Virtual Threads&#xff09;来简化和增强并发性&#xff0c;这使得在Java中编程并发程序更容易、更高效。 虚拟线程&#xff0c;也称为“用户模式线程…...

18scala笔记

Scala2.12 视频地址 1 入门 1.1 发展历史 … 1.2 Scala 和 Java Scala Java 编写代码使用scalac编译成.class字节码文件scala .class文件 执行代码 1.3 特点 1.4 安装 视频地址 注意配置好环境变量 简单代码 1.5 编译文件 编译scala文件会产生两个.class文件 使用java…...

【LeetCode周赛】LeetCode第365场周赛

目录 有序三元组中的最大值 I有序三元组中的最大值 II无限数组的最短子数组 有序三元组中的最大值 I 给你一个下标从 0 开始的整数数组nums。 请你从所有满足 i < j < k 的下标三元组 (i, j, k) 中&#xff0c;找出并返回下标三元组的最大值。如果所有满足条件的三元组的…...

响应式设计的实现方式

一. 什么是响应式 响应式网站设计是一种网络页面设计布局。页面的设计与开发应当根据用户行为以及设备环境&#xff08;系统平台&#xff0c;屏幕尺寸&#xff0c;屏幕定向等&#xff09;进行相应的响应和调整。 响应式网站常见特点&#xff1a; 1. 同时适配PC平板手机。 2…...

PHP 反序列化漏洞:__PHP_Incomplete_Class 与 serialize(unserialize($x)) !== $x;

文章目录 参考环境声明__PHP_Incomplete_Class灵显为什么需要 __PHP_Incomplete_Class&#xff1f;不可访问的属性 serialize(unserialize($x)) $x;serialize(unserialize($x)) ! $x;雾现__PHP_Incomplete_Class 对象与其序列化文本的差异试构造 __PHP__Incomplete_Class 对象…...

TempleteMethod

TempleteMethod 动机 在软件构建过程中&#xff0c;对于某一项任务&#xff0c;它常常有稳定的整体操作结构&#xff0c;但各个子步骤却有很多改变的需求&#xff0c;或者由于固有的原因 &#xff08;比如框架与应用之间的关系&#xff09;而无法和任务的整体结构同时实现。如…...

1558. 得到目标数组的最少函数调用次数

1558. 得到目标数组的最少函数调用次数 原题链接&#xff1a;完成情况&#xff1a;解题思路&#xff1a;参考代码&#xff1a; 原题链接&#xff1a; 1558. 得到目标数组的最少函数调用次数 https://leetcode.cn/problems/minimum-numbers-of-function-calls-to-make-target…...

子域名扫描, 后台扫描

子域名和后台扫描 一, 子域名扫描 在渗透测试的早期阶段&#xff0c;子域名扫描是一个非常重要的步骤&#xff0c;它有助于识别目标组织的网络结构和在线资源。 子域名扫描应该在获得适当的权限和授权的情况下进行&#xff0c;以确保所有活动都是合法和合规的。 1. 原因与目…...

毛玻璃带有光影效果的卡片

效果展示 页面结构组成 从效果展示可以看到&#xff0c;页面的主要元素是卡片&#xff0c;卡片的内容呈现上都是比较常规的布局&#xff0c;只是卡片上带有光影效果。 CSS / JavaScript 知识点 transformVanillaTilt.js 使用 页面基础结构实现 <div class"contain…...

【Java】面向过程和面向对象思想||对象和类

1.面向过程和面向对象思想 两者都贯穿于软件分析、设计和开发的各个阶段&#xff0c;对应面向对象就分别称为面向对象的分析&#xff08;OOA&#xff09;、面向对象的设计&#xff08;OOD&#xff09;和面向对象的编程&#xff08;OOP&#xff09;。C语言是一种典型的面向过程语…...

孤举者难起,众行者易趋,openGauss 5.1.0版本正式发布!

&#x1f4e2;&#x1f4e2;&#x1f4e2;&#x1f4e3;&#x1f4e3;&#x1f4e3; 哈喽&#xff01;大家好&#xff0c;我是【IT邦德】&#xff0c;江湖人称jeames007&#xff0c;10余年DBA及大数据工作经验 一位上进心十足的【大数据领域博主】&#xff01;&#x1f61c;&am…...

软考——软件设计师中级2023年11月备考(1.计算机组成原理)

一、计算机组成原理 1.数据的表示 1.1 十进制转R进制 方法&#xff1a;对十进制数除R取余&#xff0c;最后对余数取倒序 如&#xff1a; 1.2 原码反码补码 1.3 浮点数 1.4 校验码 —— 海明码 &#xff08;非重点&#xff0c;了解即可&#xff09; 海明码的构成方法&…...

前端JavaScript入门到精通,javascript核心进阶ES6语法、API、js高级等基础知识和实战 —— Web APIs(四)

思维导图 一、日期对象 1.1 实例化 实例化&#xff0c;默认得到当前时间&#xff0c;也可以指定时间 1.2 日期对象方法 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta http-equiv"X-UA-Compatible&q…...

【前端】HTML5 Audio 预加载 按照队列顺序播放音频, 可以陆续往队列中加内容

【前端】Audio 按照队列顺序播放音频, 可以陆续往队列中加内容 var 音频库 {} var 当前音频集合 [] /*** 将文本添加到队列中* 持续去播放* 播放过的音频会自动从队列中删除* * 已规划* 要保障同时进行加载的数据不能超过5个(线程池 5)* * param 文本*/播放音频队列(文本){i…...

【单片机】13-实时时钟DS1302

1.RTC的简介 1.什么是实时时钟&#xff08;RTC&#xff09; &#xff08;rtc for real time clock) &#xff08;1&#xff09;时间点和时间段的概念区分 &#xff08;2&#xff09;单片机为什么需要时间点【一定的时间点干什么事情】 &#xff08;3&#xff09;RTC如何存在于…...

springboot和vue:十三、VueX简介与安装与推荐视频+前端数据模拟MockJS

VueX简介与安装与推荐视频 VueX用于管理分散在vue各个组件中的数据。每一个VueX的核心都是一个store&#xff0c;当store中的状态发生变化时&#xff0c;与之绑定的视图也将重新渲染。store中的状态不允许被直接修改&#xff0c;只能显示提交mutationVueX中有五个重要的概念&a…...

[React] Zustand状态管理库

文章目录 1.Zustand介绍2.创建一个store3.使用方法3.1 获取状态3.2 更新状态3.3 访问存储状态3.4 处理异步数据3.5 在状态中访问和存储数组3.6 持续状态 4.总结 1.Zustand介绍 状态管理一直是现代程序应用中的重要组成部分, Zustand使用 hooks 来管理状态无需样板代码。 更少…...

【ChatGPT】ChatGPT发展历史

更多优质文章请看底部&#xff1a;ChatGPT与日本首相交流核废水事件-精准Prompt... hello&#xff0c;我是小索奇&#xff0c;在AI日益庞大的环境下&#xff0c;接下来将为大家不断的ChatGPT学习 ChatGPT使用了 Transformer 结构&#xff0c;建立在 OpenAI的 GPT-3.5 大型语言模…...

分布式文件存储系统Minio实战

分布式文件系统应用场景 互联网海量非结构化数据的存储需求电商网站&#xff1a;海量商品图片视频网站&#xff1a;海量视频文件网盘 : 海量文件社交网站&#xff1a;海量图片 1. Minio介绍 MinIO 是一个基于Apache License v2.0开源协议的对象存储服务。它兼容亚马逊S3云存…...

【MySQL】MySQL 官方安装包形式

MySQL 官方提供3种包&#xff1a; 1. 源码包 mysql-5.7.42.tar.gz mysql-5.7.42-aarch64.tar.gz http://dev.mysql.com/get/Downloads/MySQL-5.6/mysql-5.6.34.tar.gz http://dev.mysql.com/get/Downloads/MySQL-5.7/mysql-5.7.42.tar.gz需要用户根据自己的CPU架构选择对应的…...

使用sqlmap获取数据步骤

文章目录 1.使用sqlmap获取所有数据库2.使用sqlmap获取当前连接数据库3.使用sqlmap获取当前数据库下所有表名4.使用sqlmap获取当前数据库下某个表下所有列名5.使用sqlmap获取当前数据库下某个表下指定字段的数据6.测试当前用户是否是管理员7.使用burpsqlmap批量检测8.脱库命令9…...

[论文笔记]GLM

引言 今天带来论文GLM: General Language Model Pretraining with Autoregressive Blank Infilling的笔记。论文中文标题为 通用语言模型预训练与自回归填空。 有很多不同类型的预训练架构,包括自编码模型(BERT、RoBERTa、ALBERT)、自回归模型(GPT系列)以及编码器-解码器模型…...

漏洞扫描环境:win10系统用VMware Workstation打开虚拟机若干问题

win10系统用VMware Workstation打开虚拟机若干问题 一 .VMware打开虚拟机就蓝屏重启怎么解决&#xff1f;一. VMware打开虚拟机就蓝屏重启怎么解决&#xff1f;方法一&#xff1a;1、同时按下CTRLSHIFTESC打开任务管理器功能&#xff0c;之后依次点击-详细信息-性能后出现下列界…...

OpenCV实现模板匹配和霍夫线检测,霍夫圆检测

一&#xff0c;模板匹配 1.1代码实现 import cv2 as cv import numpy as np import matplotlib.pyplot as plt from pylab import mplmpl.rcParams[font.sans-serif] [SimHei]#图像和模板的读取 img cv.imread("cat.png") template cv.imread(r"E:\All_in\o…...

消息队列实现进程之间通信方式

1. snd 源代码 #include <myhead.h>#define ERR_MSG(msg) do{\fprintf(stderr,"__%d__:",__LINE__);\perror(msg);\ }while(0)typedef struct{ long msgtype; //消息类型char data[1024]; //消息正文 }Msg;#define SIZE sizeof(Msg)-sizeof(long)int main(i…...

用简单例子讲清楚webgl模板测试

文章目录 搭建简易的webgl环境绘制简单三角形&#xff08;不带stencilTest)绘制另一个三角形&#xff08;不带模板测试&#xff09;加入模板测试总结调参练习 搭建简易的webgl环境 一直以来&#xff0c;我只是想通过搭建纯webgl环境&#xff0c;进行开发&#xff0c;来清楚地了…...

区块链(8):p2p去中心化之websoket服务端实现业务逻辑

1 业务逻辑 例如 peer1和peer2之间相互通信 peer1通过onopen{ write(Mesage(QUERY_LATEST))} 向peer2发送消息“我要最新的区块”。 peer2通过onMessage收到消息,通过handleMessage方法对消息进行处理。 handleMessage根据消息类型进行处理 RESPONSE_BLOCKCHAIN:返回区块链…...