Mybatis
Mybatis(ORM)
- mybatis简介
MyBatis是支持普通SQL查询,存储过程和高级映射的优秀持久层框架。MyBatis消除了几乎所有的JDBC代码和参数的手工设置以及结果集的检索。MyBatis使用简单的XML配置将普通的Java对象映射成数据库中的记录
1.1.对象关系映射
1.2持久层
1.3.java调用mybatis执行流程
1. 加载配置文件
2. 从配置文件得到 sessionfactory.
3. 由sessionfactory产生session(代表应用和数据库的一次会话/一次连接)
4. 在session 中完成对数据的增删改查和事务提交等.
5. 在用完之后关闭session 。
2.开发环境搭建
1. 建立web工程。
2. 将 mybatis-3.0.4.jar,ojdbc14.jar ,log4j-1.2.16.jar,commons-logging-1.1.jar拷贝到 web工程的lib目录.
3. 配置log4j.properties
4. 创建ORACLE 测试数据库和用户表
create TABLE user_m ( id int NOT NULL , userName varchar(50) DEFAULT NULL, userAge int DEFAULT NULL, userAddress varchar(200) DEFAULT NULL, PRIMARY KEY ( id ) ) insert INTO user_m VALUES (1, 'summer', 100, 'shanghai,pudong'); |
5. 创建和user_m表对应的java类
public class User { |
6. 创建映射文件user.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="UserMapper"> <!-- 通过用户ID查询用户 --> <select id="selectUserByID" parameterType="int" resultType="User"> <![CDATA[ select * from user_m where id=#{id} ]]> </select> </mapper>
|
输入属性参数 :
parameterType 将会传入到这条语句的参数的类型(完全限定名或别名)。
输出属性参数:
resultType 这条语句的执行结果期望被组装成的对象(类的完全限定名或别名)。
7. src目录下建立核心配置文件:SqlMapConfig.xml
<?xml version="1.0" encoding="UTF-8" ?> <!-- 类别名定义 --> <typeAliases> <!-- 配置Mybatis的事务及数据源等等 default="development" 开发状态会显示更多的日志信息--> <environment id="development"> <transactionManager type="JDBC" /> <dataSource type="POOLED"> <property name="driver" value="oracle.jdbc.OracleDriver" /> <property name="url" value="jdbc:oracle:thin:@localhost:1521:test" /> <property name="username" value="fsup" /> <property name="password" value="fsup" /> </dataSource> </environment> </environments> |
transactionManager
在 MyBatis 中有两种事务管理器类型(也就是 type=”[JDBC|MANAGED]”):
JDBC – 这个配置直接简单使用了 JDBC 的提交和回滚设置。
MANAGED –让容器来管理事务的整个生命周期(比如 Spring)。
dataSource
有三种内建的数据源类型(也就是 type= ""):
UNPOOLED – 这个数据源的实现是每次被请求时简单打开和关闭连接.它有一点慢
POOLED – 这是 JDBC 数据源连接池的实现,这是一种当前 Web 应用程序用来快速响应请求很流行的方法。
JNDI – 这个数据源的实现是为了使用如 Spring 或应用服务器这类的容器, 容器可以集中或在外部配置数据源,然后放置一个 JNDI 上下文的引用
配置文件说明:
1.SqlMapConfig.xml 是 mybatis 用来建立 sessionFactory 用的,里面主要包含了数据库连接相关信息、java类所对应的别名、mapper映射文件的路径
2.User.xml是定义各种SQL 语句,以及这些语句的参数,以及要返回的类型等.
8.测试类
import java.io.IOException; import java.io.Reader; 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 Test { public static void main(String[] args) throws IOException { //加载mybatis配置文件 Reader reader = Resources.getResourceAsReader("SqlMapConfig.xml"); //通过reader获取SqlSessionFactory SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader); //打开session SqlSession session = sqlSessionFactory.openSession(); try { //查询 User user = (User) session.selectOne( "UserMapper.selectUserByID", 1); System.out.println(user.getUserAddress()); System.out.println(user.getUserName()); } finally { session.close(); } } }
|
项目结构如下:
注意:项目的编码格式要和配置文件的编码格式保持一致 UTF-8
3. 以接口的方式编程
1.新建项目
2.新建com.lq.mybatis.inter包,并建立接口类 IuserOperation
package com.lq.mybatis.inter; |
注意:接口的参数类型和sql的参数类型保持一致
3.新建mapper映射文件,指定namespace的值为接口的全路径:
<mapper namespace="com.lq.mybatis.inter.IUserOperation"> |
4.其他操作同上
5.测试类
public class Test { public static void main(String[] args) throws IOException { Reader reader = Resources.getResourceAsReader("SqlMapConfig.xml"); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader); SqlSession session = sqlSessionFactory.openSession(); try { IUserOperation userOperation=session.getMapper(IUserOperation.class); User user = userOperation.selectUserByID(1); System.out.println(user.getUserAddress()); System.out.println(user.getUserName()); } finally { session.close(); } } } |
项目结构图如下:
4.增删改查的实现
4.1、mybatis增加数据
<insert id="addUser" parameterType="User"> <![CDATA[ insert into user_m(id,userName,userAge,userAddress) values(#{id},#{userName},#{userAge},#{userAddress}) ]]> </insert> |
//测试增加,增加后,必须提交事务,否则不会写入到数据库. Reader reader = Resources.getResourceAsReader("SqlMapConfig.xml"); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader); SqlSession session = sqlSessionFactory.openSession(); try { User user=new User(); user.setId(3); user.setUserAddress("人民广场"); user.setUserName("飞鸟"); user.setUserAge("80"); session.insert("com.lq.mybatis.inter.IUserOperation.addUser",user); session.commit(); } finally { session.close(); } } |
4.2、mybatis删除数据
<delete id="deleteUser" parameterType="int"> |
public static void main(String[] args) throws IOException { Reader reader = Resources.getResourceAsReader("SqlMapConfig.xml"); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader); SqlSession session = sqlSessionFactory.openSession(); try { session.delete("com.lq.mybatis.inter.IUserOperation.deleteUser", 4); session.commit(); } finally { session.close(); } } |
4.2、mybatis修改数据
<update id="updateUser" parameterType="User" > |
public static void main(String[] args) throws IOException { Reader reader = Resources.getResourceAsReader("SqlMapConfig.xml"); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader); SqlSession session = sqlSessionFactory.openSession(); try { User user=new User(); user.setId(3); user.setUserAddress("人民广场修改"); user.setUserName("飞鸟"); user.setUserAge("80"); session.update("com.lq.mybatis.inter.IUserOperation.updateUser",user); session.commit(); } finally { session.close(); } } |
4.3、mybatis查询
<?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="com.lq.mybatis.inter.IUserOperation"> <select id="selectUsers" parameterType="java.lang.String" resultType="User"> <![CDATA[ select * from user_m where userName like #{userName} ]]> </select> </mapper> |
public class Test { public static void main(String[] args) throws IOException { Reader reader = Resources.getResourceAsReader("SqlMapConfig.xml"); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader); SqlSession session = sqlSessionFactory.openSession(); try { List<User> users = session.selectList("com.lq.mybatis.inter.IUserOperation.selectUsers", "summer"); for(User user:users){ System.out.println(user.getId()+":"+user.getUserName()+":"+user.getUserAddress()); } } finally { session.close(); } } } |
5.resultMap
当数据库字段与实体类字段不一致时可以定义一个resultMap ,其中配置实体类属性和查询结果字段的一一映射,和 sql语句标签
中的resultMap属性配对使用
<resultMap type="User" id="resultListUser"> <id column="id" property="id" /> <result column="userName" property="userName" /> <result column="userAge" property="userAge" /> <result column="userAddress" property="userAddress" /> </resultMap>
<select id="selectUsers" parameterType="java.lang.String" resultMap="resultListUser"> <![CDATA[ select * from user_m where userName like #{userName} ]]> </select> |
id、result语句属性配置细节:
属性 |
描述 |
property |
需要映射到JavaBean的属性名称。 |
column |
数据表的列名或者列的别名。 |
javaType |
属性的类型一个完整的类名,或者是一个类型别名。java.lang.Integer |
jdbcType |
数据表的列数据类型。这个属性只在insert,update或delete的时候针对允许空的列有用。 |
常用JDBC类型
CHAR,VARCHAR,DOUBLE,INTEGER,DATE,BOOLEAN
注意:resultType或resultMap,但不能同时使用
6.关联查询
在实际项目中,经常是关联表的查询,比如最常见到的多对一,一对多。
新建Article表,并初始化数据
create TABLE article ( id number(11) NOT NULL , userid number(11) NOT NULL, title varchar(100) NOT NULL, content varchar(1000) NOT NULL, PRIMARY KEY (id) ) Insert INTO article VALUES (2, 1, 'test_title_2', 'test_content_2'); Insert INTO article VALUES (3, 1, 'test_title_3', 'test_content_3'); Insert INTO article VALUES (4, 1, 'test_title_4', 'test_content_4'); |
几个文章对应的userid都是1
6.1.多对一
场景:获取某个用户发表的所有文章
新建Article类,在多的一方配置一的一方的对象
public class Article { |
方法一:复用前面已经定义好的 resultMap
<resultMap id="resultListUser" type="User"> |
方法二:
where user_m.id=article.userid and user_m.id=#{id} </select> |
注意:查询的字段不要有重复的名称,如果有重复名称设置别名
6.2.一对多
一个用户可以发表多篇文章,站在用户的角度是一对多
需要在用户类中添加文章字段集合类型 private List article;
用collection来得到关联的文章
User.xml的配置
<resultMap id="resultUserArticleList" type="User"> <id property="id" column="id" /> <result property="userName" column="userName" /> <result property="userAge" column="userAge" /> <result property="userAddress" column="userAddress" /> <collection property="article" ofType="com.lq.mybatis.model.Article"> <id property="id" column="aid" /> <result property="title" column="title" /> <result property="content" column="content" /> </collection > </resultMap> <select id="getUserArticles" parameterType="int" resultMap="resultUserArticleList"> select user_m.id,user_m.userName,user_m.userAddress,article.id aid,article.title,article.content from user_m,article where user_m.id=article.userid and user_m.id=#{id} </select> |
7.动态sql语句
可以方便的在 sql 语句中实现某些逻辑. 总体说来mybatis 动态SQL 语句主要有以下几类:
1. if 语句 (简单的条件判断)
2. where (主要是用来简化sql语句中where条件判断的,能智能的处理 and or ,不必担心多余导致语法错误)
3. choose (when,otherwize) ,与 jstl 中的choose 很类似.
4. set (主要用于update语句)
5. trim (在自己包含的内容前加上某些前缀或者后缀) 与java中的trim要区分开
6. foreach (在实现in 语句查询时特别有用)
新建博客表:
create table t_blog( id int PRIMARY key, title varchar(100), content varchar(500), owner varchar(50) )
insert into t_blog values(1,'标题1','内容1','张三'); insert into t_blog values(2,'标题2','内容2','张三'); insert into t_blog values(3,'标题3','内容3','张三'); insert into t_blog values(4,'标题4','内容4','张三');
|
7.1. if 语句处理
<select id="dynamicIfTest" parameterType="Blog" resultType="Blog"> |
这条语句的意思非常简单,如果你提供了title参数,那么就要满足title=#{title},同样如果你提供了Content和Owner的时候,它们也需要满足相应的条件,之后就是返回满足这些条件的所有Blog,这是非常有用的一个功能,以往我们使用JDBC的时候, 如果我们要达到同样的选择效果的时候,我们就需要拼SQL语句,这是极其麻烦的,比起来,上述的动态SQL就要简单多了
7.2. choose (when,otherwise)
<select id="dynamicChooseTest" parameterType="Blog" resultType="Blog"> |
when元素表示当when中的条件满足的时候就输出其中的内容,跟JSTL标签的choose (when,otherwize),当when中有条件满足的时候,就会跳出choose,即所有的when和otherwise条件中,只有一个会输出,当所有的条件都不满足的时候就输出otherwise中的内容。所以上述语句的意思非常简单, 当title!=null的时候就输出and titlte = #{title},不再往下判断条件,当title为空且content!=null的时候就输出and content = #{content},当所有条件都不满足的时候就输出otherwise中的内容。
7.3. trim(对包含的内容加上 prefix,或者 suffix 等,前缀,后缀)
<select id="dynamicTrimTest" parameterType="Blog" resultType="Blog"> |
trim元素的主要功能是可以在自己包含的内容前加上某些前缀,也可以在其后加上某些后缀,与之对应的属性是prefix和suffix;可以把包含内容的首部某些内容覆盖,即忽略,也可以把尾部的某些内容覆盖,对应的属性是prefixOverrides和suffixOverrides;正因为trim有这样的功能,所以我们也可以非常简单的利用trim来代替where元素的功能
7.4. where (主要是用来简化sql语句中where条件判断的,能智能的处理 and or 条件)
<select id="dynamicWhereTest" parameterType="Blog" resultType="Blog"> |
where元素的作用是会在写入where元素的地方输出一个where,另外一个好处是你不需要考虑where元素里面的条件输出是什么样子的,MyBatis会智能的帮你处理,如果所有的条件都不满足那么MyBatis就会查出所有的记录,如果输出后是and 开头的,MyBatis会把第一个and忽略,当然如果是or开头的,MyBatis也会把它忽略。
7.5. set (主要用于更新时)
<update id="dynamicSetTest" parameterType="Blog"> |
set元素主要是用在更新操作的时候,它的主要功能和where元素其实是差不多的,主要是在包含的语句前输出一个set,然后如果包含的语句是以逗号结束的话将会把该逗号忽略,如果set包含的内容为空的话则会出错。
7.6. foreach
foreach主要用在构建in条件中,它可以在SQL语句中进行迭代一个集合,foreach元素的属性主要有:
- collection 指定传入的参数
- item表示集合中每一个元素进行迭代时的别名
3. open表示该语句以什么开始,
4. separator表示在每次进行迭代之间以什么符号作为分隔符,
5. close表示以什么结束
在使用foreach的时候最关键的也是最容易出错的就是collection属性,该属性是必须指定的,但是在不同情况下,该属性的值是不一样的,主要有一下3种情况:
如果传入的是单参数且参数类型是一个List的时候,collection属性值为list(固定值)
如果传入的是单参数且参数类型是一个array数组的时候,collection的属性值为array(固定值)
如果传入的参数是多个的时候,我们就需要把它们封装成一个Map了,当然单参数也可以封装成map
7.6.1单参数List的类型
<select id="dynamicForeachTest" resultType="Blog"> select * from t_blog where id in <foreach collection="list" item="item" open="(" separator="," close=")"> #{item} </foreach> </select> |
测试类
public static void main(String[] args) throws IOException { Reader reader = Resources.getResourceAsReader("SqlMapConfig.xml"); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder() .build(reader); SqlSession session = sqlSessionFactory.openSession(); try { //foreach list List list = new ArrayList(); list.add(1); list.add(2); List<Blog> blogs =session.selectList("UserMapper.dynamicForeachTest", list); for(Blog b : blogs){ System.out.println(b.getContent()); }
} finally { session.close(); } } |
7.6.2. 数组类型的参数
<select id="dynamicForeach2Test" resultType="Blog"> |
public static void main(String[] args) throws IOException { Reader reader = Resources.getResourceAsReader("SqlMapConfig.xml"); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader); SqlSession session = sqlSessionFactory.openSession(); try { Integer[] array = new Integer[]{1,2}; List<Blog> blogs =session.selectList("UserMapper.dynamicForeachTest", array); for(Blog b : blogs){ System.out.println(b.getContent()); } } finally { session.close(); } } |
7.6.3.Map 类型的参数
<select id="dynamicForeach3Test" resultType="Blog"> |
注意like的语法
collection="ids" 是传入map集合的key title也是传入map集合的key
public static void main(String[] args) throws IOException { Reader reader = Resources.getResourceAsReader("SqlMapConfig.xml"); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader); SqlSession session = sqlSessionFactory.openSession(); try { Map map=new HashMap(); Integer[] array = new Integer[]{1,2}; map.put("ids", array); map.put("title", "标题"); List<Blog> blogs =session.selectList("UserMapper.dynamicForeach3Test", map); for(Blog b : blogs){ System.out.println(b.getContent()); } } finally { session.close(); } } |
通过以上方法,就能完成一般的mybatis 的 动态SQL 语句.最常用的就是 if where foreach这几个,一定要重点掌握
8.内建类型别名
对于普通的Java类型,有许多内建的类型别名
别名 |
映射的类型 |
string |
String |
byte |
Byte |
long |
Long |
short |
Short |
int |
Integer |
integer |
Integer |
double |
Double |
float |
Float |
boolean |
Boolean |
date |
Date |
decimal |
BigDecimal |
bigdecimal |
BigDecimal |
object |
Object |
map |
Map |
hashmap |
HashMap |
list |
List |
arraylist |
ArrayList |
collection |
Collection |
iterator |
Iterator |
9、mysql中like的使用
1. CONCAT('%',#{userName},'%')
2. '%${userName}%'
3. #{ userName } java代码传递的参数是 %张% 既传递的参数中包含%
注意:'%#{userName}%' 语法不对 '%'||#{userName}||'%' 会查询出所有数据
10.mybatis声明周期
SqlSessionFactory
项目级别 一个项目一个 单例
SqlSession
方法级别 一个方法一个sqlsession
9.mybatis缓存(查询缓存)
什么是缓存
Mybatis的一级缓存是指SqlSession。一级缓存的作用域是一个SqlSession。Mybatis默认开启一级缓存。
在同一个SqlSession中,执行相同的查询SQL,第一次会去查询数据库,并写到缓存中;第二次直接从缓存中取。当执行SQL时两次查询中间发生了增删改操作,则SqlSession的缓存清空。
Mybatis的二级缓存是指mapper映射文件。二级缓存的作用域是同一个namespace下的mapper映射文件内容,多个SqlSession共享。Mybatis需要手动设置启动二级缓存。
在同一个namespace下的mapper文件中,执行相同的查询SQL,第一次会去查询数据库,并写到缓存中;第二次直接从缓存中取。当执行SQL时两次查询中间发生了增删改操作,则二级缓存清空。
一级缓存原理
一级缓存区域是根据SqlSession为单位划分的。
每次查询会先去缓存中找,如果找不到,再去数据库查询,然后把结果写到缓存中。Mybatis的内部缓存使用一个HashMap,key为hashcode+statementId+sql语句。Value为查询出来的结果集映射成的java对象。
二级缓存原理
二级缓存是mapper级别的。Mybatis默认是没有开启二级缓存。
第一次调用mapper下的SQL去查询用户信息。查询到的信息会存到该mapper对应的二级缓存区域内。
第二次调用相同namespace下的mapper映射文件中相同的SQL去查询用户信息。会去对应的二级缓存内取结果。
如果调用相同namespace下的mapper映射文件中的增删改SQL,并执行了commit操作。此时会清空该namespace下的二级缓存。
开启二级缓存
1、 在核心配置文件SqlMapConfig.xml中加入以下内容(开启二级缓存总开关):
cacheEnabled设置为 true
<settings>
<setting name="cacheEnabled" value="true"/>
</settings>
2、在映射文件中,加入以下内容,开启二级缓存:
<mapper namespace="BlogMapper">
<cache/>
实现序列化
由于二级缓存的数据不一定都是存储到内存中,它的存储介质多种多样,所以需要给缓存的对象执行序列化。
如果该类存在父类,那么父类也要实现序列化。
禁用二级缓存
该statement中设置userCache=false可以禁用当前select语句的二级缓存,即每次查询都是去数据库中查询,默认情况下是true,即该statement使用二级缓存。
<select id="selectUsers" parameterType="java.lang.String" resultType="User" useCache=”false”> <![CDATA[ select * from user_m where userName like #{userName} ]]> </select> |
刷新二级缓存(清空缓存)
<insert id="addUser" parameterType="User" flushCache="true"> <![CDATA[ insert into user_m(id,userName,userAge,userAddress) values(#{id},#{userName},#{userAge},#{userAddress}) ]]> </insert> |
在mapper的同一个namespace中,如果有其它insert、update、delete操作数据后会自动刷新缓存,如果改成false则不会刷新