一、 MyBatis介绍
序号 | Mybatis | hibernate |
1. | iBatis SSI 2002年诞生 Cliton begin | 2001年 Given King |
2. | 2010年5月由apache投奔google | Jboss,apache |
3. | 基于SQL 面向结果集 | 基于面向对象 HQL |
4. | 效率高 | 效率低 |
5. | SqlSessionFactory | SessionFactory |
6. | SqlSession | Session |
7. | sqlMapConfig.xml | Hibernate.cfg.xml |
8. | userMapper.xml | 映射文件 user.hbm.xml |
Hibernate面向对象,它使用HQL,可以无需写SQL语句。全自动ORM。
Mybatis面向对象,它使用SQL语句,很依赖于SQL语句。半自动ORM。
二、 体验Mybatis
1. 创建一个java工程
2. 导入jar
mybatis-3.2.2.jar 核心jar
mysql-connector-java-5.1.10-bin.jar 数据库访问
asm-3.3.1.jar 增强类
cglib-2.2.2.jar 动态代理
commons-logging-1.1.1.jar 通用日志
javassist-3.17.1-GA.jar java助手
log4j-1.2.17.jar 日志
slf4j-api-1.7.5.jar 日志
slf4j-log4j12-1.7.5.jar 日志
3. 创建核心配置文件 sqlMapConfig.xml
配置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>
<!-- 1.数据源 -->
<!-- 2.事务控制 -->
<!-- 配置一个开发环境,可以配置多个,在具体用时切换 -->
<environments default="test">
<environment id="test"><!-- 测试环境 -->
<dataSource type="POOLED"></dataSource><!-- 数据源 POOLED/UNPOOLED/JNDI -->
<transactionManager type="JDBC"></transactionManager><!-- 事务管理 JDBC/MANAGED -->
</environment>
</environments>
<!-- 加载映射文件 mapper -->
<mappers>
<mapper resource=""/>
</mappers>
</configuration>
4. 创建映射文件 userMapper.xml
配置mapper提示
Mapper任务
1) 配置实体对象,和对象之间的映射关系
2) Sql映射 insert/update/delete/select
查询所有
<?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/mapper.UserMapper"><!-- 命名空间,可以随意些,只要不冲突 -->
<!-- 对象映射 -->
<!-- 查询所有 resultType设置返回值 -->
<select id="findAll" resultType="cn.itcast.Person">
<!-- SQL语句 -->
select * from Person
</select>
</mapper>
5. 测试类,创建SqlSessionFactory,从中获取sqlSession
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.Test;
import cn.itcast.Person;
/**
* @Description:
* @Author:陈子枢
* @Company:http://java.itcast.cn
* @CreateDate:2014-7-21
*/
public class TestMybatis {
@Test
public void test() throws IOException{
/*
* 1. 获得sqlSessionFactory
* 2. 获得sqlSession
* 3. 调用在mapper文件中配置的sql语句,查询所有
*/
String resource = "sqlMapConfig.xml";//定位核心配置文件
//InputStream is = Resources.getResourceAsStream(resource);
InputStream is = this.getClass().getResourceAsStream(resource);
SqlSessionFactory sf = new SqlSessionFactoryBuilder().build(is);//创建sessionFactory
SqlSession session = sf.openSession();//获取到sqlSession
//如何调用mapper中的方法namespace+id
List<Person> personList = session.selectList("cn.itcast/mapper.UserMapper.findAll");//找到mapper文件中对应的方法
for(Person p : personList){
System.out.println(p);
}
}
}
三、 增删改查实现
1. 查询所有
<select id="findAll" resultType="cn.itcast.Person">
<!-- SQL语句 -->
select * from Person
</select>
l 问题:myBatis讲结果集映射到对象时
它使用的是数据库表的字段,还是结果集中的字段。
myBatisORM转换时,它对应的是结果集中的字段名。
l 在sql语句中获取参数
Ibatis #id#
Mybatis #{id} ${id}
2. 新增记录
<!-- 新增,以实体来封装参数,在sql语句中有参数;隐患sql注入 preparestatement;解决sql注入,通过属性类型判断,如果是整数直接写,如果字符串类型两边加单撇-->
<insert id="insert" parameterType="cn.itcast.Person">
INSERT INTO person (id,user_name,age) VALUES(#{id},#{userName},#{age})
</insert>
@Test
public void testInsert(){
String resource = "sqlMapConfig.xml";//定位核心配置文件
InputStream is = this.getClass().getResourceAsStream(resource);
SqlSessionFactory sf = new SqlSessionFactoryBuilder().build(is);//创建sessionFactory
SqlSession session = sf.openSession();//获取到sqlSession
Person p = new Person();
p.setId(4);
p.setUserName("jenny");
p.setAge(3);
session.insert("cn.itcast.mapper.UserMapper.insert", p);
session.commit();//默认是不自动提交,必须手工提交
}
3. 修改记录
映射文件mapper.xml
<update id="update" parameterType="cn.itcast.Person">
UPDATE person set user_name=#{userName},age=#{age}
WHERE id=#{id}
</update>
测试类
@Test
public void testUpdate(){
String resource = "sqlMapConfig.xml";//定位核心配置文件
InputStream is = this.getClass().getResourceAsStream(resource);
SqlSessionFactory sf = new SqlSessionFactoryBuilder().build(is);//创建sessionFactory
SqlSession session = sf.openSession();//获取到sqlSession
Person p = session.selectOne("cn.itcast.mapper.UserMapper.get", 2);//修改id=2的记录
p.setId(2);
p.setAge(38);
session.update("cn.itcast.mapper.UserMapper.update", p);
session.commit();//默认是不自动提交,必须手工提交
}
4. 删除记录
Mapper文件中
<delete id="deleteById" parameterType="integer">
DELETE FROM person
WHERE id=#{xid}
</delete>
在测试类中调用
public void testDeleteById(){
String resource = "sqlMapConfig.xml";//定位核心配置文件
InputStream is = this.getClass().getResourceAsStream(resource);
SqlSessionFactory sf = new SqlSessionFactoryBuilder().build(is);//创建sessionFactory
SqlSession session = sf.openSession();//获取到sqlSession
session.delete("cn.itcast.mapper.UserMapper.deleteById", 2);
session.commit();//默认是不自动提交,必须手工提交
}
四、 对mapper文件小结:
l 设置命名空间,目标区分方法
l 创建resultMap,它是mybatis最大的特色,对象的ORM就由它来转换,非常灵活,Mybatis的强大;
n <id>主键
n <result>普通属性
n 常用两个属性 property 指实体的属性;columnSQL查询的结果集的列
l 标签 select/insert/update/delete
n 标签有id,id在同一个mapper文件中不允许重复,不同文件重名通过命名空间
n 参数:parameterMap 废弃,它为了兼容前期的项目;parameterType 它支持很丰富的类型 int/integer/string/double/list/map/实体
n 返回值:resultType简单类型 resultMap 复杂类型,一般针对配置resultMap
n 参数,返回值,当遇到集合时,它声明的是集合中的元素类型
n SQL语句,不区分大小写,它使用的默认使用PrepareStatement,预编译,防止SQL注入。
n 获得参数#{NAME}
五、 对调用小结:
1. 获得一个sqlSessionFactory
SqlSessionFactory sf = new SqlSessionFactoryBuilder().build(is);
2. 获得sqlSession
SqlSession session = sf.openSession();
3. 查询所有
session.selectList
4. 查询一个
seesion.selectOne
5. 新增
Session.insert
6. 修改
Session.update
7. 删除
Session.delete
六、 简化扩展
1. 类调用方法随意,可以将delete改为insert,一样执行,因为它是由命名空间和id决定
2. 简化命名空间,随意命名,只要保证项目中没有同命名空间和同id
3. Sql标签 ,多处调用引用它,Mybatis底层就是拼接字符串
<sql id="cols">
id,user_name,age
</sql>
SELECT<include refid="cols"/>FROM person
4. 可以给type赋别名
在Mapper文件中可以简写调用别名
<insert id="insert" parameterType="Person">
在sqlMapConfig.xml中定义别名,注意顺序
<typeAliases>
<typeAlias type="cn.itcast.Person" alias="Person"/>
</typeAliases>
MyBatis介绍
MyBatis 是支持普通 SQL 查询,存储过程和高级映射的优秀持久层框架。MyBatis 消除 了几乎所有的 JDBC 代码和参数的手工设置以及结果集的检索。MyBatis 使用简单的 XML 或注解用于配置和原始映射,将接口和 Java 的 POJOs(Plan Old Java Objects,普通的 Java 对象)映射成数据库中的记录。
MyBatis的前身是IBatis,也是一个使用很广的持久化框架。和hibernate对比,MyBatis更基础,要求使用者自己控制的东西更多。mybatis完成了基本的一些ORM概念,但是没有Hibernate那么完善。要使用mybatis,程序员的关注点更集中于SQL和数据库结构设计。mybatis没有hibernate使用起来那么面向对象,所以,在使用mybatis的时候,hibernate的一些思想和设计需要改变。
MyBatis的好处:更底层,对性能控制更有优势。
既然Mybatis也是一种ORM框架,所以,肯定也有两类配置文件。第一类,用来配置MyBatis环境,比如数据库连接等。第二类,用来配置对象到数据库表的映射。在MyBatis中,不光要配置对象到数据库表的映射,包括对应的SQL,也需要自己来完成(相当于自己来完成hibernate生成对象CRUDsql的过程要自己来完成)。在学习mybatis的过程当中,要对比着hibernate来思考,可以更容易的理解。而且,mybatis在ibatis的基础上面,更多的吸收了标准ORM的一些思想,所以,在API的设计上面和Hibernate还是有相似的地方,可以辅助学习。
1. MyBatis官网下载jar包
http://code.google.com/p/mybatis/
解压到D:\javaenv\all_jar\mybatis-3.2.2
mybatis-3.2.2.jar 核心文件才684k
2. 体验MyBatis
创建User.java pojo对象
创建mybatis的配置文件sqlMapConfig.xml
注意:有些版本的myeclipse声明dtd文件后,没提示
a) 配置事务
JDBC
MANAGED
b) DataSource
UNPOOLED
POOLED
JNDI
c) 核心配置文件 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>
<environments default="default">
<environment id="default">
<transactionManager type="JDBC" />
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql:///mybatis"/>
<property name="username" value="root"/>
<property name="password" value="admin"/>
</dataSource>
</environment>
</environments>
</configuration>
这就是mybatis的一个典型的配置文件。在这个配置文件中,我们可以清楚得看到一些关键信息。第一,有一个environments这个元素,在这个元素里面配置的是一个一个的environment元素,每一个environment元素有一个id做为名字,而environments元素上面有一个default属性,引用environment元素的名称,很容易想象出他们之间的关系。在environments里面可以定义多个enviroment元素,使用default属性指定一个默认的environment。而在environment里面,定义了一组数据库相关的东西,包括transactionManager,这个元素代表着怎么去管理事务,后面还会详细讲到。而下面的dataSource就很明显了,代表连接数据库的相关信息。在这里,dataSource有一个属性type,这里我们写的是POOLED,很明显,使用连接池来做mybatis的连接提供。在dataSource里面,配置了四个属性,做为数据库连接信息配置。
d) 映射文件 UserMapping.xml
接下来,就是为我们的User对象完成映射了。前面说了,在mybatis中,需要自己去控制sql,所以,我们的配置文件会像下面这样。在User的同级包下,添加一个XML文件,起名为UserMapper.xml,代表这个文件是User对象的映射文件:
<?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.domain.UserMapper">
<insert id="save" keyProperty="id" useGeneratedKeys="true"
parameterType="cn.itcast.mybatis.domain.User">
insert into user (name,hireDate) values(#{name},#{hireDate})
</insert>
</mapper>
Mapper:代表这是一个对象的映射关系
namespace:为当前映射关系创建的命名空间,要引用这个映射关系里面定义的东西,需要带上这个命名空间。还有其他非常重要的作用,之后会看到。
insert:代表定义了一个插入操作(即SQL的insert操作)
id:为这个插入操作起一个名字,以后要保存user这个对象,其实就是要调用这个插入操作。
keyProperty:代表主键对应对象的属性名称。有点像hibernate里面那个id元素。
parameterType:mybatis里面非常重要的一个元素,代表这个insert操作对应的方法需要传入一个什么类型的对象。这里,我们就是要把User这个对象保存到数据库中,所以,我们这个插入方法的参数就是一个User类型的对象实例。
useGeneratedKeys:代表告诉mybatis,使用autoGeneratedKey来获取数据库帮我们自动生成的ID。(这个方法在jdbc中讲过)
INSERT INTO user(name,hiredate) values (#{name},#{hireDate}):非常重要的东西。
这就是一条完整的把USER对象插入到user表的SQL语句。但是在这个SQL语句里面,最重要的就是#{name},#{hireDate}这两个占位参数。这两个参数的意思就是:使用传入对象(parameterType)中的name,hireDate两个属性的值来填充这两个参数内容。
完成映射文件之后,在sqlMapConfig.xml中加入映射文件:
<mappers>
<mapper resource="cd/itcast/mybatis/domain/UserMapper.xml"/>
</mappers>
e) 插入操作的测试。创建一个USER表:
CREATE TABLE `user` (
`id` bigint(11) NOT NULL AUTO_INCREMENT,
`name` varchar(50) DEFAULT NULL,
`hiredate` date DEFAULT NULL,
PRIMARY KEY (`id`)
)
f) 导入jar包
D:\javaenv\all_jar\mybatis-3.2.2\lib
依赖包:
asm-3.3.1.jar
cglib-2.2.2.jar
commons-logging-1.1.1.jar
javassist-3.17.1-GA.jar
log4j-1.2.17.jar
slf4j-api-1.7.5.jar
slf4j-log4j12-1.7.5.jar
核心包:
mybatis-3.2.2.jar
数据库:
mysql-connector-java-5.1.26.jar
jUnit测试:
junit-4.8.2.jar
g) 【新建】操作
然后创建一个UserTest类
package test;
import java.io.IOException;
import java.io.InputStream;
import java.util.Date;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.Before;
import org.junit.Test;
import cn.itcast.mybatis.domain.User;
public class TestMybatis {
private static SqlSessionFactory sqlSessionFactory;
@Before
public void before() throws IOException{
String config = "sqlMapConfig.xml";
InputStream inputStream = Resources.getResourceAsStream(config);
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
}
@Test
public void testSave(){
SqlSession session = sqlSessionFactory.openSession();
try{
User u = new User();
u.setName("柳传志");
u.setHireDate(new Date());
session.insert("cn.itcast.mybatis.domain.UserMapper.save", u);
session.commit();
}catch(Exception e){
e.printStackTrace();
session.rollback();
}finally{
session.close();
}
}
}
1,Resources .getResourceAsStream("sqlMapConfig.xml"):使用Reources类的静态方法getResourceAsStream,从classpath:sqlMapConfig.xml位置读入配置文件,并装成inputStream。
2,SqlSessionFactory factory= new SqlSessionFactoryBuilder().build(inputStream):使用读入的流,创建SqlSessionFactory。可以从名字上类比hibernate。
3,SqlSession session = factory.openSession();使用factory开启一个SqlSession。SqlSession即是在mybatis中操作实体对象的主要的类了。
4,User u = new User();创建一个需要保存的实体类。
5,session.insert("cd.itcast.mybatis.domain.UserMapper.save", u):非常重要的方法,使用session的insert方法,对比上面的映射文件,即告诉mybatis现在调用的是一个标记到insert元素中的SQL语句,接着传入cd.itcast.mybatis.domain.UserMapper.save,很明显这个就是namespace+id拼成的,意思就是我要调用的是定义在cd.itcast.mybatis.domain.UserMapper这个映射关系中的名字为save的insert语句。这个语句需要传入一个cd.itcast.mybatis.domain.User对象实例做为参数,所以把创建的User实体做为参数传入。
6,session.commit():很明显,即提交事务。
7,session.close():和hibernate一样,使用完后,都必须要关闭session。
从整个代码上面可以很明显的猜测到mybatis的创建过程。
执行测试,保存成功。
但是看不到mybatis执行的sql,怎么办?
h) Log4j日志
在classpath中建立一个名叫log4j.properties的文件,内容如下:
log4j.rootLogger=DEBUG, Console
#Console
log4j.appender.Console=org.apache.log4j.ConsoleAppender
log4j.appender.Console.layout=org.apache.log4j.PatternLayout
log4j.appender.Console.layout.ConversionPattern=%-5p - %m%n
log4j.logger.org.apache=INFO
log4j.logger.cd.itcast.mybatis=DEBUG
很明显,log4j.logger.cd.itcast.mybatis意思就是要监控cd.itcast.mybatis包下面的所有mapper的动作。
再次运行:
DEBUG - ooo Using Connection [com.mysql.jdbc.JDBC4Connection@1b7c984]
DEBUG - ==> Preparing: insert into user (name,hireDate) values(?,?)
DEBUG - ==> Parameters: 柳传志(String), 2013-12-31 16:49:53.493(Timestamp)
可以很清楚的看到执行的sql和执行sql使用的参数。
i) 【修改】操作
在UserMapper.xml中加入:
<update id="update" parameterType="cd.itcast.mybatis.domain.User">
UPDATE user SET name = #{name},hiredate = #{hireDate} WHERE id = #{id}
</update>
1, update:代表当前定义了一个update操作(sql的update语句);
a) id:为当前操作定义了一个名字,用于调用
b) parameterType:update调用需要一个cd.itcast.mybatis.domain.User的类型实例做为参数。
2, UPDATE user SET name = #{name},hiredate = #{hireDate} WHERE id = #{id}:即完整的UPDATE语句,其中的#{name},#{hireDate},#{id}和insert语句中的完全一致。
修改测试:
@Test
public void testUpdate(){
SqlSession session = sqlSessionFactory.openSession();
try{
User u = new User();
u.setId(1L);
u.setName("杨元庆");
u.setHireDate(new Date());
session.update("cn.itcast.mybatis.domain.UserMapper.update", u);
session.commit();
}catch(Exception e){
e.printStackTrace();
session.rollback();
}finally{
session.close();
}
}
对照保存测试,很好理解,关键的一句:
session.update("cd.itcast.mybatis.domain.UserMapper.update", u):即调用在cd.itcast.mybatis.domain.UserMapper中定义的名字为update的Update语句,传入一个cd.itcast.mybatis.domain.User的对象实例。
运行测试,console打印:
DEBUG - ooo Using Connection [com.mysql.jdbc.JDBC4Connection@14cf7d6]
DEBUG - ==> Preparing: update user set name=?, hireDate=? where id=?
DEBUG - ==> Parameters: 杨元庆(String), 2013-12-31 17:02:14.225(Timestamp), 1(Long)
j) 【获取】一个用户Get操作
在UserMapper.xml中添加如下配置:
<select id="get" parameterType="long" resultType="cn.itcast.mybatis.domain.User">
select * from user where id=#{id}
</select>
1, select:代表这是一个SELECT语句
a) parameterType:代表要执行这个select语句需要传入一个类型为long的参数,即User对象的id
b) resultType:非常重要的东西,即完成ORM的映射关系所在。这里指定的cd.itcast.mybatis.domain.User代表,把结果集转换成一个User对象实例。注意,在这里有一个要求就是,属性名称和列的名称必须一致,才能完成转型。否则就需要自定义属性到字段的映射规则。
2, SELECT * FROM user WHERE id = #{id}:即查询语句。非常重要的一点,这个#{id}是mybatis在使用查询单个对象的时候默认使用的参数的名称。即传入的类型为long的值,mybatis就用#{id}来代替。
Get测试:
@Test
public void testGet(){
SqlSession session = sqlSessionFactory.openSession();
try{
User u = session.selectOne("cn.itcast.mybatis.domain.UserMapper.get", 1);
System.out.println(u);
}catch(Exception e){
e.printStackTrace();
}finally{
session.close();
}
}
其中很重要的一点:
User u = session.selectOne("cd.itcast.mybatis.domain.UserMapper.get",1l);
调用的是cd.itcast.mybatis.domain.UserMapper中定义的get对应的SELECT语句。注意这里使用的是session.selectOne,代表,选择的是一个。那么mybatis就会把查询到的结果集中的第一行数据按照列名和属性名一一对应的关系,把结果包装成一个User类型的实例。
从这里也能得出结论:在mapper文件中定义的sql语句的类型和在session中使用的调用方法是对应的。
运行测试:
DEBUG - ooo Using Connection [com.mysql.jdbc.JDBC4Connection@1b39df1]
DEBUG - ==> Preparing: select * from user where id=?
DEBUG - ==> Parameters: 1(Integer)
User [id=1, name=柳传志, hireDate=Tue Dec 31 00:00:00 CST 2013]
k) 【查询】多条
在UserMapper.xml中加入:
<select id="list" resultType="cn.itcast.mybatis.domain.User">
select * from user
</select>
1,非常重要的一点,这次返回的结果应该是一个List<User>,但是配置的resultType仍然是cd.itcast.mybatis.domain.User,mybatis强大的一点,对于select,其实我们配置的都是把每一条结果集转成的对象类型。
2,SELECT * FROM user:即查询语句。因为不需要参数了,所以也没有定义paramterType。
查询测试:
@Test
public void testList(){
SqlSession session = sqlSessionFactory.openSession();
try{
List<User> userList = session.selectList("cn.itcast.mybatis.domain.UserMapper.list", 1);
for(User u : userList){
System.out.println(u);
}
}catch(Exception e){
e.printStackTrace();
}finally{
session.close();
}
}
1, 使用的是session.selectList,代表这次调用的select语句需要把结果集包装成一个对象列表。
DEBUG - ooo Using Connection [com.mysql.jdbc.JDBC4Connection@16ac534]
DEBUG - ==> Preparing: select * from user
DEBUG - ==> Parameters:
User [id=1, name=柳传志, hireDate=Tue Dec 31 00:00:00 CST 2013]
User [id=2, name=杨元庆, hireDate=Wed Jan 01 00:00:00 CST 2014]
得到正确的结果。
l) 【删除】操作
在UserMapper.xml中添加:
<delete id="delete" parameterType="long">
delete from user
where id=#{pid}
</delete>
使用delete标识这是一个删除语句。参数parameterType=long表明删除数据的参数。同select一样,如果传入的参数就是一个id,那么在sql语句中使用#{id}来引用这个参数。
测试用例:
@Test
public void testDelete(){
SqlSession session = sqlSessionFactory.openSession();
try{
session.delete("cn.itcast.mybatis.domain.UserMapper.delete", 2);
session.commit();
}catch(Exception e){
e.printStackTrace();
session.rollback();
}finally{
session.close();
}
}
使用session.delete方法指定调用的删除SQL和传入参数。运行结果:
DEBUG - ooo Using Connection [com.mysql.jdbc.JDBC4Connection@1a431fd]
DEBUG - ==> Preparing: delete from user where id=?
DEBUG - ==> Parameters: 1(Integer)
测试事务是否起作用,在delete后,commit前,加下面语句
System.out.println(1/0)
使其出错,
运行结果,报错,
DEBUG - ooo Using Connection [com.mysql.jdbc.JDBC4Connection@1be02b6]
DEBUG - ==> Preparing: delete from user where id=?
DEBUG - ==> Parameters: 2(Integer)
java.lang.ArithmeticException: / by zero
说明事务起作用。
到这里,mybatis的CRUD基本演示完毕。从这些方法里面大概能够感受到mybatis的一些基本的使用方式。
3. 小结
两个核心配置文件
1)sqlMapConfig.xml
文件名可以随意命名
配置3个内容,数据库链接信息,映射文件,基础配置
2)映射文件
文件名可以随意命名,习惯使用 PO对象名+Mapper.xml
命名空间<mapper namespace="cn.itcast.mybatis.mapper.UserMapper">
命名空间可以没有,<mapper namespace="">
<select 底层不关心标签的命名,随意。但是底层对映射文件是有校验。
MyBatis理论简单,说白了拼接sql语句。
4. 动态SQL语句
动态查询,动态修改
a) <if>
b) <where> 自动去除第一个条件的 and 或者 or
<!-- 带条件的动态SQL -->
<select id="find" parameterType="cn.itcast.ssm.domain.User" resultMap="userRM">
select * from user
<where>
<if test="userName!=null">
and user_name like #{userName}
</if>
<if test="age!=null">
and age>#{age}
</if>
</where>
</select>
c) <set> 自动去除SQL语句中最后一个的逗号
<update id="update" parameterType="cn.itcast.ssm.domain.User">
update user
<set>
<if test="userName!=null">
user_name=#{userName},
</if>
<if test="age!=null">
age=#{age},
</if>
</set>
where id=#{id}
</update>
d) <foreach> 批量,拼接,例如:构造in子查询串的
foreach的参数:
l foreach元素的属性主要有 item,index,collection,open,separator,close。
l collection 传入参数,数组用array;集合用list/map
l item表示集合中每一个元素进行迭代时的别名.
l index指定一个名字,用于表示在迭代过程中,每次迭代到的位置。可以用于特殊指定,例如第二行做特殊处理;间隔变色
l open表示该语句以什么开始,separator表示在每次进行迭代之间以什么符号作为分隔 符.
l close表示以什么结束.
5. 动态SQL复杂参数传递
a) 方式一,传递数组array
映射文件UserMapper.xml
<delete id="deleteBatch" parameterType="Integer">
delete from user
where id in
<foreach collection="array" open="(" close=")" item="id" separator=",">
#{id}
</foreach>
</delete>
Dao文件
public interface UserDao {
public void delete(Integer[] ids);
}
调用Controller
public void delete(Integer[] ids) {
userDao.delete(ids);
}
b) 方式二,传递list
映射文件UserMapper.xml
<delete id="deleteBatch" parameterType="Integer">
delete from user
where id in
<foreach collection="list" open="(" close=")" item="id" separator=",">
#{id}
</foreach>
</delete>
Dao文件
public interface UserDao {
public void delete(List<Integer> ids);
}
调用Controller
@RequestMapping("/deletebatch")
public String deleteBatch(Integer[] id){
List<Integer> paraList = new ArrayList<Integer>();
for(int i=0;i<id.length;i++){
paraList.add(id[i]);
}
userService.delete(paraList);
return "redirect:/user/list";
}
c) 方式三,传递map
映射文件UserMapper.xml
//参数类型可以是map也可以是hashmap
<delete id="changeState" parameterType="map">
update contract_c
set STATE=#{state}
where CONTRACT_ID in
<foreach collection="ids" open="(" close=")" item="id" separator=",">
#{id}
</foreach>
</delete>
Dao文件
public void changeState(Map map) {
this.getSqlSession().update(this.getNs() + "changeState", map);
}
调用Controller
@RequestMapping("/submit.action")
public String submit(String[] id){
Map map = new HashMap();
map.put("state", 1);
map.put("ids", id);
localService.changeState(map);
return "redirect:/cargo/contract/list.action";
}
6. 优化
前面讲了CRUD的全部测试,下面是对CRUD示例的优化:
a) 列字段
可以在配置文件中调用的地方都进行include引用
<sql id="cols">
id,name,hireDate
</sql>
<select id="list" resultType="user">
select <include refid="cols"/> from user
</select>
b) 别名
typeAliases:在示例中的UserMapper.xml文件中可以看到,凡是使用到User类型的时候,都需要写User的类的全限定名。在sqlMapConfig.xml中,可以使用typeAliases元素来简化这个操作:
在sqlMapConfig.xml中添加:
<typeAliases>
<typeAlias type="cn.itcast.mybatis.domain.User" alias="user"/>
</typeAliases>
UserMapper.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="cn.itcast.mybatis.domain.UserMapper">
<insert id="save" keyProperty="id" useGeneratedKeys="true" parameterType="user">
insert into user (name,hireDate) values(#{name},#{hireDate})
</insert>
<update id="update" parameterType="user">
update user set name=#{name}, hireDate=#{hireDate}
where id=#{id}
</update>
<select id="get" parameterType="long" resultType="user">
select * from user where id=#{pid}
</select>
<select id="list" resultType="user">
select * from user
</select>
<delete id="delete" parameterType="long">
delete from user
where id=#{pid}
</delete>
</mapper>
c) db.properties
我们说过数据库的连接信息一般要放到一个额外的.properties文件中,mybatis允许我们这样做。
首先,修改sqlMapConfig.xml文件:
<dataSource type="POOLED">
<property name="driver" value="${driverClass}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
联想spring的dataSource配置方式,不难理解。
占位符的数据来源配置有三种方式:
第一种使用方式:在SqlSessionFactoryBuilder的build方法中,还提供了额外传入Properties对象的方法:
public SqlSessionFactory build(InputStream inputStream, Properties properties)
这个方法后面的Properties对象就可以做为sqlMapConfig.xml中的参数来源。所以,我们可以这样来使用:
Properties p=new Properties();
p.load(Resources.getResourceAsStream("db.properties"));
并在classpath下定义一个db.properties文件:
driverClass=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/mybatisdb?characterEncoding=utf-8
username=root
password=root
第二种使用方式:在mybatis-config.xml中有properties这样一个标签,那么我们可以在mybatis-config.xml中定义:
<properties>
<property name="driverClass" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql:///mybatisdb?characterEncoding=utf-8"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</properties>
第三种方式:在mybatis-config.xml中的properties元素中,引入外部的properties文件:
<properties resource="db.properties" />
并在classpath中添加db.properties文件即可。
第三种方式和第二种方式可以混用,即:
db.properties文件中:
driverClass=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/mybatisdb?characterEncoding=utf-8
sqlMapConfig.xml文件中:
<properties resource="db.properties">
<property name="username" value="root"/>
<property name="password" value="root"/>
</properties>
三者的优先级为:
代码传入的Properties > resource加载的Properties > properties元素中定义的property。
d) MyBatisUtil工具类的抽象
在mybatis中,SqlSessionFactory和SqlSession的作用和地位极其类似hibernate中的SessionFactory和Session,包括其线程安全性。
即SqlSessionFactory是线程安全的,在整个应用中对应一个数据源只需要创建一个。
SqlSession是线程不安全的,生命周期最好是method或者线程。
所以,我们就能抽象一个MyBatisUtil来提供SqlSession的创建:
package cn.itcast.mybatis.utils;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
public class MyBatisUtil {
private static SqlSessionFactory factory;
static{
try{
factory = new SqlSessionFactoryBuilder()
.build(Resources.getResourceAsStream("sqlMapConfig.xml"));
}catch(Exception e){
e.printStackTrace();
}
}
public static SqlSession openSession(){
return factory.openSession();
}
}
修改调用方法:
@Test
public void testList(){
SqlSession session = MyBatisUtil.openSession();
try{
List<User> userList = session.selectList("cn.itcast.mybatis.domain.UserMapper.list", 1);
for(User u : userList){
System.out.println(u);
}
}catch(Exception e){
e.printStackTrace();
}finally{
session.close();
}
}
e) ***resultType和resultMap
假设现在User对象不变,但是对应USER表中的name列的名称DBA重新设计成了username,那现在再来运行一遍测试:
可以看到这次的name属性就为null。这时候就体现出mybatis在直接使用resultType的局限性,要求属性名称必须和列的名称一致(大小写可以不一致)。在这种情况下,就必须要手动来完成数据表列和对象的映射关系了。首先来修改get方法:
<select id="get" parameterType="int" resultMap="usermapping">
SELECT * FROM user WHERE id = #{id}
</select>
在这里,可以看到,去掉了resultType,因为resultType只能完成默认的对象类型的转换。在这里修改成了resultMap,其实这里叫做resultMapping对于学习了hibernate的童鞋来说更好理解。在这里我把结果集定义到了一个名字叫做usermapping的映射上。
下面定义usermapping:
<resultMap type="User" id="usermapping">
<id property="id" column="id"/>
<result property="name" column="username"/>
<result property="hireDate" column="hiredate"/>
</resultMap>
resultMap定义了一个ORM的具体映射方式。
1,type:代表O,即最终返回的对象类型
2,id:为该映射设置一个名称,这个名称就是在get或list中使用的resultMap对应的id
3,id/result:对应这属性的映射,可以参考hibernate的property。id和result的区别在于,id一般用于映射主键,可以提高速度,result一般对于普通的属性。
设置完成后,就可以将对象正常get了。
在mybatis中,其实如果仅仅只是使用了resultType,相当于mybatis自动的帮我们建立了一个resultMap,只是这个resultMap直接完成了属性和列相同名称的映射而已。所以,mybatis真正完成映射的地方就在resultMap。
之前咱们说过,如果说hibernate是面向对象为主,关系为辅,那么在mybatis中则是着重考虑的是关系模型,换句话说,如果对象模型设计的不好,就会很容易的感觉到实现的难度。
首先来看看最简单的单向many2one:
建立对象:
public class Customer {
private Long id;
private String name;
}
public class Orders {
private Long id;
private String sn;
private Double price;
private Customer customer;
}
很简单,Orders和Customer是一个单向的many2one的关系。和hibernate一样,我们先来配置简单的customer:
创建CustomerMapper.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="cn.itcast.mybatis.domain.CustomerMapper">
<insert id="save" keyProperty="id" parameterType="customer" useGeneratedKeys="true">
insert into customer (name) values(#{name})
</insert>
<select id="get" resultType="customer" parameterType="int">
SELECT * FROM customer WHERE id = #{id}
</select>
</mapper>
在sqlMapConfig.xml中加入
<typeAlias type="cn.itcast.mybatis.domain.Customer" alias="customer"/>
只是一个很简单的单对象映射操作。
在hibernate的many2one中我们知道,在many方的外键关联到one方的主键。在many方保存的时候,应该在对应外键保存关联one方的id。所以,many方的保存insert应该是这样:
OrdersMapper.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="cn.itcast.mybatis.domain.OrdersMapper">
<insert id="save" keyProperty="id" parameterType="orders" useGeneratedKeys="true">
INSERT INTO orders(sn,price,customer_id) values (#{sn},#{price},#{customer.id})
</insert>
</mapper>
在这里看到,我们在对应外键(customer_id)的地方,使用#{customer.id}来代表orders的customer属性的id属性,mybatis会像EL那样自动帮我们得到值。所以,在这里一定要注意保存对象的顺序,必须是先保存one再保存many方。
在hibernate中,如果保存顺序有误,hibernate会在提交事务的时候同步脏数据,帮我们补一条update语句来保证对象的关系正确,但是在mybaits中,所有的sql都由我们来完成,所以没人会帮我们来完成update。所以,对应的保存测试应该是:
@Test
public void testSave(){
SqlSession session = MyBatisUtil.openSession();
try{
Customer c = new Customer();
c.setName("itcast");
Orders o = new Orders();
o.setPrice(800d);
o.setSn("001");
o.setCustomer(c);
session.insert("cn.itcast.mybatis.domain.CustomerMapper.save", c);
session.insert("cn.itcast.mybatis.domain.OrdersMapper.save", o);
session.commit();
}catch(Exception e){
e.printStackTrace();
session.rollback();
}finally{
session.close();
}
}
下面就是many方的获取了。如果只是简单的把orders配置成一个单对象,即使用resultType来映射:
<select id="get" parameterType="long" resultType="Orders">
SELECT * FROM orders WHERE id = #{id}
</select>
如果是这样,在测试的时候:
@Test
public void testGet(){
SqlSession session = MyBatisUtil.openSession();
try{
Orders o = session.selectOne("cn.itcast.mybatis.domain.OrdersMapper.get", 1);
System.out.println(o);
Customer c=o.getCustomer();
System.out.println(c);
}catch(Exception e){
e.printStackTrace();
}finally{
session.close();
}
}
会看到,orders对应的customer是null,只能说明mybatis不能帮我们自动完成相关的关系映射。
DEBUG - ooo Using Connection [com.mysql.jdbc.JDBC4Connection@1442cd0]
DEBUG - ==> Preparing: SELECT * FROM orders WHERE id = ?
DEBUG - ==> Parameters: 1(Integer)
cn.itcast.mybatis.domain.Orders@121f043
null
下面就来看下在mybatis中的关系映射:
第一种方式:使用额外的sql:
<?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.domain.OrdersMapper">
<resultMap type="orders" id="ordersmap">
<id property="id" column="id"/>
<result property="sn" column="sn"/>
<result property="price" column="price"/>
<association property="customer" column="customer_id" javaType="customer"
select="cn.itcast.mybatis.CustomerMapper.get"/>
</resultMap>
<insert id="save" keyProperty="id" parameterType="orders" useGeneratedKeys="true">
INSERT INTO orders(sn,price,customer_id) values (#{sn},#{price},#{customer.id})
</insert>
<select id="get" parameterType="long" resultMap="ordersmap">
SELECT * FROM orders WHERE id = #{id}
</select>
</mapper>
注意<select id="get" parameterType="long" resultMap="ordersmap"> 原来的resultType要修改为resultMap
可能第一次看这个配置会觉得很奇怪,其实很好理解:
1,association:在mybatis中不像hibernate那样,把关系分的很细致,比如one-to-one,many-to-one,而只是把关系分成了两种:第一种是对单对象的关系,就用association,一种是集合的关系,这个待会再看。因为这里orders只是把customer对象作为自己的一个属性,所以使用association来表示这个关系;
2,property:和hibernate一样,定义这个关系在orders对象上面的对应属性。在完成关联对象的映射后,会使用这个属性把对象设置到orders对象上;
3,column:代表从查询结果集(select * from orders where id = ?)中得到对关系维护的列,这里就指明的是外键customer_id;
4,javaType:代表完成映射后应该返回的对象类型,这里肯定就是Orders
5,select:是这种映射方式的最关键的地方,这个select指代着cd.itcast.mybatis.customer.get这条SQL,这条sql即是得到customer的sql,而这条sql需要传入一个customer的id,我们前面已经通过column="customer_id"指定了应该传给cd.itcast.mybatis.customer.get的值就是结果集中的customer_id。
所以,可以看出,mybatis在这种映射的方式为:首先执行SELECT * FROM orders WHERE o.id = #{id},然后对id,sn,price属性直接从结果集中得到;接着,从结果集中得到customer_id列对应的值,这个值即是orders对应customer对象的id,然后把这个值作为id交给cd.itcast.mybatis.customer.get去执行,而这个执行的结果会被拼装为一个Customer对象,接着这个对象再会被设置到orders的customer属性中。完成映射。
再次执行get测试:
7. Mybatis generator 自动生成sql脚本MyEclipse插件
1)安装插件
将给定的plugins和feaures目录直接拷贝到MyEclipse Professional\dropins
重启myeclipse
2)配置文件
指定mysql的驱动包的路径 千万别放中文路径下
配置数据源和生成的代码所存放的位置
创建一个配置文件sqlMapGeneractor.xml
这个文件名称随便起,它会自动识别你文件的头信息<!DOCTYPE generatorConfiguration。
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE generatorConfiguration PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN" "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd" >
<generatorConfiguration>
<!-- 配置属性文件,这样有变更只需改配置文件 -->
<properties resource="generatorConfig.properties"/>
<!-- 制定mysql的驱动包的路径 千万别放中文路径下 -->
<classPathEntry location="D:\javaenv\all_jar\database\mysql-connector-java-5.1.26.jar" />
<!-- 配置数据源和生成的代码所存放的位置 -->
<context id="itcast">
<!-- 是否生成注释 true不生成 false生成 -->
<commentGenerator>
<property name="suppressAllComments" value="${suppressAllComments}"/>
</commentGenerator>
<jdbcConnection driverClass="${driverClass}" connectionURL="${url}" userId="${username}" password="${password}" />
<!-- 所生成的实体类的位置默认资源包src -->
<javaModelGenerator targetPackage="${modeltargetPackage}" targetProject="${targetProject}" />
<!-- 所生成的sqlMap的影射文件的位置,默认资源包src -->
<sqlMapGenerator targetPackage="${sqltargetPackage}" targetProject="${targetProject}" />
<javaClientGenerator targetPackage="${clienttargetPackage}" targetProject="${targetProject}" type="XMLMAPPER" />
<!-- 为哪些表生成代码 tableName:表名 schema:不用填写 -->
<table schema="" tableName="orders" />
<table schema="" tableName="department" />
<table schema="" tableName="employee" />
</context>
</generatorConfiguration>
配置属性文件,控制生成的代码,方便更改配置generatorConfig.properties
driverClass=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/mybatisdb?characterEncoding=utf-8
username=root
password=root
suppressAllComments=true
targetProject=cd
modeltargetPackage=cn.itcast.model
sqltargetPackage=cn.itcast.sqlMap
clienttargetPackage=cn.itcast.client
点击菜单运行即可。
我们可能会担心一旦重新执行generate的时候,我们自己编写的代码会不会丢失,不会的,插件不会修改或丢弃我们自己编写的代码。
一旦掌握了插件如何使用,重要的工作就是如何使用XXXExample类了。这种方式,完全不用编写繁琐的mapper xml文件。
8. 知识回顾
Mybatis(google),iBatis(apache) SSI。这个框架比较成熟。Hibernate全的ORM,MyBatis半自动的ORM。两个框架都是基于面对对象的关系映射。都是将数据库数据封装PO对象。不写SQL语句。Hibernate框架实现的核心,基于完全面向对象编程。设计时面向对象的思考,开发时面向对象编程。Mybatis,前端,action,jsp操作都对象,PO对象。后台代码,主要映射文件。面向SQL语句。MyBatis结构非常灵活。大数据量,大量统计时,业务系统大多采用Mybatis。企业有很多老程序员,SQL有一定基础。
体验MyBatis。
核心配置文件
1) sqlMapConfig.xml 数据库链接,加载映射文件,基础配置
2) UserMapper.xml 整体结构简单,繁杂。
标签,id(访问唯一标识) + parameter(传递前台参数) +result(返回结果)
拼接成SQL语句
3) 怎么调用?SqlSessionFactory,SqlSession
4) 工具,自动生成sql映射文件。生成PO对象,简单的SQL语句。
9. 关联关系
表的关系,无关系(单表),一对多(主从),多对多(中间表-两个表的主键)(典型例子:权限,用户和角色),多对一,一对一
Mybatis表现关联关系比hibernate简单,没有分那么细致one-to-many、many-to-one、one-to-one。而是只有两种association(一)、collection(多),表现很简洁
a) 一对多
--查询某个人的订单。例如:查询tony的订单
SELECT u.*,o.* FROM USER u, orders o
WHERE u.id=o.user_id AND u.user_name='tony'
关联关系一种书写方式
1)方式1
<!-- 一对多关系 -->
<resultMap type="cn.itcast.mybatis.domain.User" id="UserOrderRM">
<id column="id" property="id" jdbcType="INTEGER" />
<result column="user_name" property="userName" jdbcType="VARCHAR" />
<result column="age" property="age" jdbcType="INTEGER" />
<collection property="ordersList" ofType="cn.itcast.mybatis.domain.Orders">
<id property="orderId" column="order_id"/>
<result property="orderNo" column="order_no"/>
<result property="orderDesc" column="order_desc"/>
<result property="amount" column="amount"/>
</collection>
</resultMap>
2)方式2
习惯继承,推荐
<resultMap id="BaseResultMap" type="cn.itcast.mybatis.domain.User" >
<id column="id" property="id" jdbcType="INTEGER" />
<result column="user_name" property="userName" jdbcType="VARCHAR" />
<result column="age" property="age" jdbcType="INTEGER" />
</resultMap>
<!-- 一对多关系 -->
<resultMap type="cn.itcast.mybatis.domain.User" id="UserOrderRM" extends="BaseResultMap">
<collection property="ordersList" ofType="cn.itcast.mybatis.domain.Orders">
<id property="orderId" column="order_id"/>
<result property="orderNo" column="order_no"/>
<result property="orderDesc" column="order_desc"/>
<result property="amount" column="amount"/>
</collection>
</resultMap>
建立一对多操作:
1) 建立单表的Mapper,也要建立resultMap
2) 再建立一个resultMap,这个要继承单表的extends="BaseResultMap"
3) 在其中添加关联关系,<collection>。
property="ordersList" 对应PO对象中的属性
private List<Orders> ordersList; //关联的订单集合
ofType="cn.itcast.mybatis.domain.Orders" 对应PO对象
需求:
--某个订单是哪个用户的
SELECT u.*,o.* FROM USER u, orders o
WHERE u.id=o.user_id AND o.order_no=100
b) 多对一
<resultMap id="BaseResultMap" type="cn.itcast.mybatis.domain.Orders" >
<id column="order_id" property="orderId" jdbcType="INTEGER" />
<result column="order_no" property="orderNo" jdbcType="VARCHAR" />
<result column="user_id" property="userId" jdbcType="INTEGER" />
<result column="order_desc" property="orderDesc" jdbcType="VARCHAR" />
<result column="amount" property="amount" jdbcType="DECIMAL" />
</resultMap>
<!-- 多对一 -->
<resultMap type="cn.itcast.mybatis.domain.Orders" id="OrdersUserRM" extends="BaseResultMap">
<association property="user" javaType="User">
<id column="id" property="id" jdbcType="INTEGER" />
<result column="user_name" property="userName" jdbcType="VARCHAR" />
<result column="age" property="age" jdbcType="INTEGER" />
</association>
</resultMap>
public class Orders {
private User user; //关联关系
//测试多对一关联
@Test
public void testSelectUserByOrders(){
SqlSession session = sqlSessionFactory.openSession(); //从工厂中获取sqlSession
Orders o = session.selectOne("client.OrdersMapper.selectUserByOrders", "100");
System.out.println(o);
}
c) 主从从,两层一对多
一个用户下有多个订单,一个订单有多个明细
<!-- 主从从 两层一对多关系 -->
<resultMap type="cn.itcast.mybatis.domain.User" id="UserOrdersOrderDetailRM">
<id column="id" property="id" jdbcType="INTEGER" />
<result column="user_name" property="userName" jdbcType="VARCHAR" />
<result column="age" property="age" jdbcType="INTEGER" />
<collection property="ordersList" ofType="cn.itcast.mybatis.domain.Orders">
<id property="orderId" column="order_id"/>
<result property="orderNo" column="order_no"/>
<result property="orderDesc" column="order_desc"/>
<result property="amount" column="amount"/>
<collection property="orderDetailList" ofType="cn.itcast.mybatis.domain.OrderDetail" >
<id property="orderDetailId" column="order_detail_id"/>
<result property="productName" column="product_name"/>
<result property="price" column="price"/>
<result property="cnumber" column="cnumber"/>
</collection>
</collection>
</resultMap>
注意,关系的嵌套,在<collection>中在写一个<collection>
//测试主从从
@Test
public void testSelectOrderDetailByUser(){
SqlSession session = sqlSessionFactory.openSession(); //从工厂中获取sqlSession
User u = session.selectOne("client.UserMapper.selectOrderDetailByUser", "tony");
System.out.println(u);
}
10. =====================延迟加载
在MyBatis核心配置文件中配置:
<settings>
<setting name=”lazyLoadingEnabled” value=”true”/>
<setting name=”aggressiveLazyLoading” value=”false”/>
</settings>
11. 一级缓存
Hibernate 一级缓存,session
Mybatis SqlSession
User user = session.selectOne("cn.itcast.mybatis.mapper.UserMapper.get", "1");
System.out.println(user);
User u = session.selectOne("cn.itcast.mybatis.mapper.UserMapper.get", "1");
System.out.println(u);
只有第一句才进行数据库查询,打印SQL到控制台。
第二句直接从内存中获取对象内容。
12. 二级缓存
1)默认开启二级缓存
sqlMapConfig.xml中配置settings <setting name="cacheEnabled" value="true"/>
用不用不一定,靠下面指定,才生效
2)要真实缓存时,必须在映射文件中配置 UserMapper.xml
<mapper namespace="cn.itcast.mybatis.mapper.UserMapper">
<cache/><!-- 使用二级缓存 -->
<cache
eviction="FIFO" //逐出策略
flushInterval="60000" //多少时间刷新,单位:毫秒
size="512" //缓存大小,恰当的值才能做好发挥它的效能
readOnly="true"/> //只读
eviction逐出策略:
LRU-最少使用的被逐出
FIFO-先进先出
SOFT、WEAK – 类似java GC垃圾回收机制
在映射文件中不加<cache/>即使settings设置缓存开启,也不会应用。设置了<cache/>二级缓存才真正应用。
<cache
eviction="LRU"
flushInterval="60000"
size="512"
readOnly="true"/>
13. EHcathe第三方缓存插件
1) 拷贝jar,整合mybatis-ehcache-1.0.2.jar,核心包ehcache-core-2.6.5.jar,依赖包slf4j-api-1.6.1.jar
2) 首先要确定settings二级缓存开启
3) 拷贝资料\5-EHcache\mybatis-ehcache-1.0.2\lib\ehcache-core-2.6.5下的ehcache-failsafe.xml,到src下,然后改名为ehcache.xml
defaultCache配置说明:
l maxElementsInMemory 内存中最大缓存对象数.当超过最大对象数的时候,ehcache会按指定的策略去清理内存
l eternal 缓存对象是否永久有效,一但设置了,timeout将不起作用.
l timeToIdleSeconds 设置Element在失效前的允许闲置时间.仅当element不是永久有效时使用,可选属性,默认值是0,也就是可闲置时间无穷大.
l timeToLiveSeconds:设置Element在失效前允许存活时间.最大时间介于创建时间和失效时间之间.仅当element是永久有效时使用,默认是0.,也就是element存活时间无穷大.
l overflowToDisk 配置此属性,当内存中Element数量达到maxElementsInMemory时,Ehcache将会Element写到磁盘中.
l diskSpoolBufferSizeMB 这个参数设置DiskStore(磁盘缓存)的缓存区大小.默认是30MB.每个Cache都应该有自己的一个缓冲区.
l maxElementsOnDisk 磁盘中最大缓存对象数,若是0表示无穷大.
l diskPersistent 是否在重启服务的时候清楚磁盘上的缓存数据.true不清除.
l diskExpiryThreadIntervalSeconds 磁盘失效线程运行时间间隔.
l memoryStoreEvictionPolicy:当达到maxElementsInMemory限制时,Ehcache将会根据指定的策略去清理内存.默认策略是LRU(最近最少使用).你可以设置为FIFO(先进先出)或是LFU(较少使用).
14. ===========拦截器
15. SSM整合 springmvc+spring+mybatis
1) jar包,SSM的jar,数据库,JUnit
2) 编写配置文件web.xml、springmvc-servlet.xml、beans.xml、sqlMapConfig.xml
3) 创建数据库ssmdb utf-8,创建表
4) 创建po对象 cn.itcast.ssm.domain.User
5) 创建映射文件 cn.itcast.ssm.mapper UserMapper.xml
6) 注解方式 DAO
7) Service
8) Controller
9) Jsp
10) 发布、测试