4、配置解析
4.1、核心配置文件
-
SqlMapConifg.xml
(可叫别的): -
MyBatis 的配置文件包含了会深深影响 MyBatis 行为的设置和属性信息
properties(属性)
settings(设置)
typeAliases(类型别名)
typeHandlers(类型处理器)
objectFactory(对象工厂)
plugins(插件)
environments(环境配置)
environment(环境变量)
transactionManager(事务管理器)
dataSource(数据源)
databaseIdProvider(数据库厂商标识)
mappers(映射器)
4.2、环境配置(Environments)
-
MyBatis 可以配置成适应多种环境,这种机制有助于将 SQL 映射应用于多种数据库之中, 现实情况下有多种理由需要这么做。
-
开发、测试和生产环境需要有不同的配置
-
想在具有相同 Schema 的多个生产数据库中 使用相同的 SQL 映射
-
不过要记住:尽管可以配置多个环境,但每个 SqlSessionFactory 实例只能选择一种环境。
<environments default="mysql">
<!--配置mysql环境-->
<environment id="mysql">
<!--事务管理器-->
<transactionManager type="jdbc"/>
<!--数据源-->
<dataSource type="pooled">
<property name="driver" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</dataSource>
</environment>
<environment id="development">
<transactionManager type="jdbc">
<property name="..." value="..."/>
</transactionManager>
<dataSource type="POOLED">
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
</environment>
</environments>
environments
的default
属性配置了默认选择的环境
4.3、属性(properties)
<properties resource="jdbcConfig.properties">
<property name="aaa" value="bbb"/>
<property name="ccc" value="ddd"/>
</properties>
- 可以配置
properties
标签的resource
属性来引入外部配置文件*.properties
- 可以在
properties
标签内添加子标签peoperty
设置属性键值对 - 如果属性同名,则外部文件的优先级较高
4.4、类型别名(typeAliases)
-
类型别名是为 Java 类型设置一个短的名字
-
它只和 XML 配置有关,存在的意义仅在于用来减少类完全限定名的冗余。例如:
<typeAliases> <typeAlias type="com.ajacker.domain.User" alias="user"/> </typeAliases>
这时我们就可以把之前配置的类名使用类型别名代替:
<!--配置查询所有--> <select id="findAll" resultType="user"> select * from user </select>
-
也可以使用
package
标签指定一个包,Mybatis
会在包下扫描类,并用首字母小写的类名作为默认别名<typeAliases> <package name="com.ajacker.domain"/> </typeAliases>
这时我们的别名就变为了
user
,达到了同样的效果这时可以通过注解
@Alias()
手动设置其它的别名:@Alias("userAccount") public class User implements Serializable { ... }
此时的别名为
“userAccount”
4.5、设置
这里只说常用的设置,具体的可以查官方文档
设置名 | 描述 | 有效值 | 默认值 |
---|---|---|---|
useGeneratedKeys | 允许 JDBC 支持自动生成主键,需要驱动支持。 如果设置为 true 则这个设置强制使用自动生成主键,尽管一些驱动不能支持但仍可正常工作(比如 Derby)。 | true | false | False |
cacheEnabled | 全局地开启或关闭配置文件中的所有映射器已经配置的任何缓存。 | true | false | true |
lazyLoadingEnabled | 延迟加载的全局开关。当开启时,所有关联对象都会延迟加载。 特定关联关系中可通过设置 fetchType 属性来覆盖该项的开关状态。 | true | false | false |
mapUnderscoreToCamelCase | 是否开启自动驼峰命名规则(camel case)映射,即从经典数据库列名 A_COLUMN 到经典 Java 属性名 aColumn 的类似映射。 | true | false | False |
<settings>
<setting name="cacheEnabled" value="true"/>
<setting name="lazyLoadingEnabled" value="true"/>
<setting name="multipleResultSetsEnabled" value="true"/>
<setting name="useColumnLabel" value="true"/>
<setting name="useGeneratedKeys" value="false"/>
<setting name="autoMappingBehavior" value="PARTIAL"/>
<setting name="autoMappingUnknownColumnBehavior" value="WARNING"/>
<setting name="defaultExecutorType" value="SIMPLE"/>
<setting name="defaultStatementTimeout" value="25"/>
<setting name="defaultFetchSize" value="100"/>
<setting name="safeRowBoundsEnabled" value="false"/>
<setting name="mapUnderscoreToCamelCase" value="false"/>
<setting name="localCacheScope" value="SESSION"/>
<setting name="jdbcTypeForNull" value="OTHER"/>
<setting name="lazyLoadTriggerMethods" value="equals,clone,hashCode,toString"/>
</settings>
4.6、映射器(mappers)
可以使用以下四种方式配置映射器:
<!-- 使用相对于类路径的资源引用 -->
<mappers>
<mapper resource="org/mybatis/builder/AuthorMapper.xml"/>
<mapper resource="org/mybatis/builder/BlogMapper.xml"/>
<mapper resource="org/mybatis/builder/PostMapper.xml"/>
</mappers>
<!-- 使用完全限定资源定位符(URL) -->
<mappers>
<mapper url="file:///var/mappers/AuthorMapper.xml"/>
<mapper url="file:///var/mappers/BlogMapper.xml"/>
<mapper url="file:///var/mappers/PostMapper.xml"/>
</mappers>
<!-- 使用映射器接口实现类的完全限定类名 -->
<mappers>
<mapper class="org.mybatis.builder.AuthorMapper"/>
<mapper class="org.mybatis.builder.BlogMapper"/>
<mapper class="org.mybatis.builder.PostMapper"/>
</mappers>
<!-- 将包内的映射器接口实现全部注册为映射器 -->
<mappers>
<package name="org.mybatis.builder"/>
</mappers>
- 我们推荐使用相对于类路径的资源引用方式,如果采用完全限定类名或者包的方式配置,需要注意:
- 接口和配置文件需要同名
- 接口和配置文件在相同的路径下或在
resources
下相同的路径下
5、作用域(Scope)和生命周期
SqlSessionFactoryBuilder
- 一旦创建了
SqlSessionFactory
,就不再需要它了
SqlSessionFactory
SqlSessionFactory
一旦被创建就应该在应用的运行期间一直存在- 在应用运行期间不要重复创建多次
- 使用单例模式或者静态单例模式
SqlSession
- 每个线程都应该有它自己的
SqlSession
实例 SqlSession
的实例不是线程安全的,因此是不能被共享的- 它的最佳的作用域是请求或方法作用域
- 下面的示例是一个确保
SqlSession
关闭的标准模式:
try (SqlSession session = sqlSessionFactory.openSession()) {
// 你的应用逻辑代码
}
在你的所有的代码中一致地使用这种模式来保证所有数据库资源都能被正确地关闭
6、解决属性名和字段名不一致的问题
我们之前要求数据库的列名和实体类的属性名保持一致,因为Mybatis
会自动把名字相同进行映射,可是当我们遇到列名和属性名不同的时候,该如何处理呢?
我们将之前的例子中的id
属性替换为userId
,此时属性名和数据库中的列名不对应
6.1、ResultMap结果集映射
我们通过设置结果集映射,可以手动指定列名和实体类属性名的对应关系,此时在sql语句配置中,应将resultType
属性替换为resultMap
,并引用结果集id,例如:
<!--结果集映射-->
<resultMap id="userMap" type="user">
<result property="userId" column="id"/>
<!--这里可以不同设置,因为名字相同
<result property="birthday" column="birthday"/>
<result property="username" column="username"/>
<result property="address" column="address"/>
<result property="sex" column="sex"/>
-->
</resultMap>
<!--配置查询所有-->
<select id="findAll" resultMap="userMap">
select * from user
</select>
6.2、sql语句别名
我们修改sql语句,设置查询返回列名的别名,例如下面将id
列的别名设置为userId
,达到和属性名匹配的目的:
<!--配置查询所有-->
<select id="findAll" resultMap="userMap">
select id as userId, username, birthday, sex, address from user
</select>
7、日志
7.1、日志工厂
如果数据库出现错误,我们可以通过日志了解更详细的信息,便于排错。
我们可以通过设置属性LogImpl
来指定日志工厂:
- SLF4J
- LOG4J
- LOG4J2
- JDK_LOGGING
- COMMONS_LOGGING
- STDOUT_LOGGING:标准日志输出
- NO_LOGGING
7.2、log4j
- Log4j是Apache的一个开源项目
- 可以控制日志信息输送的目的地是控制台、文件、GUI组件,甚至是套接口服务器、NT的事件记录器、UNIX Syslog守护进程等
- 可以控制每一条日志的输出格式
- 通过定义每一条日志信息的级别,我们能够更加细致地控制日志的生成过程
- 可以通过一个配置文件来灵活地进行配置,而不需要修改应用的代码。
-
导入包
<dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.17</version> </dependency>
-
设置配置文件
# 控制日志输出的位置 log4j.rootCategory=debug,CONSOLE,LOGFILE log4j.logger.org.apache.axis.enterprise=FATAL, CONSOLE log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout log4j.appender.CONSOLE.layout.ConversionPattern=%d{ISO8601} %-6r [%15.15t] %-5p %30.30c %x - %m\n log4j.appender.LOGFILE=org.apache.log4j.FileAppender # 设置日志文件 log4j.appender.LOGFILE.File=\axis.log # 设置文件追加 log4j.appender.LOGFILE.Append=true log4j.appender.LOGFILE.layout=org.apache.log4j.PatternLayout log4j.appender.LOGFILE.layout.ConversionPattern=%d{ISO8601} %-6r [%15.15t] %-5p %30.30c %x - %m\n
-
设置日志工厂
<setting name="logImpl" value="LOG4J"/>
简单使用
-
导入包并初始化Logger对象
private static Logger logger = Logger.getLogger(MyBatisTest.class);
-
在合适的位置添加log语句
@After public void destroy() throws IOException { //提交事务 sqlSession.commit(); logger.info("SqlSession提交事务"); //6.释放资源 sqlSession.close(); in.close(); logger.info("释放资源"); }
-
查看效果
8、分页
为什么要分页?
- 减少数据的处理量,限制范围
8.1、Limit分页
语法
select * from user limit startIndex,pageSize
-
编写接口
/** * 有限制的查询所有 * @param startIndex * @param limit * @return */ List<User> findAllByLimit(@Param("startIndex") int startIndex, @Param("limit") int limit);
-
编写mapper中对应的sql语句:
<!--配置查询所有限制查询--> <select id="findAllByLimit" resultMap="userMap"> select * from user limit #{startIndex},#{limit} </select>
-
编写测试类测试:
@Test public void testFindAllByLimit(){ List<User> userList = userDao.findAllByLimit(0, 4); userList.forEach(System.out::println); }
8.2、RowBounds分页
不使用sql使用代码的形式实现分页
-
使用一般的接口:(sql语句中没有limit)
/** * 查询所有 * @return 查询结果 */ List<User> findAll();
-
mapper中对应的sql语句为:
<!--配置查询所有限制查询--> <select id="findAll" resultMap="userMap"> select * from user </select>
-
在调用处使用
RowBounds
@Test public void testFindAllByRowBounds(){ //rowBounds可以设置startIndex和pageSize RowBounds rowBounds = new RowBounds(0,4); List<User> userList = sqlSession.selectList("com.ajacker.dao.IUserDao.findAll", null,rowBounds); userList.forEach(System.out::println); }
9、使用注解开发(day01_03mybatis)
我们把之前所有的方法都用注解实现一次,删除xml的mapper文件
/** * @author ajacker * 用户的持久层接口 */ public interface IUserDao { @Results(id = "userMap", value = { @Result(property = "userId", column = "id") }) /** * 查询所有 * @return 查询结果 */ @Select({"select * from user"}) @ResultMap("userMap") List<User> findAll(); /** * 保存用户 * @param user 要保存的用户 */ @Insert("insert into user(username, address, sex, birthday) values(#{username},#{address},#{sex},#{birthday})") @SelectKey(statement = "select last_insert_id()", keyProperty = "userId", keyColumn = "id", before = false, resultType = Integer.class) void saveUser(User user); /** * 插入用户,参数用map包装 * @param map */ @Insert("insert into user(username, address, sex, birthday) values(#{userId},#{userAds},#{userSex},#{userBirth})") void saveUserUseMap(Map<String, Object> map); /** * 更新用户 * @param user */ @Update("update user set username=#{username},address=#{address},sex=#{sex},birthday=#{birthday} where id=#{id}") void updateUser(User user); /** * 根据id删除用户 * @param userId */ @Delete("delete from user where id=#{uid}") void deleteUser(int userId); /** * 根据id查询用户 * @param userId * @return */ @Select("select * from user where id=#{uid}") @ResultMap("userMap") User findById(int userId); /** * 根据名字模糊查询 * @param name * @return */ @Select("select * from user where username like #{uname}") @ResultMap("userMap") List<User> findByName(String name); /** * 查询总用户数 * @return */ @Select("select count(id) from user") int findTotal(); /** * 根据QueryVo中的条件模糊查询 * @param vo * @return */ @Select("select * from user where username like #{user.username}") @ResultMap("userMap") List<User> findUserByVo(QueryVo vo); /** * 有限制的查询所有 * @param startIndex * @param limit * @return */ @Select("select * from user limit #{startIndex},#{limit}") @ResultMap("userMap") List<User> findAllByLimit(@Param("startIndex") int startIndex, @Param("limit") int limit); }
9.1、注解设置sql语句
类型:
@Select
:和select
标签对应@Delete
:和delete
标签对应@Update
:和update
标签对应@Insert
:和insert
标签对应
9.2、注解实现ResultMap
- 在接口内使用
@Results
注解,这和我们的ResultMap
标签对应 @Results
的value
属性中填写多个@Result
注解,这和result
标签对应
@Results(id = "userMap", value = { @Result(property = "userId", column = "id") })
相当于:
<!--结果集映射--> <resultMap id="userMap" type="user"> <result property="userId" column="id"/> </resultMap>
- 设置了
ResultMap
之后可以通过在方法上添加@ResultMap
注解来设置
9.3、注解实现SelectKey
- 在接口方法上使用
@SelectKey
,和selectKey
标签对应,before = false
,代表设置为after
@SelectKey(statement = "select last_insert_id()", keyProperty = "userId", keyColumn = "id", before = false, resultType = Integer.class)
相当于:
<!--配置插入后获得插入用户的id,Property对应属性,Column对应数据库列名--> <selectKey keyProperty="id" keyColumn="id" resultType="int" order="AFTER"> select last_insert_id() </selectKey>