MyBatis基础

来源:互联网 发布:我的邻居是exo网络剧 编辑:程序博客网 时间:2024/04/28 05:18

一.MyBatis是什么?

MyBatis是一个持久层的框架,在项目中负责对数据库的操作,是阿帕奇下的顶级项目。

MyBatis让程序猿将精力放在sql语句上,通过MyBatis提供的映射方式,自由灵活生成(半自动化,大部分需要程序猿写sql语句)

MyBatis可以将PreparedStatement中的输入参数自动进行输入映射,将查询结果集灵活映射成java对象(输出映射)。

二.MyBatis框架

1.SqlMapConfig.xml :是mybatis的全局配置文件,配置了数据源、事物等运行环境。

2.mapper.xml ;配置映射关系(配置sql语句)

三.MyBatis 操作数据库步骤:

1.配置 mybatis 配置文件 SqlMapCOnfig.xml(名称不固定)

2.通过配置文件加载运行环境,创建SqlSessionFactory 会话工厂(SqlSessionFactory 实际使用时按单例模式)

3.通过SqlSessionFactory 创建 SqlSession,

SqlSession 是一个面向用户接口(提供数据库操作方法)实现对象是线程不安全的建议应用场合在方法体内

4.掉用 sqlsession 方法去操作数据,如果需要执行提交则调用 sqlSession 的 commit()

5.释放资源,关闭sqlSession.

 (sqlsession  内部通过  Executor (执行器,是一个接口,缓存执行器)操作数据库。

mapped statement: 底层封装对象,作用:对数据库存储封装,包括sql语句,输入参数,输出结果类型)

四.mapper.xml映射文件使用案例

案例①:根据id查询用户

数据源已在SqlMapConfig中配置完毕

该映射文件命名为 UserMapper.xml,配置内容:

<!-- namespace是命名空间 ,作用是对sql进行分类化管理,理解sql隔离 --><mapper namespace="test"><!-- 在映射文件中配置很多sql语句--><!-- 通过select 执行数据库查询操作:id:标识映射文件中的sql,称为statement 的 id.parameterType:指定传入参数类型,这里指定int 类型resultType:指定sql输出结果所映射的java对象类型,select 指定 resultType 表示将查询到的单条记录映射成java对象,此时要建一个java类,属性与数据库字段名对应这里就建了一个User类每个属性对应查询到的记录字段名。#{}:标识一个占位符,这里sql语句下完后不加分号#{ID}:其中的ID表示接入输入的参数,参数名就是 ID ,如果输入的参数是简单类型,则{}中的名字任意,可以 ID 也可以是 ID1、ID2.......--><select id="findUserById" parameterType="int" resultType="cn.mybatis.entity.User">select * from user  where id=#{id}</select></mapper>注意:在mapper映射文件编写完后,需要在sqlMapConfig.xml文件中加载mapper映射文件<mappers><mapper resource="sqlMap/UserMapper.xml" /></mappers>

下面编写java代码完成案例

public void findUserById(){//声明mybatis配置文件String resource = "SqlMapConfig.xml";//得到配置文件流InputStream is  = Resources.getResourceAsStream(resource);//创建会话工厂,传入mybatis配置文件信息SqlSessionFactory ssf =new SqlSessionFactoryBuilder().build(is);//通过工厂得到sqlSessionSqlSession ss = ssf.openSession();//通过SqlSession操作数据库该方法的第一个参数:映射文件中statement 的 id ,等于namespace+ . +statement 中的id第二个参数:指定和映射文件中所匹配的和parameterType 类型的参数该方法的返回结果是映射文件中 resultType 所指定的类型对象User user = ss.selectOne("test.findUserById",1);system.out.println(user);//释放会话资源ss.clolse():
案例②:根据用户名称模糊查询用户信息(可能返回多条记录)

在上述UserMapper.xml追加如下代码:

<!-- resultType:指定单条记录所映射的java 对象类型,无论返回结果是单条还是多条--><!--${}:表示拼接sql串,将接收到的参数不加任何修饰拼接到sql语句中,使用${}拼接sql会引起sql注入 ${value}:接收参数的内容,如果传入的参数是简单类型的那么${}中只能是value--><select id="findUserByName" parameterType="java.lang.String" resultType="cn.mybatix.entity.User">select * from User where userName like '%${value}%'</select>

编写java 代码

public void findUserByName(){//声明mybatis配置文件String resource = "SqlMapConfig.xml";//得到配置文件流InputStream is  = Resources.getResourceAsStream(resource);//创建会话工厂,传入mybatis配置文件信息SqlSessionFactory ssf =new SqlSessionFactoryBuilder().build(is);//通过工厂得到sqlSessionSqlSession ss = ssf.openSession();//List中的 user 和映射文件中resultType所指定的类型一致,查询返回的结果是多条记录用 selectList()方法。List<Uest>list = ss.selectList("test.findUserByNmae","小明");system.out.println(list);ss.close();}
案例③:添加用户

在UseMapper.xml中追加如下代码:

<!--此处parameterType指定的参数是 对象类型,无返回对象则不用设置resultType#{}指定参数对象的属性名,mybatis通过OGNL技术获取对象的属性值--><insert id="insertUser" parameterType="cn.mybatis.entity.User">insert into User values(#{userName},#{birthday},#{sex},#{address})</insert>


下面编写java代码

public void insertUser(){/声明mybatis配置文件String resource = "SqlMapConfig.xml";//得到配置文件流InputStream is  = Resources.getResourceAsStream(resource);//创建会话工厂,传入mybatis配置文件信息SqlSessionFactory ssf =new SqlSessionFactoryBuilder().build(is);//通过工厂得到sqlSessionSqlSession ss = ssf.openSession();//插入用户对象User user = new User();user.setUserName("李白");user.setBirthday(new Date());user.setSex(1);user.setAddress("长安");ss.insert("test.insertUser",user);//提交事务ss.commit();//关闭会话ss.close();}

案例④:删除用户和更新用户

在UseMapper.xml中追加如下代码:

<delete id="deleteUser" parameterType="java.lang.Ingeger" >delete from user where id=#{id}</delete><!-- 执行更新操作需要传入用户 id 和用户更新信息,parameterType 指定 id 和用户更新信息,注意:id 必须存在#{id} 从 user 对象中获取id 属性值--><update id="updateUser" parameterType="cn.mybatis.entity.User">update  User set userName=#{userName},birthday=#{birthday},sex=#{sex},address=#{address} where id=#{id}</update>

下面编写java代码 测试删除和更新功能

public void deleteUser(){声明mybatis配置文件String resource = "SqlMapConfig.xml";//得到配置文件流InputStream is  = Resources.getResourceAsStream(resource);//创建会话工厂,传入mybatis配置文件信息SqlSessionFactory ssf =new SqlSessionFactoryBuilder().build(is);//通过工厂得到sqlSessionSqlSession ss = ssf.openSession();ss.delete("test.deleteUser",2);ss.commit();ss.close();}
 public void updateUser(){声明mybatis配置文件String resource = "SqlMapConfig.xml";//得到配置文件流InputStream is  = Resources.getResourceAsStream(resource);//创建会话工厂,传入mybatis配置文件信息SqlSessionFactory ssf =new SqlSessionFactoryBuilder().build(is);//通过工厂得到sqlSessionUser user = new User();user.setId(3);user.setUserName("李白");user.setBirthday(new Date());user.setSex(1);user.setAddress("长安");ss.update("test.updateUser",user);//提交事务ss.commit();//关闭会话ss.close();}

五.MyBatis 开发Dao

1.原始Dao开发方法(需要写dao接口和dao实现类)

思路:需要向实现类中注入SqlSessionFactory,在方法体内通过SqlSessionFactory创建 SqlSession

接口代码:

public interfice UserDao{//根据ID查询用户信息public User findUserById(int id);//添加用户public void insertUser(User user);//根据ID删除用户信息public void deleteUser(int id);}

实现类代码:

public class UserDaoImpl implements UserDao{//需要向dao实现类中注入 SqlSessionFactory//这里通过构造器注入:private SqlSessionFactory ssf;public UserDaoImpl(SqlSessionFactory ssf){this.ssf = ssf;}//根据ID查询用户信息public User findUserById(int id){SqlSession ss = ssf.openSession(); User user = ss.selectOne("test.findUserById",id);//释放资源ss.close();return user;}//添加用户public void insertUser(User user){SqlSession ss = ssf.openSession();ss.insert("test.insertUser",user);//提交事务ss.commit();//释放资源ss.close();}//根据ID删除用户信息public void deleteUser(int id){SqlSession ss = ssf.openSession();ss.delete("test.deletetUser",id);//提交事务ss.commit();//释放资源ss.close();}}
2.原始方法开发Dao问题总结

1.dao接口实现类方法中存在大量的重复代码,将这些代码取出来以减轻程序猿的工作量

2.调用sqlSession时将 statement 的ID 硬编码了

3.调用sqlSession时传入的参数由于 sqlSession方法使用的泛型即使参数类型传错编译阶段也不会报错,这样不利于程序猿开发

3.mapper 代理方式开发 Dao

思路:程序猿只需要写mapper接口(相当于dao 接口)和 mapper.xml 映射文件

程序猿编写mapper接口时需要遵循一些开发规范,mybatis可以自动生成mapper接口实现类代理对象

规范如下:

①mapper.xml 中的 namespace 等于 mapper 接口的地址 

②mapper.java 中的方法名和 mapper.xml 中 statement 的 id 一致

③mapperr.java中 方法的输入参数类型和 mapper.xml 中 parameterType 指定的类型一致

④ mapper.java 中方法返回值的 类型 和 mapper.xml 中 resultType 指定的类型一致

以上规范是对如下代码进行统一生成:

 User user = ss.selectOne("test.findUserById",id);

ss.insert("test.insertUser",user);
......

   根据以上规范实现 mapper 代理方式开发 Dao

①创建一个接口类 名为 UserMapper 

代码:

public interface UserMappper{//根据ID查询用户信息public User findUserById(int id);//添加用户public void insertUser(User user);//根据ID删除用户信息public void deleteUser(int id);}

②将 UserMapper.xml 中的 namespace 改为 UserMapper 的地址 即 

<mapper  namespace="cn.mybatis.mapper.UserMapper"/>

③编写测试类  

....省略部分代码....

public void testFindUserById(){SqlSession ss = ssf.openSession();//创建 UserMapper 对象UserMapper um = ss.getMapper(UserMapper.class);//调用 UserMapper 的方法User user = um.findUserById(1);system.out.println(user);}

注意:代理对象内部调用selectOne(0或 selectList()

如果 mapper 返回的是单个对象,代理对象内部通过 selectOne()查询数据库

如果 mapper 返回的是集合对象,代理对象内部通过 selectList()查询数据库

六.SqlMapConfig概述

1.将数据库的链接参数单独配置在 db.properties 中,方便对参数进行统一的管理,其它的.xml 可以引用该 db 文件,这样只需要在SqlMapConfig.xml 中加载 db 文件的属性值,而不需要对数据链接参数进行硬编码。

案例:在 SqlMapConfig 中加载 db.properties 文件

db.properties 文件 代码:

jdbc.driver = com.mysql.jdbc.Driverjdbc.url = jdbc:mysql://localhost:3306/mybatisjdbc.username = rootjdbc.password = mysql

SqlMapConfig 中 代码:

<!-- 加载属性文件 --> <properties resource="db.properites" ><!-- 这里还可以为 properties 配置属性和属性值--><property name=" " value=" "/> <properties><!-- 和 Spring 整合后,environments 将废除 --> <environments defutl="development"><environment id = "development"><!-- 使用jdbc事务管理,由mybatis 控制--><transactionManager type = "jdbc"/><!-- 数据库连接池由mybatis 管理--><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>

注意:

properties 特性:mybatis 将会按照下面的顺序加载属性。

在properties元素定义的属性优先被读取。

然后会读取 properites 元素中 resource 或 url 加载的属性,

最后会读取parameterType 传递的属性

建议:

不要在 properties 元素体内添加任何属性值,只将属性定义在properties 文件中,在文件中定义的属性名要有一定的特殊性如 XX.XX

2.settings 

mybatis框架在运行时可以调整一些运行参数,比如 开启二级缓存,开启延迟加载,全局参数将影响mybatis的运行行为。

3.typeAlliases(别名)

在mapper.xml中 需要定义 很多的statement 而且 需要 parametetype 指定输入参数类型,需要resultType指定 输出参数类型。如果在指定类型时输入类型全路径太长会导致不方便开发,因此可以针对这两个参数类型路径定义一个别名。

案例:如上述xml文件中 parameterType和 resultType 的指定参数类型 的路径名就过长,在这里进行定义别名方式引用。

<!-- 针对单个别名定义:

type:类型路径

alias:别名-->

<typeAliases><typeAlias type="cn.mybatis.entity.User"  alias="user"/></typeAliases>

<!-- 批量别名定义(常用)

指定包名,mybatis自动扫描包中的类,自动定义别名,别名就是类名(首字母大小写都可以)

-->

<typeAliases> <package name="cn.mybatis.entity"/>  </typeAliases>


4.typeHandlers(类型处理器)

mybatis 中通过 类型处理器完成jdbc和 java 类型的转换

通常情况下,mybatis 提供的处理器已经满足需要,不用再自定义。

5.mappers(mapper配置)

①通过 resource 加载映射文件

通过 url 加载映射文件

③通过mapper接口加载,遵循规范:将mapper接口类名和mapper.xml 文件名保持一致且在一个目录,前提是使用 mapper代理方式开发 dao 层

<mapper class=" cn.mybatis.entity.UserMapper”/>

④批量加载(常用) mappper:指定 接口的包名,mybatis 会自动扫描并加载包下的所有接口,该方法同样遵循③方式的规范

 <package name="cn.mybatis.mapper"/>  

6.输入映射

通过 parameterType 来指定输入参数的类型,类型可以是 简单类型,hashman,pojo类型(简单的java对象)

案例: 完成用户信息的综合查询(网站里常见的高级查询),需要传入很多的查询条件(可能包括用户信息,其它信息比如商品订单)

分析需求建议使用自定义的包装类型pojo,在其中将复杂的查询条件包装进去。

包装类代码:

pulic  class UserQuert{//这里包装需要查询的条件//用户查询条件private UserCustomer us;/* 省略代码,这里是 us 的 get,set 方法*///还可以包装其它的查询条件如 订单商品。//...}

扩展类代码:

pulic class UserCustomer extends User{//这里可以扩展用户信息}

定义映射文件 ,采用 mapper 代理方式开发

在 UserMapper.xml 中 定义用户信息的综合查询(查询条件复杂,即高级查询一般通过多个表的关联查询)

<select id="findUserList" parameterType="cn.mybatis.po.UserQuery" resultType="cn.mybatis.po.UserQuery">select * from user where user.sex=#{us.sex} and user.username like '%${us.username}%'</select>

mapper 接口代码:

public List<UserCustomer> findUserList(UserQuery userquery);

测试代码:

pulic void testFindUserList(){SqlSession ss = ssf.openSession();//创建UserMapper 对象,mybatis 自动生成mapper 代理对象UserMapper um = ss.getMapper(UserMapper.class);//创建包装类对象设置查询条件UserQuery uq = new UserQuery();UserCustomer uc = new UserCustomer();uc.setSex("1");uc.setUserName("张三");uq.setUserCustomer(uc);//调用 userMapper 方法List<UserCustomer>list = um.findUserList(uq);system.out.println(list);}

7.输出映射

resultType:

①使用resultType 进行映射,查询出来的列名和pojo中的属性名一致,该列才可以映射成功,若全部不一致则不会创建pojo对象

只要 查询出来的列名与 pojo 的属性名有一个 一样,就会创建 pojo 对象

②输出简单类型:

案例:用户信息综合查询的列表总数,通过查询总数和上边用户综合查询列表才可以实现分页

实现:

UserMapper.xml

<!-- 用户信息综合查询总数parameterType:指定输入类型和 findUserList 一样,他俩结合使用才能实现分页查询resultType:输出简单类型--><select id="findUserCount" parameterType="cn.mybatis.po.UserQuery">select count(*) from user where user.sex=#{us.sex} and user.username like '%${us.username}%'</select>

UserMapper.java

//用户信息综合查询总数public int  findUserCount(UserQuery userquery);

测试代码:

pulic void testFindUserCount(){SqlSession ss = ssf.openSession();//创建UserMapper 对象,mybatis 自动生成mapper 代理对象UserMapper um = ss.getMapper(UserMapper.class);//创建包装类对象设置查询条件UserQuery uq = new UserQuery();UserCustomer uc = new UserCustomer();uc.setSex("1");uc.setUserName("张三");uq.setUserCustomer(uc);//调用 userMapper 方法int count = um.findUserCount(uq);system.out.println(count);}

注意:

①查询出来的结果集只有一行且只有一列才可以用简单类型进行映射

②不管输出的是 pojo 单个对象还是list 列表(list中包含多个pojo),在 mapper.xml 中 resultType 指定的 类型对象是 一样的,而在mapper .java 中的方法返回的对象不一样:(案例见上述根据 ID 和 用户名查询 用户信息)

输出单个 pojo对象 返回值是单个对象类型;

输出 pojo 列表时 返回值是 List<pojo>   

之所以有区别是 因为动态代理对象是根据 mapper.java 方法的返回值类型来确定调用 selectOne(返回单个对象调用) 还是 selectList(返回集合调用)


resultMap

mybatis 利用 resultMap 完成高级的输出映射

使用场景:如果查询出来的列名和 要映射对象的 属性名不一致,通过定义一个resultMap  列名和 pojo 属性名 之间做一个映射关系。

①定义 resultMap 

②使用 resultMap 作为 statement  输出映射类型

案例:将下面的 sql 使用 User完成映射

select  id id_,username username_ from user where id = #{value}

user 类中 属性名 和 该查询语句的类名不一致,所以要通过上面两步 完成映射

UserMapper.xml: 

<!-- 定义 resultMap  将上面这条 sql 语句和user 类中的 属性名 做一个映射type:result 最终映射成的 java 对象,可以使用别名id: 对 ResultMap 的 唯一标识--><resultMap type="user" id =" userResultMap"><!-- id 标识结果集中的 唯一标识,column 表示查询出来的列名,property 表示 要映射的java对象中的类名,最终resultMap对column和property做一个映射关系--><id column="id_" property="id" /><!-- result 是对 普通列 的定义--><result column=" username_" property="username "/></resultMap><!-- 使用resultMap 进行输出映射resultMap 指定 定义的 resultMap 的id,如果这个resultMap 在其它的mapper 文件,前面需要加 namespace--><select id="findUserByIdResultMap" parameterType="int" resultMap="userResultMap">select  id id_,username username_ from user where id = #{value}</select>

UserMapper.java

//根据ID查询用户信息,使用 ResultMap 输出public User findUserByIdResultMap(int id);

七.动态Sql

mybatis 的核心是 对 sql 语句进行灵活的操作,通过表达式判读,对sql 进行灵活 拼接组装

1.if 语句

用户信息综合查询列表和用户信息查询列表总数这两个 statement 的定义使用动态 sql

对查询条件进行潘多,如果输入的条件不为空才进行查询条件的拼接

UserMapper.xml

<!-- where 可以自动去掉满足条件中的第一个 and--><select id="findUserList" parameterType="cn.mybatis.po.UserQuery" resultType="cn.mybatis.entity.UserCustom">select * from user <where><if test="userCustom!=null"><if test="userCustom.sex!=null and userCustom.sex!=''">and user.sex=#{userCustom.sex}</if><if test="userCustom.username!=null and userCustom.username!=''">and user.username like '%${userCustom.username}%'</if></if></where></select>

2.sql 片段

将上面实现的 判断代码块抽取出来组成一个sql 片段,其它的statement 就可以引用这个 sql  片段

定义 sql片段

<!--定义 sql片段 id:sql 片段的唯一标识基于单表定义 sql 片段,可重用性高;在 sql  片段中 不要包括 where--><sql id=" query_user_where"><if test="userCustom!=null"><if test="userCustom.sex!=null and userCustom.sex!=''">and user.sex=#{userCustom.sex}</if><if test="userCustom.username!=null and userCustom.username!=''">and user.username like '%${userCustom.username}%'</if></if></sql>

引用 sql 片段

<select id="findUserList" parameterType="cn.mybatis.po.UserQuery" resultType="cn.mybatis.entity.UserCustom">select * from user <where> <!-- 引用 sql 片段的 id 如果 refid 指定的id 不在本mapper 文件中,前面需要加namespace --><include refid="query_user_where"/><!-- 这里还要引用其它的 sql 片段 --></where></select>
3.foreach
向sql 传递数组或 list ,mybatis 用 foreach 解析
案例:在用户查询列表和查询总数的statement 中增加多个id 输入查询。
两种方法:
select * from user where id=1 or id=8 or  id=12
select * from user where id  in{1,8,12}
实现:
①在输入参数类型中添加 List<Integer>ids 传入多个ID

pulic  class UserQuert{//这里包装需要查询的条件//传入多个IDprivate List<Integer>ids//用户查询条件private UserCustomer us;/* 省略代码,这里是 属性 的 get,set 方法*///还可以包装其它的查询条件如 订单商品。//...}
②修改 UserMapper.xml
查询条件定义成一个sql片段,在上述sql片段中增加如下代码

<if test=" ids!=null"><!-- 使用 foreach 遍历传入 IDcollection:指定输入对象中集合属性名item:每次遍历生成的对象名open:开始遍历时要拼接的串close:结束遍历时拼接的串separator: 遍历的两个对象之间需要拼的串--><!-- 使用这句sql 拼接-:and (id=1 or id=8 or id=12 )-><foreach collection=" ids" item="user_id " open="and ( "  close=") "  separator=" or"><!-- -每次遍历要拼接的串->id=#{user_id}</foreach><!-- 使用这句sql 拼接:and id in(1,8,12)--> <foreach collection=" ids" item="user_id " open="and id in ( "  close=") "  separator=" or"><!-- -每次遍历要拼接的串->#{user_id}</foreach></if>
 
原创粉丝点击