1 Mybatis入门
1.1 单独使用jdbc编程问题总结
1.1.1 jdbc程序
Public static void main(String[] args) {
Connection connection = null;
PreparedStatement preparedStatement = null;
ResultSet resultSet = null;
try {
//载入数据库驱动
Class.forName("com.mysql.jdbc.Driver");
//通过驱动管理类获取数据库链接
connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/mybatis?
characterEncoding=utf-8", "root", "mysql");
//定义sql语句 ?表示占位符
Stringsql = "select * from user whereusername = ?";
//获取预处理statement
preparedStatement = connection.prepareStatement(sql);
//设置參数,第一个參数为sql语句中參数的序号(从1開始),第二个參数为设置的參数值
preparedStatement.setString(1, "王五");
//向数据库发出sql运行查询,查询出结果集
resultSet = preparedStatement.executeQuery();
//遍历查询结果集
while(resultSet.next()){
System.out.println(resultSet.getString("id")+" "+resultSet.getString("username"));
}
} catch (Exception e) {
e.printStackTrace();
}finally{
//释放资源
if(resultSet!=null){
try {
resultSet.close();
} catch (SQLException e) {
// TODO Auto-generatedcatch block
e.printStackTrace();
}
}
if(preparedStatement!=null){
try {
preparedStatement.close();
} catch (SQLException e) {
// TODO Auto-generatedcatch block
e.printStackTrace();
}
}
if(connection!=null){
try {
connection.close();
} catch (SQLException e) {
// TODO Auto-generatedcatch block
e.printStackTrace();
}
}
}
}
上边使用jdbc的原始方法(未经封装)实现了查询数据库表记录的操作。
1.1.2 jdbc编程步骤:
1、 载入数据库驱动
2、 创建并获取数据库链接
3、 创建jdbcstatement对象
4、 设置sql语句
5、 设置sql语句中的參数(使用preparedStatement)
6、 通过statement运行sql并获取结果
7、 对sql运行结果进行解析处理
8、 释放资源(resultSet、preparedstatement、connection)
1.1.3 jdbc问题总结例如以下:
1、 数据库链接创建、释放频繁造成系统资源浪费从而影响系统性能,假设使用数据库链接池可解决此问题。
2、 Sql语句在代码中硬编码。造成代码不易维护,实际应用sql变化的可能较大,sql变动须要改变java代码。
3、 使用preparedStatement向占有位符号传參数存在硬编码。由于sql语句的where条件不一定,可能多也可能少,改动sql还要改动代码,系统不易维护。
4、 对结果集解析存在硬编码(查询列名)。sql变化导致解析代码变化,系统不易维护,假设能将数据库记录封装成pojo对象解析比較方便。
1.2 MyBatis介绍
MyBatis 本是apache的一个开源项目iBatis, 2010年这个项目由apache software foundation 迁移到了google code,而且改名为MyBatis,实质上Mybatis对ibatis进行一些改进。
MyBatis是一个优秀的持久层框架,它对jdbc的操作数据库的过程进行封装。使开发人员仅仅须要关注 SQL 本身,而不须要花费精力去处理比如注冊驱动、创建connection、创建statement、手动设置參数、结果集检索等jdbc繁杂的过程代码。
Mybatis通过xml或注解的方式将要运行的各种statement(statement、preparedStatemnt、CallableStatement)配置起来,并通过java对象和statement中的sql进行映射生成终于运行的sql语句,最后由mybatis框架运行sql并将结果映射成java对象并返回。
1.3 Mybatis架构
1、 mybatis配置
SqlMapConfig.xml。此文件作为mybatis的全局配置文件,配置了mybatis的执行环境等信息。
mapper.xml文件即sql映射文件。文件里配置了操作数据库的sql语句。此文件须要在SqlMapConfig.xml中载入。
2、 通过mybatis环境等配置信息构造SqlSessionFactory即会话工厂
3、 由会话工厂创建sqlSession即会话,操作数据库须要通过sqlSession进行。
4、 mybatis底层自己定义了Executor运行器接口操作数据库,Executor接口有两个实现,一个是基本运行器、一个是缓存运行器。
5、 Mapped Statement也是mybatis一个底层封装对象。它包装了mybatis配置信息及sql映射信息等。
mapper.xml文件里一个sql相应一个Mapped Statement对象,sql的id即是Mapped statement的id。
6、 Mapped Statement对sql运行输入參数进行定义。包含HashMap、基本类型、pojo,Executor通过Mapped Statement在运行sql前将输入的java对象映射至sql中,输入參数映射就是jdbc编程中对preparedStatement设置參数。
7、 Mapped Statement对sql运行输出结果进行定义,包含HashMap、基本类型、pojo,Executor通过Mapped Statement在运行sql后将输出结果映射至java对象中,输出结果映射过程相当于jdbc编程中对结果的解析处理过程。
1.4 mybatis下载
mybaits的代码由github.com管理,地址:https://github.com/mybatis/mybatis-3/releases
mybatis-3.2.7.jar----mybatis的核心包
lib----mybatis的依赖包
mybatis-3.2.7.pdf----mybatis使用手冊
1.5 创建mysql数据库
先导入sql_table.sql,再导入sql_data.sql脚本:
例如以下:
1.6 Mybatis入门程序
1.6.1 需求
实现下面功能:
依据用户id查询一个用户信息
依据username称模糊查询用户信息列表
加入用户
更新用户
删除用户
1.6.2 第一步:创建javaproject
使用eclipse创建javaproject。jdk使用1.7.0_72。
1.6.3 第二步:增加jar包
增加mybatis核心包、依赖包、数据驱动包。
1.6.4 第三步:log4j.properties
在classpath下创建log4j.properties例如以下:
# Global loggingconfiguration
log4j.rootLogger=DEBUG, stdout
# Console output...
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n
mybatis默认使用log4j作为输出日志信息。
1.6.5 第四步:SqlMapConfig.xml
在classpath下创建SqlMapConfig.xml,例如以下:
<?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>
<!-- 和spring整合后 environments配置将废除-->
<environments default="development">
<environment id="development">
<!-- 使用jdbc事务管理-->
<transactionManager type="JDBC" />
<!--数据库连接池-->
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis?characterEncoding=utf-8"/>
<property name="username" value="root"/>
<property name="password" value="mysql"/>
</dataSource>
</environment>
</environments>
</configuration>
SqlMapConfig.xml是mybatis核心配置文件。上边文件的配置内容为数据源、事务管理。
1.6.6 第五步:po类
Po类作为mybatis进行sql映射使用,po类通常与数据库表相应,User.java例如以下:
Public class User {
private int id;
private String username;// 用户姓名
private String sex;// 性别
private Date birthday;// 生日
private String address;// 地址
get/set……
1.6.7 第六步:程序编写
1.6.7.1 查询
1.6.7.1.1 映射文件:
在classpath下的sqlmap文件夹下创建sql映射文件Users.xml:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="test">
</mapper>
namespace :命名空间,用于隔离sql语句。后面会讲还有一层很关键的数据。
在SqlMapConfig.xml中加入:
<!-- 依据id获取用户信息 -->
<select id="findUserById"parameterType="int" resultType="cn.itcast.mybatis.po.User">
select * from user where id = #{id}
</select>
<!-- 自己定义条件查询用户列表 -->
<select id="findUserByUsername"parameterType="java.lang.String"
resultType="cn.itcast.mybatis.po.User">
select * from user whereusername like '%${value}%'
</select>
parameterType:定义输入到sql中的映射类型。#{id}表示使用preparedstatement设置占位符号并将输入变量id传到sql。
resultType:定义结果映射类型。
1.6.7.1.2 载入映射文件
mybatis框架须要载入映射文件。将Users.xml加入在SqlMapConfig.xml。例如以下:
<mappers>
<mapper resource="sqlmap/User.xml"/>
</mappers>
1.6.7.1.3 測试程序:
publicclass Mybatis_first {
//会话工厂
private SqlSessionFactory sqlSessionFactory;
@Before
publicvoid createSqlSessionFactory() throws IOException {
// 配置文件
String resource = "SqlMapConfig.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
// 使用SqlSessionFactoryBuilder从xml配置文件里创建SqlSessionFactory
sqlSessionFactory = new SqlSessionFactoryBuilder()
.build(inputStream);
}
// 依据 id查询用户信息
@Test
publicvoid testFindUserById() {
// 数据库会话实例
SqlSessionsqlSession = null;
try {
// 创建数据库会话实例sqlSession
sqlSession= sqlSessionFactory.openSession();
// 查询单个记录,依据用户id查询用户信息
Useruser = sqlSession.selectOne("test.findUserById", 10);
// 输出用户信息
System.out.println(user);
}catch(Exception e) {
e.printStackTrace();
}finally{
if (sqlSession != null) {
sqlSession.close();
}
}
}
// 依据username称模糊查询用户信息
@Test
publicvoidtestFindUserByUsername() {
// 数据库会话实例
SqlSessionsqlSession = null;
try {
// 创建数据库会话实例sqlSession
sqlSession= sqlSessionFactory.openSession();
// 查询单个记录。依据用户id查询用户信息
List<User>list = sqlSession.selectList("test.findUserByUsername", "张");
System.out.println(list.size());
}catch(Exception e) {
e.printStackTrace();
}finally{
if (sqlSession != null) {
sqlSession.close();
}
}
}
}
1.6.7.1.4 #{}和${}
#{}表示一个占位符号。通过#{}能够实现preparedStatement向占位符中设置值,自己主动进行java类型和jdbc类型转换,#{}能够有效防止sql注入。 #{}能够接收简单类型值或pojo属性值。
假设parameterType传输单个简单类型值,#{}括号里能够是value或其他名称。
${}表示拼接sql串。通过${}能够将parameterType 传入的内容拼接在sql中且不进行jdbc类型转换, ${}能够接收简单类型值或pojo属性值。假设parameterType传输单个简单类型值,${}括号里仅仅能是value。
1.6.7.1.5 parameterType和resultType
parameterType:指定输入參数类型。mybatis通过ognl从输入对象中获取參数值拼接在sql中。
resultType:指定输出结果类型,mybatis将sql查询结果的一行记录数据映射为resultType指定类型的对象。
1.6.7.1.6 selectOne和selectList
selectOne查询一条记录。假设使用selectOne查询多条记录则抛出异常:
org.apache.ibatis.exceptions.TooManyResultsException: Expected one result (or null) to be returned byselectOne(), but found: 3
at org.apache.ibatis.session.defaults.DefaultSqlSession.selectOne(DefaultSqlSession.java:70)
selectList能够查询一条或多条记录。
1.6.7.2 加入
1.6.7.2.1 映射文件:
在SqlMapConfig.xml中加入:
<!-- 加入用户 -->
<insert id="insertUser"parameterType="cn.itcast.mybatis.po.User">
<selectKey keyProperty="id"order="AFTER" resultType="java.lang.Integer">
select LAST_INSERT_ID()
</selectKey>
insert into user(username,birthday,sex,address)
values(#{username},#{birthday},#{sex},#{address})
</insert>
1.6.7.2.2 測试程序:
// 加入用户信息
@Test
publicvoid testInsert() {
// 数据库会话实例
SqlSession sqlSession = null;
try {
// 创建数据库会话实例sqlSession
sqlSession = sqlSessionFactory.openSession();
// 加入用户信息
User user = new User();
user.setUsername("张小明");
user.setAddress("河南郑州");
user.setSex("1");
user.setPrice(1999.9f);
sqlSession.insert("test.insertUser", user);
//提交事务
sqlSession.commit();
} catch (Exception e) {
e.printStackTrace();
} finally {
if (sqlSession != null) {
sqlSession.close();
}
}
}
1.6.7.2.3 mysql自增主键返回
通过改动sql映射文件。能够将mysql自增主键返回:
<insertid="insertUser"parameterType="cn.itcast.mybatis.po.User">
<!-- selectKey将主键返回,须要再返回 -->
<selectKey keyProperty="id" order="AFTER" resultType="java.lang.Integer">
</selectKey>
insert into user(username,birthday,sex,address)
values(#{username},#{birthday},#{sex},#{address});
</insert>
加入selectKey实现将主键返回
keyProperty:返回的主键存储在pojo中的哪个属性
order:selectKey的运行顺序,是相对与insert语句来说,因为mysql的自增原理运行完insert语句之后才将主键生成,所以这里selectKey的运行顺序为after
resultType:返回的主键是什么类型
LAST_INSERT_ID():是mysql的函数,返回auto_increment自增列新记录id值。
1.6.7.2.4 Mysql使用 uuid实现主键
须要添加通过select uuid()得到uuid值
<insert id="insertUser" parameterType="cn.itcast.mybatis.po.User">
<selectKeyresultType="java.lang.String" order="BEFORE"
keyProperty="id">
select uuid()
</selectKey>
insert into user(id,username,birthday,sex,address)
values(#{id},#{username},#{birthday},#{sex},#{address})
</insert>
注意这里使用的order是“BEFORE”
1.6.7.2.5 Oracle使用序列生成主键
首先自己定义一个序列且用于生成主键,selectKey使用例如以下:
<insert id="insertUser" parameterType="cn.itcast.mybatis.po.User">
<selectKeyresultType="java.lang.Integer" order="BEFORE"
keyProperty="id">
SELECT 自己定义序列.NEXTVAL FROM DUAL
</selectKey>
insert into user(id,username,birthday,sex,address)
values(#{id},#{username},#{birthday},#{sex},#{address})
</insert>
注意这里使用的order是“BEFORE”
1.6.7.3 删除
1.6.7.3.1 映射文件:
<!-- 删除用户 -->
<delete id="deleteUserById"parameterType="int">
delete from user where id=#{id}
</delete>
1.6.7.3.2 測试程序:
// 依据id删除用户
@Test
publicvoid testDelete(){
// 数据库会话实例
SqlSession sqlSession = null;
try {
// 创建数据库会话实例sqlSession
sqlSession = sqlSessionFactory.openSession();
// 删除用户
sqlSession.delete("test.deleteUserById",18);
// 提交事务
sqlSession.commit();
} catch (Exception e) {
e.printStackTrace();
} finally {
if (sqlSession != null) {
sqlSession.close();
}
}
}
1.6.7.4 改动
1.6.7.4.1 映射文件
<!-- 更新用户 -->
<update id="updateUser"parameterType="cn.itcast.mybatis.po.User">
update user set username=#{username},birthday=#{birthday},sex=#{sex},address=#{address}
where id=#{id}
</update>
1.6.7.4.2 測试程序
// 更新用户信息
@Test
publicvoid testUpdate(){
// 数据库会话实例
SqlSession sqlSession = null;
try {
// 创建数据库会话实例sqlSession
sqlSession = sqlSessionFactory.openSession();
// 加入用户信息
User user = new User();
user.setId(16);
user.setUsername("张小明");
user.setAddress("河南郑州");
user.setSex("1");
user.setPrice(1999.9f);
sqlSession.update("test.updateUser", user);
// 提交事务
sqlSession.commit();
} catch (Exception e) {
e.printStackTrace();
} finally {
if (sqlSession != null) {
sqlSession.close();
}
}
}
1.6.8 Mybatis解决jdbc编程的问题
1、 数据库链接创建、释放频繁造成系统资源浪费从而影响系统性能,假设使用数据库链接池可解决此问题。
解决:在SqlMapConfig.xml中配置数据链接池,使用连接池管理数据库链接。
2、 Sql语句写在代码中造成代码不易维护,实际应用sql变化的可能较大,sql变动须要改变java代码。
解决:将Sql语句配置在XXXXmapper.xml文件里与java代码分离。
3、 向sql语句传參数麻烦。由于sql语句的where条件不一定,可能多也可能少,占位符须要和參数一一相应。
解决:Mybatis自己主动将java对象映射至sql语句,通过statement中的parameterType定义输入參数的类型。
4、 对结果集解析麻烦,sql变化导致解析代码变化,且解析前须要遍历,假设能将数据库记录封装成pojo对象解析比較方便。
解决:Mybatis自己主动将sql运行结果映射至java对象,通过statement中的resultType定义输出结果的类型。
1.6.9 与hibernate不同
Mybatis和hibernate不同,它不全然是一个ORM框架,由于MyBatis须要程序猿自己编写Sql语句,只是mybatis能够通过XML或注解方式灵活配置要运行的sql语句,并将java对象和sql语句映射生成终于运行的sql。最后将sql运行的结果再映射生成java对象。
Mybatis学习门槛低,简单易学。程序猿直接编写原生态sql,可严格控制sql运行性能。灵活度高,很适合对关系数据模型要求不高的软件开发,比如互联网软件、企业运营类软件等,由于这类软件需求变化频繁,一但需求变化要求成果输出迅速。可是灵活的前提是mybatis无法做到数据库无关性。假设须要实现支持多种数据库的软件则须要自己定义多套sql映射文件,工作量大。
Hibernate对象/关系映射能力强,数据库无关性好。对于关系模型要求高的软件(比如需求固定的定制化软件)假设用hibernate开发能够节省非常多代码,提高效率。可是Hibernate的学习门槛高,要精通门槛更高。并且怎么设计O/R映射,在性能和对象模型之间如何权衡,以及如何用好Hibernate须要具有非常强的经验和能力才行。
总之,依照用户的需求在有限的资源环境下仅仅要能做出维护性、扩展性良好的软件架构都是好架构,所以框架仅仅有适合才是最好。
2 Dao开发方法
使用Mybatis开发Dao,通常有两个方法。即原始Dao开发方法和Mapper接口开发方法。
2.1 需求
将下边的功能实现Dao:
依据用户id查询一个用户信息
依据username称模糊查询用户信息列表
加入用户信息
2.2 SqlSession的使用范围
SqlSession中封装了对数据库的操作,如:查询、插入、更新、删除等。
通过SqlSessionFactory创建SqlSession。而SqlSessionFactory是通过SqlSessionFactoryBuilder进行创建。
2.2.1 SqlSessionFactoryBuilder
SqlSessionFactoryBuilder用于创建SqlSessionFacoty,SqlSessionFacoty一旦创建完毕就不须要SqlSessionFactoryBuilder了,由于SqlSession是通过SqlSessionFactory生产。所以能够将SqlSessionFactoryBuilder当成一个工具类使用,最佳使用范围是方法范围即方法体内局部变量。
2.2.2 SqlSessionFactory
SqlSessionFactory是一个接口,接口中定义了openSession的不同重载方法,SqlSessionFactory的最佳使用范围是整个应用执行期间,一旦创建后能够反复使用,通常以单例模式管理SqlSessionFactory。
2.2.3 SqlSession
SqlSession是一个面向用户的接口, sqlSession中定义了数据库操作。默认使用DefaultSqlSession实现类。
运行步骤例如以下:
1、 载入数据源等配置信息
Environment environment = configuration.getEnvironment();
2、 创建数据库链接
3、 创建事务对象
4、 创建Executor。SqlSession全部操作都是通过Executor完毕。mybatis源代码例如以下:
if (ExecutorType.BATCH == executorType) {
executor = newBatchExecutor(this, transaction);
} elseif (ExecutorType.REUSE == executorType){
executor = new ReuseExecutor(this, transaction);
} else {
executor = new SimpleExecutor(this, transaction);
}
if (cacheEnabled) {
executor = newCachingExecutor(executor, autoCommit);
}
5、 SqlSession的实现类即DefaultSqlSession,此对象中对操作数据库实质上用的是Executor
结论:
每一个线程都应该有它自己的SqlSession实例。
SqlSession的实例不能共享使用,它也是线程不安全的。因此最佳的范围是请求或方法范围。绝对不能将SqlSession实例的引用放在一个类的静态字段或实例字段中。
打开一个SqlSession。使用完成就要关闭它。
通常把这个关闭操作放到 finally 块中以确保每次都能运行关闭。例如以下:
SqlSession session = sqlSessionFactory.openSession();
try {
// do work
} finally {
session.close();
}
2.3 原始Dao开发方式
原始Dao开发方法须要程序猿编写Dao接口和Dao实现类。
2.3.1 映射文件
xml version="1.0"encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="test">
<!-- 依据id获取用户信息 -->
<select id="findUserById" parameterType="int"resultType="cn.itcast.mybatis.po.User">
select * from user where id = #{id}
</select>
<!-- 加入用户 -->
<insert id="insertUser"parameterType="cn.itcast.mybatis.po.User">
<selectKey keyProperty="id"order="AFTER" resultType="java.lang.Integer">
selectLAST_INSERT_ID()
</selectKey>
insert into user(username,birthday,sex,address)
values(#{username},#{birthday},#{sex},#{address})
</insert>
</mapper>
2.3.2 Dao接口
Public interface UserDao {
public User getUserById(int id) throws Exception;
public void insertUser(User user) throws Exception;
}
Public class UserDaoImpl implements UserDao {
//注入SqlSessionFactory
public UserDaoImpl(SqlSessionFactory sqlSessionFactory){
this.setSqlSessionFactory(sqlSessionFactory);
}
private SqlSessionFactory sqlSessionFactory;
@Override
public User getUserById(int id) throws Exception {
SqlSession session = sqlSessionFactory.openSession();
User user = null;
try {
//通过sqlsession调用selectOne方法获取一条结果集
//參数1:指定定义的statement的id,參数2:指定向statement中传递的參数
user = session.selectOne("test.findUserById", 1);
System.out.println(user);
} finally{
session.close();
}
return user;
}
@Override
Public void insertUser(User user) throws Exception {
SqlSession sqlSession =sqlSessionFactory.openSession();
try {
sqlSession.insert("insertUser", user);
sqlSession.commit();
} finally{
session.close();
}
}
}
2.3.3 问题
原始Dao开发中存在下面问题:
u Dao方法体存在反复代码:通过SqlSessionFactory创建SqlSession。调用SqlSession的数据库操作方法
u 调用sqlSession的数据库操作方法须要指定statement的id,这里存在硬编码,不得于开发维护。
2.4 Mapper动态代理方式
2.4.1 实现原理
Mapper接口开发方法仅仅须要程序猿编写Mapper接口(相当于Dao接口)。由Mybatis框架依据接口定义创建接口的动态代理对象,代理对象的方法体同上边Dao接口实现类方法。
Mapper接口开发须要遵循下面规范:
1、 Mapper.xml文件里的namespace与mapper接口的类路径同样。
2、 Mapper接口方法名和Mapper.xml中定义的每一个statement的id同样
3、 Mapper接口方法的输入參数类型和mapper.xml中定义的每一个sql 的parameterType的类型同样
4、 Mapper接口方法的输出參数类型和mapper.xml中定义的每一个sql的resultType的类型同样
2.4.2 Mapper.xml(映射文件)
定义mapper映射文件UserMapper.xml(内容同Users.xml)。须要改动namespace的值为UserMapper接口路径。将UserMapper.xml放在classpath 下mapper文件夹下。
<?
xml version="1.0"encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="cn.itcast.mybatis.mapper.UserMapper">
<!-- 依据id获取用户信息 -->
<select id="findUserById"parameterType="int" resultType="cn.itcast.mybatis.po.User">
select * from user where id = #{id}
</select>
<!-- 自己定义条件查询用户列表 -->
<select id="findUserByUsername"parameterType="java.lang.String"
resultType="cn.itcast.mybatis.po.User">
select * from user whereusername like '%${value}%'
</select>
<!-- 加入用户 -->
<insert id="insertUser"parameterType="cn.itcast.mybatis.po.User">
<selectKey keyProperty="id"order="AFTER" resultType="java.lang.Integer">
select LAST_INSERT_ID()
</selectKey>
insert into user(username,birthday,sex,address)
values(#{username},#{birthday},#{sex},#{address})
</insert>
</mapper>
2.4.3 Mapper.java(接口文件)
/**
* 用户管理mapper
*/
Public interface UserMapper {
//依据用户id查询用户信息
public User findUserById(int id) throws Exception;
//查询用户列表
public List<User> findUserByUsername(String username) throws Exception;
//加入用户信息
publicvoid insertUser(User user)throws Exception;
}
接口定义有例如以下特点:
1、 Mapper接口方法名和Mapper.xml中定义的statement的id同样
2、 Mapper接口方法的输入參数类型和mapper.xml中定义的statement的parameterType的类型同样
3、 Mapper接口方法的输出參数类型和mapper.xml中定义的statement的resultType的类型同样
2.4.4 载入UserMapper.xml文件
改动SqlMapConfig.xml文件:
<!-- 载入映射文件 -->
<mappers>
<mapper resource="mapper/UserMapper.xml"/>
</mappers>
2.4.5 測试
Public class UserMapperTest extends TestCase {
private SqlSessionFactory sqlSessionFactory;
protected void setUp() throws Exception {
//mybatis配置文件
String resource = "sqlMapConfig.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
//使用SqlSessionFactoryBuilder创建sessionFactory
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
}
Public void testFindUserById() throws Exception {
//获取session
SqlSession session = sqlSessionFactory.openSession();
UserMapper userMapper = session.getMapper(UserMapper.class);
//调用代理对象方法
User user = userMapper.findUserById(1);
System.out.println(user);
//关闭session
session.close();
}
@Test
publicvoid testFindUserByUsername() throws Exception {
SqlSession sqlSession = sqlSessionFactory.openSession();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
List<User> list = userMapper.findUserByUsername("张");
System.out.println(list.size());
}
Public void testInsertUser() throws Exception {
//获取session
SqlSession session = sqlSessionFactory.openSession();
//获取mapper接口的代理对象
UserMapper userMapper = session.getMapper(UserMapper.class);
//要加入的数据
User user = new User();
user.setUsername("张三");
user.setBirthday(new Date());
user.setSex("1");
user.setAddress("北京市");
//通过mapper接口加入用户
userMapper.insertUser(user);
//提交
session.commit();
//关闭session
session.close();
}
}
2.4.6 总结
u selectOne和selectList
动态代理对象调用sqlSession.selectOne()和sqlSession.selectList()是依据mapper接口方法的返回值决定。假设返回list则调用selectList方法,假设返回单个对象则调用selectOne方法。
u namespace
mybatis官方推荐使用mapper代理方法开发mapper接口,程序猿不用编写mapper接口实现类。使用mapper代理方法时,输入參数能够使用pojo包装对象或map对象,保证dao的通用性。
3 SqlMapConfig.xml配置文件
3.1 配置内容
SqlMapConfig.xml中配置的内容和顺序例如以下:
properties(属性)
settings(全局配置參数)
typeAliases(类型别名)
typeHandlers(类型处理器)
objectFactory(对象工厂)
plugins(插件)
environments(环境集合属性对象)
environment(环境子属性对象)
transactionManager(事务管理)
dataSource(数据源)
mappers(映射器)
3.2 properties(属性)
SqlMapConfig.xml能够引用java属性文件里的配置信息例如以下:
在classpath下定义db.properties文件。
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/mybatis
jdbc.username=root
jdbc.password=mysql
SqlMapConfig.xml引用例如以下:
<properties resource="db.properties"/>
<environments default="development">
<environment id="development">
<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>
</environments>
注意: MyBatis 将依照以下的顺序来载入属性:
u 在 properties 元素体内定义的属性首先被读取。
u 然后会读取properties元素中resource或 url 载入的属性,它会覆盖已读取的同名属性。
u 最后读取parameterType传递的属性。它会覆盖已读取的同名属性。
因此,通过parameterType传递的属性具有最高优先级。resource或 url 载入的属性次之,最低优先级的是 properties 元素体内定义的属性。
3.3 settings(配置)
mybatis全局配置參数,全局參数将会影响mybatis的执行行为。
具体參见“学习资料/mybatis-settings.xlsx”文件
3.4 typeAliases(类型别名)
3.4.1 mybatis支持别名:
别名 |
映射的类型 |
_byte |
byte |
_long |
long |
_short |
short |
_int |
int |
_integer |
int |
_double |
double |
_float |
float |
_boolean |
boolean |
string |
String |
byte |
Byte |
long |
Long |
short |
Short |
int |
Integer |
integer |
Integer |
double |
Double |
float |
Float |
boolean |
Boolean |
date |
Date |
decimal |
BigDecimal |
bigdecimal |
BigDecimal |
3.4.2 自己定义别名:
在SqlMapConfig.xml中配置:
<typeAliases>
<!-- 单个别名定义 -->
<typeAlias alias="user"type="cn.itcast.mybatis.po.User"/>
<!-- 批量别名定义。扫描整个包下的类,别名为类名(首字母大写或小写都能够) -->
<package name="cn.itcast.mybatis.po"/>
<package name="其他包"/>
</typeAliases>
3.5 typeHandlers(类型处理器)
类型处理器用于java类型和jdbc类型映射,例如以下:
<selectid="findUserById"parameterType="int" resultType="user">
select * from user where id = #{id}
</select>
mybatis自带的类型处理器基本上满足日常需求。不须要单独定义。
mybatis支持类型处理器:
类型处理器 |
Java类型 |
JDBC类型 |
BooleanTypeHandler |
Boolean。boolean |
不论什么兼容的布尔值 |
ByteTypeHandler |
Byte,byte |
不论什么兼容的数字或字节类型 |
ShortTypeHandler |
Short,short |
不论什么兼容的数字或短整型 |
IntegerTypeHandler |
Integer,int |
不论什么兼容的数字和整型 |
LongTypeHandler |
Long,long |
不论什么兼容的数字或长整型 |
FloatTypeHandler |
Float,float |
不论什么兼容的数字或单精度浮点型 |
DoubleTypeHandler |
Double。double |
不论什么兼容的数字或双精度浮点型 |
BigDecimalTypeHandler |
BigDecimal |
不论什么兼容的数字或十进制小数类型 |
StringTypeHandler |
String |
CHAR和VARCHAR类型 |
ClobTypeHandler |
String |
CLOB和LONGVARCHAR类型 |
NStringTypeHandler |
String |
NVARCHAR和NCHAR类型 |
NClobTypeHandler |
String |
NCLOB类型 |
ByteArrayTypeHandler |
byte[] |
不论什么兼容的字节流类型 |
BlobTypeHandler |
byte[] |
BLOB和LONGVARBINARY类型 |
DateTypeHandler |
Date(java.util) |
TIMESTAMP类型 |
DateOnlyTypeHandler |
Date(java.util) |
DATE类型 |
TimeOnlyTypeHandler |
Date(java.util) |
TIME类型 |
SqlTimestampTypeHandler |
Timestamp(java.sql) |
TIMESTAMP类型 |
SqlDateTypeHandler |
Date(java.sql) |
DATE类型 |
SqlTimeTypeHandler |
Time(java.sql) |
TIME类型 |
ObjectTypeHandler |
随意 |
其它或未指定类型 |
EnumTypeHandler |
Enumeration类型 |
VARCHAR-不论什么兼容的字符串类型。作为代码存储(而不是索引)。 |
3.6 mappers(映射器)
Mapper配置的几种方法:
3.6.1 <mapper resource=" " />
使用相对于类路径的资源
如:<mapper resource="sqlmap/User.xml" />
3.6.2 <mapper url=" " />
使用全然限定路径
如:<mapperurl="file:///D:workspace_spingmvcmybatis_01configsqlmapUser.xml"/>
3.6.3 <mapper class=" " />
使用mapper接口类路径
如:<mapperclass="cn.itcast.mybatis.mapper.UserMapper"/>
注意:此种方法要求mapper接口名称和mapper映射文件名同样。且放在同一个文件夹中。
3.6.4 <package name=""/>
注冊指定包下的全部mapper接口
如:<package name="cn.itcast.mybatis.mapper"/>
注意:此种方法要求mapper接口名称和mapper映射文件名同样,且放在同一个文件夹中。
4 Mapper.xml映射文件
Mapper.xml映射文件里定义了操作数据库的sql,每一个sql是一个statement。映射文件是mybatis的核心。
4.1 parameterType(输入类型)
4.1.1 #{}与${}
#{}实现的是向prepareStatement中的预处理语句中设置參数值。sql语句中#{}表示一个占位符即?
。
<!-- 依据id查询用户信息 -->
<select id="findUserById"parameterType="int" resultType="user">
select * from user where id = #{id}
</select>
使用占位符#{}能够有效防止sql注入,在使用时不须要关心參数值的类型,mybatis会自己主动进行java类型和jdbc类型的转换。
#{}能够接收简单类型值或pojo属性值。假设parameterType传输单个简单类型值。#{}括号里能够是value或其他名称。
${}和#{}不同。通过${}能够将parameterType传入的内容拼接在sql中且不进行jdbc类型转换。 ${}能够接收简单类型值或pojo属性值,假设parameterType传输单个简单类型值,${}括号里仅仅能是value。
使用${}不能防止sql注入。可是有时用${}会很方便,例如以下的样例:
<!-- 依据名称模糊查询用户信息 -->
<select id="selectUserByName"parameterType="string" resultType="user">
select * from user whereusername like '%${value}%'
</select>
假设本样例使用#{}则传入的字符串中必须有%号,而%是人为拼接在參数中,显然有点麻烦,假设採用${}在sql中拼接为%的方式则在调用mapper接口传递參数就方便非常多。
//假设使用占位符号则必须人为在传參数中加%
List<User> list= userMapper.selectUserByName("%管理员%");
//假设使用${}原始符号则不用人为在參数中加%
List<User>list= userMapper.selectUserByName("管理员");
再比方order by排序,假设将列名通过參数传入sql,依据传的列名进行排序。应该写为:
ORDER BY${columnName}
假设使用#{}将无法实现此功能。
4.1.2 传递简单类型
參考上边的样例。
4.1.3 传递pojo对象
Mybatis使用ognl表达式解析对象字段的值,例如以下样例:
<!—传递pojo对象综合查询用户信息 -->
<select id="findUserByUser"parameterType="user" resultType="user">
select * from user whereid=#{id} and usernamelike '%${username}%'
</select>
上边红色标注的是user对象中的字段名称。
測试:
Public void testFindUserByUser()throws Exception{
//获取session
SqlSession session = sqlSessionFactory.openSession();
//获限mapper接口实例
UserMapper userMapper = session.getMapper(UserMapper.class);
//构造查询条件user对象
User user = new User();
user.setId(1);
user.setUsername("管理员");
//传递user对象查询用户列表
List<User>list = userMapper.findUserByUser(user);
//关闭session
session.close();
}
异常測试:
Sql中字段名输入错误后測试,username输入dusername測试结果报错:
org.apache.ibatis.exceptions.PersistenceException:
### Error querying database. Cause:org.apache.ibatis.reflection.ReflectionException: Thereis no getter for property named 'dusername' in 'classcn.itcast.mybatis.po.User'
### Cause: org.apache.ibatis.reflection.ReflectionException:There is no getter for property named 'dusername' in 'classcn.itcast.mybatis.po.User'
4.1.4 传递pojo包装对象
开发中通过pojo传递查询条件 ,查询条件是综合的查询条件,不仅包含用户查询条件还包含其他的查询条件(比方将用户购买商品信息也作为查询条件)。这时能够使用包装对象传递输入參数。
4.1.4.1 定义包装对象
定义包装对象将查询条件(pojo)以类组合的方式包装起来。
publicclass QueryVo {
private User user;
//自己定义用户扩展类
private UserCustom userCustom;
4.1.4.2 mapper.xml映射文件
说明:mybatis底层通过ognl从pojo中获取属性值:#{user.username}。user即是传入的包装对象的属性。
queryVo是别名。即上边定义的包装对象类型。
4.1.5 传递hashmap
Sql映射文件定义例如以下:
<!-- 传递hashmap综合查询用户信息 -->
<select id="findUserByHashmap"parameterType="hashmap" resultType="user">
select * from user whereid=#{id} and usernamelike '%${username}%'
</select>
上边红色标注的是hashmap的key。
測试:
Public void testFindUserByHashmap()throws Exception{
//获取session
SqlSession session = sqlSessionFactory.openSession();
//获限mapper接口实例
UserMapper userMapper = session.getMapper(UserMapper.class);
//构造查询条件Hashmap对象
HashMap<String, Object> map = newHashMap<String, Object>();
map.put("id", 1);
map.put("username", "管理员");
//传递Hashmap对象查询用户列表
List<User>list = userMapper.findUserByHashmap(map);
//关闭session
session.close();
}
异常測试:
传递的map中的key和sql中解析的key不一致。
測试结果没有报错。仅仅是通过key获取值为空。
4.2 resultType(输出类型)
4.2.1 输出简单类型
參考getnow输出日期类型。看下边的样例输出整型:
Mapper.xml文件
<!-- 获取用户列表总数 -->
<select id="findUserCount"parameterType="user" resultType="int">
select count(1) fromuser
</select>
Mapper接口
public int findUserCount(User user)throws Exception;
调用:
Public void testFindUserCount() throws Exception{
//获取session
SqlSession session = sqlSessionFactory.openSession();
//获取mapper接口实例
UserMapper userMapper = session.getMapper(UserMapper.class);
User user = new User();
user.setUsername("管理员");
//传递Hashmap对象查询用户列表
int count = userMapper.findUserCount(user);
//关闭session
session.close();
}
总结:
输出简单类型必须查询出来的结果集有一条记录。终于将第一个字段的值转换为输出类型。
使用session的selectOne可查询单条记录。
4.2.2 输出pojo对象
參考findUserById的定义:
Mapper.xml
<!-- 依据id查询用户信息 -->
<selectid="findUserById"parameterType="int" resultType="user">
select * from user where id = #{id}
</select>
Mapper接口:
public User findUserById(intid) throws Exception;
測试:
Public void testFindUserById() throws Exception {
//获取session
SqlSession session = sqlSessionFactory.openSession();
//获限mapper接口实例
UserMapper userMapper = session.getMapper(UserMapper.class);
//通过mapper接口调用statement
User user = userMapper.findUserById(1);
System.out.println(user);
//关闭session
session.close();
}
使用session调用selectOne查询单条记录。
4.2.3 输出pojo列表
參考selectUserByName的定义:
Mapper.xml
<!-- 依据名称模糊查询用户信息 -->
<select id="findUserByUsername"parameterType="string" resultType="user">
select * from user whereusername like '%${value}%'
</select>
Mapper接口:
public List<User> findUserByUsername(String username)throws Exception;
測试:
Public void testFindUserByUsername()throws Exception{
//获取session
SqlSession session = sqlSessionFactory.openSession();
//获限mapper接口实例
UserMapper userMapper = session.getMapper(UserMapper.class);
//假设使用占位符号则必须人为在传參数中加%
//List<User>list = userMapper.selectUserByName("%管理员%");
//假设使用${}原始符号则不用人为在參数中加%
List<User> list = userMapper.findUserByUsername("管理员");
//关闭session
session.close();
}
使用session的selectList方法获取pojo列表。
4.2.4 resultType总结:
输出pojo对象和输出pojo列表在sql中定义的resultType是一样的。
返回单个pojo对象要保证sql查询出来的结果集为单条,内部使用session.selectOne方法调用。mapper接口使用pojo对象作为方法返回值。
返回pojo列表表示查询出来的结果集可能为多条,内部使用session.selectList方法。mapper接口使用List<pojo>对象作为方法返回值。
4.2.5 输出hashmap
输出pojo对象能够改用hashmap输出类型。将输出的字段名称作为map的key,value为字段值。
4.3 resultMap
resultType能够指定pojo将查询结果映射为pojo。但须要pojo的属性名和sql查询的列名一致方可映射成功。
假设sql查询字段名和pojo的属性名不一致,能够通过resultMap将字段名和属性名作一个相应关系 。resultMap实质上还须要将查询结果映射到pojo对象中。
resultMap能够实现将查询结果映射为复杂类型的pojo,比方在查询结果映射对象中包含pojo和list实现一对一查询和一对多查询。
4.3.1 Mapper.xml定义
使用resultMap指定上边定义的personmap。
4.3.2 定义resultMap
因为上边的mapper.xml中sql查询列和Users.java类属性不一致,须要定义resultMap:userListResultMap将sql查询列和Users.java类属性相应起来
<id />:此属性表示查询结果集的唯一标识,很重要。假设是多个字段为复合唯一约束则定义多个<id />。
Property:表示person类的属性。
Column:表示sql查询出来的字段名。
Column和property放在一块儿表示将sql查询出来的字段映射到指定的pojo类属性上。
<result />:普通结果,即pojo的属性。
4.3.3 Mapper接口定义
public List<User> findUserListResultMap()throws Exception;
4.4 动态sql(重点)
通过mybatis提供的各种标签方法实现动态拼接sql。
4.4.1 If
<!-- 传递pojo综合查询用户信息 -->
<select id="findUserList" parameterType="user" resultType="user">
select * from user
where 1=1
<if test="id!=nulland id!=''">
and id=#{id}
</if>
<if test="username!=nulland username!=''">
and username like '%${username}%'
</if>
</select>
注意要做不等于空字符串校验。
4.4.2 Where
上边的sql也能够改为:
<selectid="findUserList"parameterType="user" resultType="user">
select * from user
<where>
<if test="id!=nulland id!=''">
and id=#{id}
</if>
<if test="username!=nulland username!=''">
and username like '%${username}%'
</if>
</where>
</select>
<where />能够自己主动处理第一个and。
4.4.3 foreach
向sql传递数组或List。mybatis使用foreach解析。例如以下:
4.4.3.1 通过pojo传递list
u 需求
传入多个id查询用户信息,用下边两个sql实现:
SELECT * FROM USERS WHERE username LIKE '%张%' AND (id=10 OR id =89 OR id=16)
SELECT * FROM USERS WHERE username LIKE '%张%' id IN (10,89,16)
u 在pojo中定义list属性ids存储多个用户id,并加入getter/setter方法
u mapper.xml
<if test="ids!=null and ids.size>0">
<foreach collection="ids"open=" and id in(" close=")" item="id"separator="," >
#{id}
</foreach>
</if>
u 測试代码:
List<Integer> ids= new ArrayList<Integer>();
ids.add(1);//查询id为1的用户
ids.add(10); //查询id为10的用户
queryVo.setIds(ids);
List<User> list = userMapper.findUserList(queryVo);
4.4.3.2 传递单个List
传递List类型在编写mapper.xml没有差别,唯一不同的是仅仅有一个List參数时它的參数名为list。
例如以下:
u Mapper.xml
<selectid="selectUserByList"parameterType="java.util.List" resultType="user">
select * from user
<where>
<!-- 传递List,List中是pojo -->
<if test="list!=null">
<foreach collection="list"item="item" open="and id in("separator=","close=")">
#{item.id}
</foreach>
</if>
</where>
</select>
u Mapper接口
public List<User>selectUserByList(List userlist) throws Exception;
u 測试:
Public void testselectUserByList()throws Exception{
//获取session
SqlSession session = sqlSessionFactory.openSession();
//获限mapper接口实例
UserMapper userMapper = session.getMapper(UserMapper.class);
//构造查询条件List
List<User> userlist = newArrayList<User>();
User user = new User();
user.setId(1);
userlist.add(user);
user = new User();
user.setId(2);
userlist.add(user);
//传递userlist列表查询用户列表
List<User>list =userMapper.selectUserByList(userlist);
//关闭session
session.close();
}
4.4.3.3 传递单个数组(数组中是pojo):
请阅读文档学习。
u Mapper.xml
<!-- 传递数组综合查询用户信息 -->
<select id="selectUserByArray"parameterType="Object[]" resultType="user">
select * from user
<where>
<!-- 传递数组 -->
<if test="array!=null">
<foreach collection="array"index="index" item="item" open="and id in("separator=","close=")">
#{item.id}
</foreach>
</if>
</where>
</select>
sql仅仅接收一个数组參数,这时sql解析參数的名称mybatis固定为array。假设数组是通过一个pojo传递到sql则參数的名称为pojo中的属性名。
index:为数组的下标。
item:为数组每一个元素的名称,名称任意定义
open:循环開始
close:循环结束
separator:中间分隔输出
u Mapper接口:
public List<User>selectUserByArray(Object[] userlist) throws Exception;
u 測试:
Public void testselectUserByArray()throws Exception{
//获取session
SqlSession session = sqlSessionFactory.openSession();
//获限mapper接口实例
UserMapper userMapper = session.getMapper(UserMapper.class);
//构造查询条件List
Object[] userlist = new Object[2];
User user = new User();
user.setId(1);
userlist[0]=user;
user = new User();
user.setId(2);
userlist[1]=user;
//传递user对象查询用户列表
List<User>list =userMapper.selectUserByArray(userlist);
//关闭session
session.close();
}
4.4.3.4 传递单个数组(数组中是字符串类型):
请阅读文档学习。
u Mapper.xml
<!-- 传递数组综合查询用户信息 -->
<select id="selectUserByArray"parameterType="Object[]" resultType="user">
select * from user
<where>
<!-- 传递数组 -->
<if test="array!=null">
<foreach collection="array"index="index"item="item"open="and idin("separator=","close=")">
#{item}
</foreach>
</if>
</where>
</select>
假设数组中是简单类型则写为#{item},不用再通过ognl获取对象属性值了。
u Mapper接口:
public List<User>selectUserByArray(Object[] userlist) throws Exception;
u 測试:
Public void testselectUserByArray()throws Exception{
//获取session
SqlSession session = sqlSessionFactory.openSession();
//获限mapper接口实例
UserMapper userMapper = session.getMapper(UserMapper.class);
//构造查询条件List
Object[] userlist = new Object[2];
userlist[0]=”1”;
userlist[1]=”2”;
//传递user对象查询用户列表
List<User>list =userMapper.selectUserByArray(userlist);
//关闭session
session.close();
}
4.4.4 Sql片段
Sql中可将反复的sql提取出来。使用时用include引用就可以。终于达到sql重用的目的。例如以下:
<!-- 传递pojo综合查询用户信息 -->
<select id="findUserList"parameterType="user" resultType="user">
select * from user
<where>
<if test="id!=nulland id!=''">
and id=#{id}
</if>
<if test="username!=nulland username!=''">
and username like '%${username}%'
</if>
</where>
</select>
u 将where条件抽取出来:
<sqlid="query_user_where">
<if test="id!=nulland id!=''">
and id=#{id}
</if>
<if test="username!=nulland username!=''">
and username like '%${username}%'
</if>
</sql>
u 使用include引用:
<selectid="findUserList"parameterType="user" resultType="user">
select * from user
<where>
<include refid="query_user_where"/>
</where>
</select>
注意:假设引用其他mapper.xml的sql片段,则在引用时须要加上namespace。例如以下:
<include refid="namespace.sql片段”/>
5 关联查询
5.1 商品订单数据模型
5.2 一对一查询
案例:查询全部订单信息,关联查询下单用户信息。
注意:由于一个订单信息仅仅会是一个人下的订单。所以从查询订单信息出发关联查询用户信息为一对一查询。假设从用户信息出发查询用户下的订单信息则为一对多查询,由于一个用户能够下多个订单。
5.2.1 方法一:
使用resultType,定义订单信息po类,此po类中包含了订单信息和用户信息:
5.2.1.1 Sql语句:
SELECT
orders.*,
user.username,
userss.address
FROM
orders,
user
WHERE orders.user_id = user.id
5.2.1.2 定义po类
Po类中应该包含上边sql查询出来的全部字段,例如以下:
publicclass OrdersCustom extends Orders {
private String username;// username称
private String address;// 用户地址
get/set。
。。。
OrdersCustom类继承Orders类后OrdersCustom类包含了Orders类的全部字段。仅仅须要定义用户的信息字段就可以。
5.2.1.3 Mapper.xml
<!-- 查询全部订单信息 -->
<select id="findOrdersList"resultType="cn.itcast.mybatis.po.OrdersCustom">
SELECT
orders.*,
user.username,
user.address
FROM
orders, user
WHERE orders.user_id = user.id
</select>
5.2.1.4 Mapper接口:
public List<OrdersCustom>findOrdersList() throws Exception;
5.2.1.5 測试:
Public void testfindOrdersList()throws Exception{
//获取session
SqlSession session = sqlSessionFactory.openSession();
//获限mapper接口实例
UserMapper userMapper = session.getMapper(UserMapper.class);
//查询订单信息
List<OrdersCustom> list = userMapper.findOrdersList();
System.out.println(list);
//关闭session
session.close();
}
5.2.1.6 总结:
定义专门的po类作为输出类型。当中定义了sql查询结果集全部的字段。此方法较为简单,企业中使用普遍。
5.2.2 方法二:
使用resultMap。定义专门的resultMap用于映射一对一查询结果。
5.2.2.1 Sql语句:
SELECT
orders.*,
user.username,
user.address
FROM
orders,
user
WHERE orders.user_id = user.id
5.2.2.2 定义po类
在Orders类中增加User属性。user属性中用于存储关联查询的用户信息,由于订单关联查询用户是一对一关系,所以这里使用单个User对象存储关联查询的用户信息。
5.2.2.3 Mapper.xml
<selectid="findOrdersListResultMap"resultMap="userordermap">
SELECT
orders.*,
user.username,
user.address
FROM
orders, user
WHERE orders.user_id = user.id
</select>
这里resultMap指定userordermap。
5.2.2.4 定义resultMap
须要关联查询映射的是用户信息,使用association将用户信息映射到订单对象的用户属性中。
<!-- 订单信息resultmap -->
<resultMap type="cn.itcast.mybatis.po.Orders" id="userordermap">
<!-- 这里的id,是mybatis在进行一对一查询时将user字段映射为user对象时要使用,必须写 -->
<id property="id" column="id"/>
<result property="user_id"column="user_id"/>
<result property="number"column="number"/>
<association property="user"javaType="cn.itcast.mybatis.po.User">
<!-- 这里的id为user的id,假设写上表示给user的id属性赋值 -->
<id property="id" column="user_id"/>
<result property="username"column="username"/>
<result property="address"column="address"/>
</association>
</resultMap>
association:表示进行关联查询单条记录
property:表示关联查询的结果存储在cn.itcast.mybatis.po.Orders的user属性中
javaType:表示关联查询的结果类型
<id property="id" column="user_id"/>:查询结果的user_id列相应关联对象的id属性。这里是<id />表示user_id是关联查询对象的唯一标识。
<result property="username"column="username"/>:查询结果的username列相应关联对象的username属性。
5.2.2.5 Mapper接口:
public List<Orders>findOrdersListResultMap() throws Exception;
5.2.2.6 測试:
Public void testfindOrdersListResultMap()throws Exception{
//获取session
SqlSession session = sqlSessionFactory.openSession();
//获限mapper接口实例
UserMapper userMapper = session.getMapper(UserMapper.class);
//查询订单信息
List<Orders> list = userMapper.findOrdersList2();
System.out.println(list);
//关闭session
session.close();
}
5.2.2.7 小结:
使用association完毕关联查询,将关联查询信息映射到pojo对象中。
5.3 一对多查询
案例:查询全部订单信息及订单下的订单明细信息。
订单信息与订单明细为一对多关系。
使用resultMap实现例如以下:
5.3.1 Sql语句:
orders.*,
user.username,
user.address,
orderdetail.idorderdetail_id,
orderdetail.items_id,
orderdetail.items_num
FROM
orders,user,orderdetail
WHERE orders.user_id = user.id
AND orders.id = orderdetail.orders_id
5.3.2 定义po类
在Orders类中增加User属性。
在Orders类中增加List<Orderdetail> orderdetails属性
5.3.3 Mapper.xml
<select id="findOrdersDetailList"resultMap="userorderdetailmap">
SELECT
orders.*,
user.username,
user.address,
orderdetail.id orderdetail_id,
orderdetail.items_id,
orderdetail.items_num
FROM orders,user,orderdetail
WHERE orders.user_id = user.id
AND orders.id = orderdetail.orders_id
</select>
5.3.4 定义resultMap
<!-- 订单信息resultmap -->
<resultMap type="cn.itcast.mybatis.po.Orders"id="userorderdetailmap">
<id property="id"column="id"/>
<result property="user_id" column="user_id"/>
<result property="number" column="number"/>
<association property="user" javaType="cn.itcast.mybatis.po.User">
<id property="id" column="user_id"/>
<result property="username" column="username"/>
<result property="address" column="address"/>
</association>
<collection property="orderdetails"ofType="cn.itcast.mybatis.po.Orderdetail">
<id property="id" column="orderdetail_id"/>
<result property="items_id"column="items_id"/>
<result property="items_num"column="items_num"/>
</collection>
</resultMap>
黄色部分和上边一对一查询订单及用户信息定义的resultMap同样。
collection部分定义了查询订单明细信息。
collection:表示关联查询结果集
property="orderdetails":关联查询的结果集存储在cn.itcast.mybatis.po.Orders上哪个属性。
ofType="cn.itcast.mybatis.po.Orderdetail":指定关联查询的结果集中的对象类型即List中的对象类型。
<id />及<result/>的意义同一对一查询。
5.3.4.1 resultMap使用继承
上边定义的resultMap中黄色部分和一对一查询订单信息的resultMap同样,这里使用继承能够不再填写反复的内容。例如以下:
<resultMaptype="cn.itcast.mybatis.po.Orders" id="userorderdetailmap" extends="userordermap">
<collection property="orderdetails" ofType="cn.itcast.mybatis.po.Orderdetail">
<id property="id" column="orderdetail_id"/>
<result property="items_id"column="items_id"/>
<result property="items_num"column="items_num"/>
</collection>
</resultMap>
使用extends继承订单信息userordermap。
5.3.5 Mapper接口:
public List<Orders>findOrdersDetailList() throws Exception;
5.3.6 測试:
Public void testfindOrdersDetailList()throws Exception{
//获取session
SqlSession session = sqlSessionFactory.openSession();
//获限mapper接口实例
UserMapper userMapper = session.getMapper(UserMapper.class);
//查询订单信息
List<Orders> list = userMapper.findOrdersDetailList();
System.out.println(list);
//关闭session
session.close();
}
5.3.7 小结
使用collection完毕关联查询,将关联查询信息映射到集合对象。
5.4 多对多查询
5.4.1 查询用户购买的商品信息
5.4.1.1 需求
查询用户购买的商品信息。
5.4.1.2 sql
须要查询全部用户信息,关联查询订单及订单明细信息,订单明细信息中关联查询商品信息
SELECT
orders.*,
USER.username,
USER.address,
orderdetail.idorderdetail_id,
orderdetail.items_id,
orderdetail.items_num,
items.nameitems_name,
items.detailitems_detail
FROM
orders,
USER,
orderdetail,
items
WHERE
orders.user_id= USER .id
AND orders.id = orderdetail.orders_id
AND orderdetail.items_id = items.id
5.4.1.3 po定义
在User中加入List<Orders> orders 属性,在Orders类中加入List<Orderdetail>orderdetails属性
5.4.1.4resultMap
须要关联查询映射的信息是:订单、订单明细、商品信息
订单:一个用户相应多个订单。使用collection映射到用户对象的订单列表属性中
订单明细:一个订单相应多个明细。使用collection映射到订单对象中的明细属性中
商品信息:一个订单明细相应一个商品。使用association映射到订单明细对象的商品属性中。
<!-- 一对多查询
查询用户信息、关联查询订单、订单明细信息、商品信息
-->
<resultMap type="cn.itcast.mybatis.po.User" id="userOrderListResultMap">
<id column="user_id"property="id"/>
<result column="username"property="username"/>
<collection property="orders"ofType="cn.itcast.mybatis.po.Orders">
<id column="id"property="id"/>
<result property="number" column="number"/>
<collection property="orderdetails"ofType="cn.itcast.mybatis.po.Orderdetail">
<id column="orderdetail_id" property="id"/>
<result property="ordersId"column="id"/>
<result property="itemsId"column="items_id"/>
<result property="itemsNum"column="items_num"/>
<association property="items"javaType="cn.itcast.mybatis.po.Items">
<id column="items_id" property="id"/>
<result column="items_name" property="name"/>
<result column="items_detail" property="detail"/>
</association>
</collection>
</collection>
</resultMap>
5.4.2 小结
一对多是多对多的特例。例如以下需求:
查询用户购买的商品信息。用户和商品的关系是多对多关系。
需求1:
查询字段:用户账号、username称、用户性别、商品名称、商品价格(最常见)
企业开发中常见明细列表,用户购买商品明细列表,
使用resultType将上边查询列映射到pojo输出。
需求2:
查询字段:用户账号、username称、购买商品数量、商品明细(鼠标移上显示明细)
使用resultMap将用户购买的商品明细列表映射到user对象中。
5.5 resultMap小结
resultType:
作用:
将查询结果依照sql列名pojo属性名一致性映射到pojo中。
场合:
常见一些明细记录的展示。比方用户购买商品明细。将关联查询信息所有展示在页面时,此时可直接使用resultType将每一条记录映射到pojo中,在前端页面遍历list(list中是pojo)就可以。
resultMap:
使用association和collection完毕一对一和一对多高级映射(对结果有特殊的映射要求)。
association:
作用:
将关联查询信息映射到一个pojo对象中。
场合:
为了方便查询关联信息能够使用association将关联订单信息映射为用户对象的pojo属性中。比方:查询订单及关联用户信息。
使用resultType无法将查询结果映射到pojo对象的pojo属性中。依据对结果集查询遍历的须要选择使用resultType还是resultMap。
collection:
作用:
将关联查询信息映射到一个list集合中。
场合:
为了方便查询遍历关联信息能够使用collection将关联信息映射到list集合中。比方:查询用户权限范围模块及模块下的菜单,可使用collection将模块映射到模块list中,将菜单列表映射到模块对象的菜单list属性中。这种作的目的也是方便对查询结果集进行遍历查询。
假设使用resultType无法将查询结果映射到list集合中。
5.6 延迟载入
须要查询关联信息时。使用mybatis延迟载入特性可有效的降低数据库压力,首次查询仅仅查询主要信息,关联信息等用户获取时再载入。
5.6.1 打开延迟载入开关
在mybatis核心配置文件里配置:
lazyLoadingEnabled、aggressiveLazyLoading
设置项 |
描写叙述 |
同意值 |
默认值 |
全局性设置懒载入。假设设为‘false’,则全部相关联的都会被初始化载入。 |
true | false |
false |
|
当设置为‘true’的时候,懒载入的对象可能被不论什么懒属性所有载入。否则,每一个属性都按需载入。 |
true | false |
true |
<settings>
<setting name="lazyLoadingEnabled" value="true"/>
<setting name="aggressiveLazyLoading" value="false"/>
</settings>
5.6.2 一对一查询延迟载入
5.6.2.1 需求
查询订单信息,关联查询用户信息。
默认仅仅查询订单信息。当须要查询用户信息时再去查询用户信息。
5.6.2.2 Sql语句:
SELECT
orders.*
FROM
orders
5.6.2.3 定义po类
在Orders类中增加User属性。
5.6.2.4 Mapper.xml
<selectid="findOrdersList3"resultMap="userordermap2">
SELECT
orders.*
FROM
orders
</select>
5.6.2.5 定义resultMap
<!-- 订单信息resultmap -->
<resultMap type="cn.itcast.mybatis.po.Orders"id="userordermap2">
<id property="id" column="id"/>
<result property="user_id"column="user_id"/>
<result property="number"column="number"/>
<association property="user"javaType="cn.itcast.mybatis.po.User"select="findUserById" column="user_id"/>
</resultMap>
association:
select="findUserById":指定关联查询sql为findUserById
column="user_id":关联查询时将users_id列的值传入findUserById
最后将关联查询结果映射至cn.itcast.mybatis.po.User。
5.6.2.6 Mapper接口:
public List<Orders>findOrdersList3() throws Exception;
5.6.2.7 測试:
Public void testfindOrdersList3()throws Exception{
//获取session
SqlSession session = sqlSessionFactory.openSession();
//获限mapper接口实例
UserMapper userMapper = session.getMapper(UserMapper.class);
//查询订单信息
List<Orders> list = userMapper.findOrdersList3();
System.out.println(list);
//開始载入,通过orders.getUser方法进行载入
for(Orders orders:list){
System.out.println(orders.getUser());
}
//关闭session
session.close();
}
5.6.2.8 延迟载入的思考
不使用mybatis提供的延迟载入功能能否够实现延迟载入?
实现方法:
针对订单和用户两个表定义两个mapper方法。
1、订单查询mapper方法
2、依据用户id查询用户信息mapper方法
默认使用订单查询mapper方法仅仅查询订单信息。
当须要关联查询用户信息时再调用依据用户id查询用户信息mapper方法查询用户信息。
5.6.3 一对多延迟载入
一对多延迟载入的方法同一对一延迟载入,在collection标签中配置select内容。
本部分内容自学。
5.6.4 延迟载入小结
作用:
当须要查询关联信息时再去数据库查询,默认不去关联查询。提高数据库性能。
仅仅有使用resultMap支持延迟载入设置。
场合:
当仅仅有部分记录须要关联查询其他信息时,此时可按需延迟载入,须要关联查询时再向数据库发出sql,以提高数据库性能。
当所有须要关联查询信息时,此时不用延迟载入。直接将关联查询信息所有返回就可以,可使用resultType或resultMap完毕映射。
6 查询缓存
6.1 mybatis缓存介绍
例如以下图,是mybatis一级缓存和二级缓存的差别图解:
Mybatis一级缓存的作用域是同一个SqlSession。在同一个sqlSession中两次运行同样的sql语句。第一次运行完成会将数据库中查询的数据写到缓存(内存)。第二次会从缓存中获取数据将不再从数据库查询,从而提高查询效率。当一个sqlSession结束后该sqlSession中的一级缓存也就不存在了。Mybatis默认开启一级缓存。
Mybatis二级缓存是多个SqlSession共享的,其作用域是mapper的同一个namespace。不同的sqlSession两次运行同样namespace下的sql语句且向sql中传递參数也同样即终于运行同样的sql语句,第一次运行完成会将数据库中查询的数据写到缓存(内存),第二次会从缓存中获取数据将不再从数据库查询。从而提高查询效率。Mybatis默认没有开启二级缓存须要在setting全局參数中配置开启二级缓存。
6.2 一级缓存
6.2.1 原理
下图是依据id查询用户的一级缓存图解:
一级缓存区域是依据SqlSession为单位划分的。
每次查询会先从缓存区域找,假设找不到从数据库查询。查询到数据将数据写入缓存。
Mybatis内部存储缓存使用一个HashMap,key为hashCode+sqlId+Sql语句。value为从查询出来映射生成的java对象
sqlSession运行insert、update、delete等操作commit提交后会清空缓存区域。
6.2.2 測试1
//获取session
SqlSession session = sqlSessionFactory.openSession();
//获限mapper接口实例
UserMapper userMapper = session.getMapper(UserMapper.class);
//第一次查询
User user1 = userMapper.findUserById(1);
System.out.println(user1);
//第二次查询,因为是同一个session则不再向数据发出语句直接从缓存取出
User user2 = userMapper.findUserById(1);
System.out.println(user2);
//关闭session
session.close();
6.2.3 測试2
//获取session
SqlSession session = sqlSessionFactory.openSession();
//获限mapper接口实例
UserMapper userMapper = session.getMapper(UserMapper.class);
//第一次查询
User user1 = userMapper.findUserById(1);
System.out.println(user1);
//在同一个session运行更新
User user_update = new User();
user_update.setId(1);
user_update.setUsername("李奎");
userMapper.updateUser(user_update);
session.commit();
//第二次查询,尽管是同一个session可是因为运行了更新操作session的缓存被清空,这里又一次发出sql操作
User user2 = userMapper.findUserById(1);
System.out.println(user2);
6.3 二级缓存
6.3.1 原理
下图是多个sqlSession请求UserMapper的二级缓存图解。
二级缓存区域是依据mapper的namespace划分的,同样namespace的mapper查询数据放在同一个区域,假设使用mapper代理方法每一个mapper的namespace都不同,此时能够理解为二级缓存区域是依据mapper划分。
每次查询会先从缓存区域找,假设找不到从数据库查询,查询到数据将数据写入缓存。
Mybatis内部存储缓存使用一个HashMap,key为hashCode+sqlId+Sql语句。value为从查询出来映射生成的java对象
sqlSession运行insert、update、delete等操作commit提交后会清空缓存区域。
6.3.2 开启二级缓存:
在核心配置文件SqlMapConfig.xml中增加
<setting name="cacheEnabled"value="true"/>
描写叙述 |
同意值 |
默认值 |
|
cacheEnabled |
对在此配置文件下的全部cache 进行全局性开/关设置。 |
true false |
true |
要在你的Mapper映射文件里加入一行: <cache /> ,表示此mapper开启二级缓存。
6.3.3 实现序列化
二级缓存须要查询结果映射的pojo对象实现java.io.Serializable接口实现序列化和反序列化操作,注意假设存在父类、成员pojo都须要实现序列化接口。
publicclass Orders implements Serializable
publicclass User implements Serializable
....
6.3.4 測试
//获取session1
SqlSession session1 = sqlSessionFactory.openSession();
UserMapper userMapper = session1.getMapper(UserMapper.class);
//使用session1运行第一次查询
User user1 = userMapper.findUserById(1);
System.out.println(user1);
//关闭session1
session1.close();
//获取session2
SqlSession session2 = sqlSessionFactory.openSession();
UserMapper userMapper2 = session2.getMapper(UserMapper.class);
//使用session2运行第二次查询,因为开启了二级缓存这里从缓存中获取数据不再向数据库发出sql
User user2 = userMapper2.findUserById(1);
System.out.println(user2);
//关闭session2
session2.close();
6.3.5 禁用二级缓存
在statement中设置useCache=false能够禁用当前select语句的二级缓存。即每次查询都会发出sql去查询。默认情况是true,即该sql使用二级缓存。
<selectid="findOrderListResultMap" resultMap="ordersUserMap" useCache="false">
6.3.6 刷新缓存
在mapper的同一个namespace中。假设有其他insert、update、delete操作数据后须要刷新缓存。假设不运行刷新缓存会出现脏读。
设置statement配置中的flushCache="true" 属性,默认情况下为true即刷新缓存。假设改成false则不会刷新。
使用缓存时假设手动改动数据库表中的查询数据会出现脏读。
例如以下:
<insertid="insertUser" parameterType="cn.itcast.mybatis.po.User" flushCache="true">
6.3.7 Mybatis Cache參数
flushInterval(刷新间隔)能够被设置为随意的正整数,并且它们代表一个合理的毫秒形式的时间段。
默认情况是不设置。也就是没有刷新间隔,缓存只调用语句时刷新。
size(引用数目)能够被设置为随意正整数,要记住你缓存的对象数目和你执行环境的可用内存资源数目。默认值是1024。
readOnly(仅仅读)属性能够被设置为true或false。仅仅读的缓存会给全部调用者返回缓存对象的同样实例。
因此这些对象不能被改动。这提供了非常重要的性能优势。
可读写的缓存会返回缓存对象的拷贝(通过序列化)。这会慢一些,可是安全,因此默认是false。
例如以下样例:
<cache eviction="FIFO" flushInterval="60000" size="512" readOnly="true"/>
这个更高级的配置创建了一个 FIFO 缓存,并每隔 60 秒刷新,存数结果对象或列表的 512 个引用,并且返回的对象被觉得是仅仅读的,因此在不同线程中的调用者之间改动它们会导致冲突。
可用的收回策略有, 默认的是 LRU:
1. LRU – 近期最少使用的:移除最长时间不被使用的对象。
2. FIFO – 先进先出:按对象进入缓存的顺序来移除它们。
3. SOFT – 软引用:移除基于垃圾回收器状态和软引用规则的对象。
4. WEAK – 弱引用:更积极地移除基于垃圾收集器状态和弱引用规则的对象。
6.3.8 mybatis整合ehcache
EhCache是一个纯Java的进程内缓存框架,是一种广泛使用的开源Java分布式缓存,具有高速、精干等特点。是Hibernate中默认的CacheProvider。
6.3.8.1 mybatis整合ehcache原理
mybatis提供二级缓存Cache接口。例如以下:
它的默认实现类:
通过实现Cache接口能够实现mybatis缓存数据通过其他缓存数据库整合。mybatis的特长是sql操作,缓存数据的管理不是mybatis的特长,为了提高缓存的性能将mybatis和第三方的缓存数据库整合。比方ehcache、memcache、redis等。
6.3.8.2 第一步:引入缓存的依赖包
maven坐标:
<dependency>
<groupId>org.mybatis.caches</groupId>
<artifactId>mybatis-ehcache</artifactId>
<version>1.0.2</version>
</dependency>
6.3.8.3 第二步:引入缓存配置文件
classpath下加入:ehcache.xml
内容例如以下:
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="../config/ehcache.xsd">
<diskStore path="F:developehcache"/>
<defaultCache
maxElementsOnDisk="10000000"
eternal="false"
overflowToDisk="false"
timeToIdleSeconds="120"
timeToLiveSeconds="120"
diskExpiryThreadIntervalSeconds="120"
memoryStoreEvictionPolicy="LRU">
</defaultCache>
</ehcache>
属性说明:
diskStore:指定数据在磁盘中的存储位置。
defaultCache:当借助CacheManager.add("demoCache")创建Cache时。EhCache便会採用<defalutCache/>指定的的管理策略
下面属性是必须的:
maxElementsInMemory - 在内存中缓存的element的最大数目
maxElementsOnDisk - 在磁盘上缓存的element的最大数目。若是0表示无穷大
eternal - 设定缓存的elements是否永远只是期。
假设为true。则缓存的数据始终有效,假设为false那么还要依据timeToIdleSeconds,timeToLiveSeconds推断
overflowToDisk - 设定当内存缓存溢出的时候是否将过期的element缓存到磁盘上
下面属性是可选的:
timeToIdleSeconds - 当缓存在EhCache中的数据前后两次訪问的时间超过timeToIdleSeconds的属性取值时。这些数据便会删除,默认值是0,也就是可闲置时间无穷大
timeToLiveSeconds - 缓存element的有效生命期。默认是0.,也就是element存活时间无穷大
diskSpoolBufferSizeMB 这个參数设置DiskStore(磁盘缓存)的缓存区大小.默认是30MB.每一个Cache都应该有自己的一个缓冲区.
diskPersistent- 在VM重新启动的时候是否启用磁盘保存EhCache中的数据,默认是false。
diskExpiryThreadIntervalSeconds - 磁盘缓存的清理线程执行间隔,默认是120秒。每一个120s。对应的线程会进行一次EhCache中数据的清理工作
memoryStoreEvictionPolicy - 当内存缓存达到最大,有新的element增加的时候,移除缓存中element的策略。默认是LRU(近期最少使用)。可选的有LFU(最不常使用)和FIFO(先进先出)
6.3.8.4 第三步:开启ehcache缓存
EhcacheCache是ehcache对Cache接口的实现:
改动mapper.xml文件,在cache中指定EhcacheCache。
<cache type="org.mybatis.caches.ehcache.EhcacheCache"/>
依据需求调整缓存參数:
<cache type="org.mybatis.caches.ehcache.EhcacheCache">
<property name="timeToIdleSeconds" value="3600"/>
<property name="timeToLiveSeconds" value="3600"/>
<!-- 同ehcache參数maxElementsInMemory-->
<property name="maxEntriesLocalHeap"value="1000"/>
<!-- 同ehcache參数maxElementsOnDisk -->
<property name="maxEntriesLocalDisk" value="10000000"/>
<property name="memoryStoreEvictionPolicy" value="LRU"/>
</cache>
6.3.9 应用场景
对于訪问多的查询请求且用户对查询结果实时性要求不高。此时可採用mybatis二级缓存技术减少数据库訪问量,提高訪问速度。业务场景比方:耗时较高的统计分析sql、电话账单查询sql等。
实现方法例如以下:通过设置刷新间隔时间,由mybatis每隔一段时间自己主动清空缓存。依据数据变化频率设置缓存刷新间隔flushInterval。比方设置为30分钟、60分钟、24小时等,依据需求而定。
6.3.10 局限性
mybatis二级缓存对细粒度的数据级别的缓存实现不好。比方例如以下需求:对商品信息进行缓存,由于商品信息查询訪问量大。可是要求用户每次都能查询最新的商品信息。此时假设使用mybatis的二级缓存就无法实现当一个商品变化时仅仅刷新该商品的缓存信息而不刷新其他商品的信息,由于mybaits的二级缓存区域以mapper为单位划分,当一个商品信息变化会将所有商品信息的缓存数据所有清空。解决此类问题须要在业务层依据需求对数据有针对性缓存。
7 与spring整合
实现mybatis与spring进行整合。通过spring管理SqlSessionFactory、mapper接口。
7.1 mybatis与spring整合jar
mybatis官方提供与mybatis与spring整合jar包:
还包含其他jar:
spring3.2.0
mybatis3.2.7
dbcp连接池
数据库驱动
參考:
7.2 Mybatis配置文件
在classpath下创建mybatis/SqlMapConfig.xml
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>
<!—使用自己主动扫描器时,mapper.xml文件假设和mapper.java接口在一个文件夹则此处不用定义mappers -->
<mappers>
<packagename="cn.itcast.mybatis.mapper" />
</mappers>
</configuration>
7.3 Spring配置文件:
在classpath下创建applicationContext.xml。定义数据库链接池、SqlSessionFactory。
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.2.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.2.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.2.xsd">
<!-- 载入配置文件 -->
<context:property-placeholder location="classpath:db.properties"/>
<!-- 数据库连接池 -->
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"destroy-method="close">
<property name="driverClassName" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
<property name="maxActive" value="10"/>
<property name="maxIdle" value="5"/>
</bean>
<!-- mapper配置 -->
<!-- 让spring管理sqlsessionfactory使用mybatis和spring整合包中的 -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<!-- 数据库连接池 -->
<property name="dataSource" ref="dataSource" />
<!-- 载入mybatis的全局配置文件 -->
<property name="configLocation" value="classpath:mybatis/SqlMapConfig.xml" />
</bean>
</beans>
注意:在定义sqlSessionFactory时指定数据源dataSource和mybatis的配置文件。
7.4 Mapper编写的三种方法
7.4.1 Dao接口实现类继承SqlSessionDaoSupport
使用此种方法即原始dao开发方法,须要编写dao接口。dao接口实现类、映射文件。
1、 在sqlMapConfig.xml中配置映射文件的位置
<mappers>
<mapper resource="mapper.xml文件的地址" />
<mapper resource="mapper.xml文件的地址" />
</mappers>
2、 定义dao接口
3、 dao接口实现类集成SqlSessionDaoSupport
dao接口实现类方法中能够this.getSqlSession()进行数据增删改查。
4、 spring 配置
<beanid=" "class="mapper接口的实现">
<property name="sqlSessionFactory"ref="sqlSessionFactory"></property>
</bean>
7.4.2 使用org.mybatis.spring.mapper.MapperFactoryBean
此方法即mapper接口开发方法,仅仅需定义mapper接口,不用编写mapper接口实现类。每一个mapper接口都须要在spring配置文件里定义。
1、 在sqlMapConfig.xml中配置mapper.xml的位置
假设mapper.xml和mappre接口的名称同样且在同一个文件夹,这里能够不用配置
<mappers>
<mapper resource="mapper.xml文件的地址" />
<mapper resource="mapper.xml文件的地址" />
</mappers>
2、 定义mapper接口
3、 Spring中定义
<beanid="" class="org.mybatis.spring.mapper.MapperFactoryBean">
<property name="mapperInterface"value="mapper接口地址"/>
<property name="sqlSessionFactory" ref="sqlSessionFactory"/>
</bean>
7.4.3 使用mapper扫描器
此方法即mapper接口开发方法,仅仅需定义mapper接口。不用编写mapper接口实现类。仅仅须要在spring配置文件里定义一个mapper扫描器,自己主动扫描包中的mapper接口生成代代理对象。
1、 mapper.xml文件编写,
2、 定义mapper接口
注意mapper.xml的文件名称和mapper的接口名称保持一致,且放在同一个文件夹
3、 配置mapper扫描器
<beanclass="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="mapper接口包地址"></property>
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
</bean>
basePackage:扫描包路径,中间能够用逗号或分号分隔定义多个包
4、 使用扫描器后从spring容器中获取mapper的实现对象
假设将mapper.xml和mapper接口的名称保持一致且放在一个文件夹则不用在sqlMapConfig.xml中进行配置
8 Mybatis逆向project
使用官方站点的mapper自己主动生成工具mybatis-generator-core-1.3.2来生成po类和mapper映射文件。
8.1 第一步:mapper生成配置文件:
在generatorConfig.xml中配置mapper生成的具体信息。注意改下几点:
1、 加入要生成的数据库表
2、 po文件所在包路径
3、 mapper文件所在包路径
配置文件例如以下:
详见generatorSqlmapCustomproject
8.2 第二步:使用java类生成mapper文件:
Public void generator() throws Exception{
List<String>warnings = newArrayList<String>();
boolean overwrite = true;
FileconfigFile = newFile("generatorConfig.xml");
ConfigurationParsercp = newConfigurationParser(warnings);
Configurationconfig = cp.parseConfiguration(configFile);
DefaultShellCallbackcallback = new DefaultShellCallback(overwrite);
MyBatisGeneratormyBatisGenerator = new MyBatisGenerator(config,
callback,warnings);
myBatisGenerator.generate(null);
}
Public static voidmain(String[] args) throws Exception {
try {
GeneratorSqlmapgeneratorSqlmap = new GeneratorSqlmap();
generatorSqlmap.generator();
}catch(Exception e) {
e.printStackTrace();
}
}
8.3 第三步:拷贝生成的mapper文件到project中指定的文件夹中
8.3.1 Mapper.xml
Mapper.xml的文件拷贝至mapper文件夹内
8.3.2 Mapper.java
Mapper.java的文件拷贝至mapper 文件夹内
注意:mapper xml文件和mapper.java文件在一个文件夹内且文件名称同样。
8.3.3 第四步Mapper接口測试
学会使用mapper自己主动生成的增、删、改、查方法。
//删除符合条件的记录
int deleteByExample(UserExample example);
//依据主键删除
int deleteByPrimaryKey(String id);
//插入对象全部字段
int insert(User record);
//插入对象不为空的字段
int insertSelective(User record);
//自己定义查询条件查询结果集
List<User>selectByExample(UserExample example);
//依据主键查询
UserselectByPrimaryKey(Stringid);
//依据主键将对象中不为空的值更新至数据库
int updateByPrimaryKeySelective(User record);
//依据主键将对象中全部字段的值更新至数据库
int updateByPrimaryKey(User record);
8.4 逆向project注意事项
8.4.1 Mapper文件内容不覆盖而是追加
XXXMapper.xml文件已经存在时。假设进行又一次生成则mapper.xml文件内容不被覆盖而是进行内容追加。结果导致mybatis解析失败。
解决方法:删除原来已经生成的mapper xml文件再进行生成。
Mybatis自己主动生成的po及mapper.java文件不是内容而是直接覆盖没有此问题。
8.4.2 Table schema问题
下边是关于针对oracle数据库表生成代码的schema问题:
Schma即数据库模式,oracle中一个用户相应一个schema。能够理解为用户就是schema。
当Oralce数据库存在多个schema能够訪问同样的表名时,使用mybatis生成该表的mapper.xml将会出现mapper.xml内容反复的问题,结果导致mybatis解析错误。
解决方法:在table中填写schema,例如以下:
<table schema="XXXX"tableName=" " >
XXXX即为一个schema的名称,生成后将mapper.xml的schema前缀批量去掉,假设不去掉当oracle用户变更了sql语句将查询失败。
快捷操作方式:mapper.xml文件里批量替换:“from XXXX.”为空
Oracle查询对象的schema可从dba_objects中查询,例如以下:
select * from dba_objects