原理详解
MyBatis应用程序根据XML配置文件创建SqlSessionFactory,SqlSessionFactory在根据配置,配置来源于两个地方,一处是配置文件,一处是Java代码的注解,获取一个SqlSession。SqlSession包含了执行sql所需要的所有方法,可以通过SqlSession实例直接运行映射的sql语句,完成对数据的增删改查和事务提交等,用完之后关闭SqlSession。
优点:
1、简单易学
mybatis本身就很小且简单。没有任何第三方依赖,最简单安装只要两个jar文件+配置几个sql映射文件易于学习,易于使用,通过文档和源代码,可以比较完全的掌握它的设计思路和实现。
2、灵活
mybatis不会对应用程序或者数据库的现有设计强加任何影响。 sql写在xml里,便于统一管理和优化。通过sql基本上可以实现我们不使用数据访问框架可以实现的所有功能,或许更多。
3、解除sql与程序代码的耦合
通过提供DAL层,将业务逻辑和数据访问逻辑分离,使系统的设计更清晰,更易维护,更易单元测试。sql和代码的分离,提高了可维护性。
4、提供映射标签,支持对象与数据库的orm字段关系映射
5、提供对象关系映射标签,支持对象关系组建维护
6、提供xml标签,支持编写动态sql。
缺点:
1、编写SQL语句时工作量很大,尤其是字段多、关联表多时,更是如此。
2、SQL语句依赖于数据库,导致数据库移植性差,不能更换数据库。
3、框架还是比较简陋,功能尚有缺失,虽然简化了数据绑定代码,但是整个底层数据库查询实际还是要自己写的,工作量也比较大,而且不太容易适应快速数据库修改。
4、二级缓存机制不佳
别名
<!-- 配置实体类的别名,配置实体类别名的目的是为了在引用实体类时可以使用实体类的别名来代替实体类,达到简写的目的 -->
<typeAliases>
<!-- 为实体类me.gacl.domain.User配置一个别名_User -->
<!-- <typeAlias type="me.gacl.domain.User" alias="_User"/> -->
<!-- 为me.gacl.domain包下的所有实体类配置别名,MyBatis默认的设置别名的方式就是去除类所在的包后的简单的类名
比如me.gacl.domain.User这个实体类的别名就会被设置成User
-->
<package name="me.gacl.domain"/>
</typeAliases>
<package name="me.gacl.domain"/>就表示为这个包下面的所有实体类设置别名。MyBatis默认的设置别名的方式就是去除类所在的包后的简单的类名,比如me.gacl.domain.User这个实体类的别名就会被设置成User。
使用注解指明方法要执行的SQL
/**
* 定义sql映射的接口,使用注解指明方法要执行的SQL
*/
public interface UserMapperI {
//使用@Insert注解指明add方法要执行的SQL
@Insert("insert into users(name, age) values(#{name}, #{age})")
public int add(User user);
//使用@Delete注解指明deleteById方法要执行的SQL
@Delete("delete from users where id=#{id}")
public int deleteById(int id);
//使用@Update注解指明update方法要执行的SQL
@Update("update users set name=#{name},age=#{age} where id=#{id}")
public int update(User user);
//使用@Select注解指明getById方法要执行的SQL
@Select("select * from users where id=#{id}")
public User getById(int id);
//使用@Select注解指明getAll方法要执行的SQL
@Select("select * from users")
public List<User> getAll();
}
需要说明的是,我们不需要针对UserMapperI接口去编写具体的实现类代码,这个具体的实现类由MyBatis帮我们动态构建出来,我们只需要直接拿来使用即可。
在conf.xml中引用
<mappers>
<!-- 注册userMapper.xml文件,
userMapper.xml位于me.gacl.mapping这个包下,所以resource写成me/gacl/mapping/userMapper.xml-->
<mapper resource="me/gacl/mapping/userMapper.xml"/>
<!-- 注册UserMapper映射接口-->
<mapper class="me.gacl.mapping.UserMapperI"/>
</mappers>
解决实体类中的属性名和表中的字段名不一致
当实体类中的属性名和表中的字段名不一致时,使用MyBatis进行查询操作时无法查询出相应的结果的问题以及针对问题采用的两种办法:
解决办法一: 通过在查询的sql语句中定义字段名的别名,让字段名的别名和实体类的属性名一致,这样就可以表的字段名和实体类的属性名一一对应上了,这种方式是通过在sql语句中定义别名来解决字段名和属性名的映射关系的。
解决办法二: 通过<resultMap>来映射字段名和实体类属性名的一一对应关系。这种方式是使用MyBatis提供的解决方式来解决字段名和属性名的映射关系的。
SQL片段
1. <select id="findUserList" parameterType="cn.bj.mybatis.model.UserQueryVO"
2. resultType="cn.bj.mybatis.model.UserCustom">
3. select * from t_user
4. <!-- where可以自动去掉第一个条件的and -->
5. <where>
6. <!-- sql片段引入 -->
7. <include refid="query_user_where"></include>
8. </where>
9. </select>
10. <!-- sql片段 -->
11. <sql id="query_user_where">
12. <if test="userCustom!=null">
13. <if test="userCustom.sex!=null and userCustom.sex!=''">
14. and t_user.sex=#{userCustom.sex}
15. </if>
16. <if test="userCustom.username!=null and userCustom.username!=''">
17. and t_user.username like '%${userCustom.username}%'
18. </if>
19. </if>
20. </sql>
动态sql
1. <select id="findUserList" parameterType="cn.bj.mybatis.model.UserQueryVO"
2. resultType="cn.bj.mybatis.model.UserCustom">
3. select * from t_user
4. <!-- where可以自动去掉第一个条件的and -->
5. <where>
6. <if test="userCustom!=null">
7. <if test="userCustom.sex!=null and userCustom.sex!=''">
8. and t_user.sex=#{userCustom.sex}
9. </if>
10. <if test="userCustom.username!=null and userCustom.username!=''">
11. and t_user.username like '%${userCustom.username}%'
12. </if>
13. </if>
14. </where>
15. </select>
一对一关联
public class Person2 {
private String pid;
private String truename;
private Person person;
….}
<select id="getPerson" parameterType="String" resultType="person">
select * from person where pid=#{pid}
</select>
<select id="getPerson2" parameterType="String" resultMap="One2One">
select * from person_detail where pid=#{pid}
</select>
<resultMap id="One2One" type="person2">
<id column="pid" property="pid" />
<result column="truename" property="truename" />
<association column="pid" property="person" select = "getPerson"/>
</resultMap>
association 中根据 pid 来关联,执行的 select = "getPerson" 赋值到 person 属性中 来完成一对一映射
一对多
private List<Area> areas;
<select id="One2More" parameterType="String" resultMap="one2moremap">
select d.*,p.pname,a.areaname from person_detail d,area a,person p where p.pid = #{pid}
and d.truename = a.truename
</select>
<resultMap type="person2" id="one2moremap">
<id column="pid" property="pid"/>
<result column="truename" property="truename"/>
<association column="pid" property="person" javaType="com.sds.bean.Person">
<id column="pid" property="pid"/>
<result column="pname" property="pname"/>
</association>
<collection property="areas" ofType="com.sds.bean.Area">
<result column="truename" property="truename"></result>
<result column="areaname" property="areaname"/>
</collection>
</resultMap>
多对一关联
<select id="More2One" resultMap="More2OneMap" parameterType="java.util.Map">
select d.* ,a.*from area a ,person_detail d where a.areaname =#{areaname}
and a.truename = d.truename;
</select>
<resultMap type="area" id="More2OneMap">
<result column="truename" property="truename"/>
<result column="areaname" property="areaname"/>
<association column="pid" property="person2" javaType="com.sds.bean.Person2">
<id column="pid" property="pid"/>
<result column="pname" property="pname"/>
</association>
</resultMap>
MyBatis调用存储过程
1.无输入和输出参数的存储过程
我写了一个比较简单的,需要注意的是Oracle无参存储过程不能写括号
CREATE OR REPLACE Procedure cascadeoperation
As
Begin
Delete From teacher Where id=1;
Update studentdetail Set address='宁波市海曙区' Where studentid=10;
End;
这里执行了2个操作,可能用过mybatis的人会迷惑执行的时候到底使用update标签呢还是delete标签,其实都行,我也试过select标签也是OK的,下面是部分的配置文件
<delete id="cascadeOperation" statementType="CALLABLE" >
{call cascadeoperation}
</delete>
2.带有输入和输出参数的存储过程
CREATE OR REPLACE Procedure queryTeacher(fid In Integer,Type In Varchar,Name Out Varchar)
As
Begin
If Type='1' then
Select Name Into Name From student Where id=fid;
Else if Type='2' Then
Select Name Into Name From teacher Where id=fid;
Else
Name:='错误';
End If;
End If;
End;
下面顺便把我在命令行窗口执行的存储过程语句贴出来
Declare
Name Varchar2(50);
Begin
queryteacher(3,'2',Name);
DBMS_OUTPUT.put_line(Name);
End;
/
下面使用 mybatis 来执行这个存储过程,下面是映射文件的写 法
<select id="queryTeacher" statementType="CALLABLE" parameterType="java.util.Map">
{call queryTeacher(#{fid,mode=IN,jdbcType=INTEGER},#{type,mode=IN,jdbcType=VARCHAR},#{name,mode=OUT,jdbcType=VARCHAR})}
</select>
那怎么取得返回的内容呢,其实只要 存储过程执行后 map 里就有值了 , java 代码大致如 下
Map<String,Object> mm=new HashMap<String,Object>();
mm.put("fid", 3);
mm.put("type", 2);
m.queryTeacher(mm);
System.out.println(mm.get("name"));
3.返回游标的存储过程
还有一种存储过程,它可以返回一个游标就类似一个集合这种
CREATE OR REPLACE Procedure getTeacher(cur_arg out Sys_Refcursor)
As
begin
open cur_arg for Select * From teacher;
End;
这种情况,在 mybatis 里就稍微有些不同了,此时 jdbcType 就是 CURSOR , javaType 则是 ResultSet 了,这里还可以把结果转成 resultMap 了,如下所 示
<resultMap id="resultMap3" type="org.lxh.module.usefunction.info.Teacher">
<result property="address" column="address"/>
<result property="name" column="name"/>
<result property="id" column="id"/>
</resultMap>
<select id="getAllTeacherInfo" statementType="CALLABLE" parameterType="java.util.Map" >
{call GETTEACHER(#{result,jdbcType=CURSOR,mode=OUT,javaType=ResultSet, resultMap=resultMap3})}
</select>
返回游标 可以直接用下面的方法 上面原作者的写法太麻烦了
Map map = new HashMap();
map.put("jid", jid);
userInfoMapper.getFriendList(map);
//result 为在mybatis xml文件时 写的返回结果名
List<UserInfo> list = (List<UserInfo>)map.get("result");
return list;
}
MyBatis 调用函数
1.先在pl/sql中编写测试好自定义函数,待用。
2.在mybatis的mapper映射文件中调用函数
<select id="getUerids" statementType="CALLABLE" parameterType="Java.util.Map">
{#{userids,mode=OUT,jdbcType=VARCHAR} = call F_GET_ROLEIDS(#{userid,mode=IN,jdbcType=VARCHAR})}
</select>
3.java中的调用
Map map = new HashMap();
map.put("userids", "");
map.put("userid", 4);
sqlSessionTemplate.selectOne("test.getUerids", map);
String ids = (String)map.get("userids");
说明:
java.util.Map中put了userid、userids两个属性,其中userid存放了函数的输入参数,userids用于存放函数调用后的返回值。