mybatis 原理
Mybatis通过xml或注解的方式将要执行的各种statement(statement、preparedStatemnt、CallableStatement)配置起来,
并通过java对象和statement中的sql进行映射生成最终执行的sql语句,最后由mybatis框架执行sql并将结果映射成java对象并返回。
测试
1 public class MybatisTest {
2 public static void main(String[] args)throws Exception { 3 //1.读取配置文件 4 InputStream in = Resources.getResourceAsStream("SqlMapConfig.xml"); 5 //2.创建 SqlSessionFactory 的构建者对象 6 SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder(); 7 //3.使用构建者创建工厂对象 SqlSessionFactory 8 SqlSessionFactory factory = builder.build(in); 9 //4.使用 SqlSessionFactory 生产 SqlSession 对象 10 SqlSession session = factory.openSession(); 11 //5.使用 SqlSession 创建 dao 接口的代理对象 12 IUserDao userDao = session.getMapper(IUserDao.class); 13 //6.使用代理对象执行查询所有方法 14 List<User> users = userDao.findAll(); 15 for(User user : users) { 16 System.out.println(user); 17 } 18 //7.释放资源 19 session.close(); 20 in.close(); 21 } 22 }
mybatis用到的设计模式
创建工厂 :使用 构建者模式,把对象的创建细节隐藏 ,使用者直接调用方法即可拿到对象, SqlSessionFactory factory = builder.build(in);
生产SqlSession 使用了工厂模式 ,解耦,降低类之间的依赖关闭 factory.openSession();
创建dao接口实现类使用了代理模式,不修改源码的基础上对已有方法的增强 session.getMapper(IUserDao.class);
创建多个类的优势:灵活的封装,应用中选择更多
映射文件的配置
举例:
User findUserById(Integer userId)
1 <?xml version="1.0" encoding="UTF-8" ?> 2 <!DOCTYPE mapper 3 PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" 4 "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> 5 <mapper namespace="com.qyy.dao.IUserDao"> 6 <select id="findUserById" parameterType="int" resultType="com.qyy.domain.User"> 7 SELECT * FROM USER WHERE id = #{id} 8 </select> 9 </mapper>
namespace:命名空间与定义的接口的全限定名保持一致
id与接口方法保持一致
parameterType 指定传入参数的类型,可以使用别名或者类的全限定名。它可以接收简单类型,POJO对象、HashMap。
resultType 指定结果集的类型,查询的列名和映射的pojo属性名完全一致,该列才能映射成功。
如果查询出来的列名和属性名不一致,通过定义一个resultMap将列名和pojo属性名之间作一个映射关系。
sql语句中的 #{ } 代表占位符,用于执行语句时替换的实际的数据,具体的数据是由#{ }里面的数据决定的
ognl表达式 语法格式就是使用 #{对象.对象}的方式 ,#{user.username}它会先去找 user 对象,然后在 user 对象中找到 username 属性,并调用
getUsername()方法把值取出来。但是我们在 parameterType 属性上指定了实体类名称,所以可以省略 user.
而直接写 username。
主键自增
1 <insert id="insertUser" parameterType="com.qyy.domain.User"> 2 <!-- 3 [selectKey标签]:通过select查询来生成主键 4 [keyProperty]:指定存放生成主键的属性 5 [resultType]:生成主键所对应的Java类型 6 [order]:指定该查询主键SQL语句的执行顺序,相对于insert语句 7 [last_insert_id]:MySQL的函数,要配合insert语句一起使用 --> 8 <selectKey keyProperty="id" resultType="int" order="AFTER"> 9 SELECT LAST_INSERT_ID() 10 </selectKey> 11 <!-- 如果主键的值是通过MySQL自增机制生成的,那么我们此处不需要再显示的给ID赋值 --> 12 INSERT INTO USER (username,sex,birthday,address) 13 VALUES(#{username},#{sex},#{birthday},#{address}) 14 </insert>
${ } 和 #{ }
#{ }:相当于预处理中的占位符?。
#{ }可以防止SQL注入。
#{ }里面的参数表示接收java输入参数的名称。
#{ }可以接受HashMap、POJO类型的参数。当接受简单类型的参数时,#{}里面可以是value,也可以是其他。
${ }:相当于拼接SQL串,对传入的值不做任何解释的原样输出。
${ }会引起SQL注入,所以要谨慎使用。
${ }可以接受HashMap、POJO类型的参数,当接受简单类型的参数时,${}里面只能是value。
resultMap
resultMap 标签可以建立查询的列名和实体类的属性名称不一致时建立对应关系。从而实现封装。
1 <resultMap id="userMap" type="com.qyy.domain.User"> 2 <id column="id" property="userId"/> 3 <result column="username" property="userName"/> 4 <result column="sex" property="userSex"/> 5 <result column="address" property="userAddress"/> 6 <result column="birthday" property="userBirthday"/> 7 </resultMap>
id属性 :给定一个唯一标识,是给查询 select 标签引用用的
type属性:指定实体类的全限定类名
id 标签:用于指定主键字段
result 标签:用于指定非主键字段
column 属性:用于指定数据库列名
property 属性:用于指定实体类属性名称
SqlMapConfig,xml
typeAliases(类型别名)
1 <typeAliases> 2 <!-- 单个别名定义 --> 3 <typeAlias alias="user" type="com.qyy.domain.User"/> 4 <!-- 批量别名定义,扫描整个包下的类,别名为类名(首字母大写或小写都可以) --> 5 <package name="com.itheima.domain"/> 6 <package name="其它包"/> 7 </typeAliases>
mappers(映射器)
1.使用类路径的资源
<mapper resource=" "/>
2.使用mapper接口类路径 ,此种方法要求 mapper 接口名称和 mapper 映射文件名称相同,且放在同一个目录中。
<mapper class=" "/>
3.注册指定包下所有的接口,此种方法要求 mapper 接口名称和 mapper 映射文件名称相同,且放在同一个目录中。
<package name = " "/>
1 <mapper resource="com/qyy/dao/IUserDao.xml" /> 2 <mapper class="com.qyy.dao.UserDao"/> 3 <package name="cn.itqyy.mybatis.mapper"/>
mybatis 连接池
dataSource type=" "
UNPOOLED 不使用连接池的数据源
POOLED 使用连接池的数据源
JNDI 使用 JNDI 实现的数据源
mybatis 自动提交事务
1 @Before 2 public void init()throws Exception { 3 //1.读取配置文件 4 in = Resources.getResourceAsStream("SqlMapConfig.xml"); 5 //2.创建构建者对象 6 SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder(); 7 //3.创建 SqlSession 工厂对象 8 factory = builder.build(in); 9 //4.创建 SqlSession 对象 10 session = factory.openSession(true); 11 //5.创建 Dao 的代理对象 12 userDao = session.getMapper(IUserDao.class); 13 } 14 15 16 @After 17 public void destroy() throws Exception{ 18 //7.释放资源 19 session.close(); 20 in.close(); 21 }
动态sql
if
choose(when,where)
trim(where,set)
foreach
<if>
举例:
List<User> findByUser(User user);
1 <select id="findByUser" resultType="user" parameterType="user"> 2 select * from user where 1=1 3 <if test="username!=null and username != '' "> 4 and username like #{username} 5 </if> 6 <if test="address != null"> 7 and address like #{address} 8 </if> 9 </select>
<if>标签的 test 属性中写的是对象的属性名,如果是包装类的对象要使用 OGNL 表达式
<where>
简化where 1 = 1 的条件拼装
1 <select id="findByUser" resultType="user" parameterType="user"> 2 select * from user 3 <where> 4 <if test="username!=null and username != '' "> 5 and username like #{username} 6 </if> 7 <if test="address != null"> 8 and address like #{address} 9 </if> 10 </where> 11 </select>
<foreach>
进行范围查询时,就要将一个集合中的值,作为参数动态添加
举例:
在QueryVo加入List集合用于封装参数
1 public class QueryVo implements Serializable { 2 private List<Integer> ids; 3 //getter setter 4 5 }
List<User> findInIds(QueryVo vo);
1 <select id="findInIds" resultType="user" parameterType="queryvo"> 2 <!-- select * from user where id in (1,2,3,4,5); --> 3 select * from user 4 <where> 5 <if test="ids != null and ids.size() > 0"> 6 <foreach collection="ids" open="id in ( " close=")" item="uid" 7 separator=","> 8 #{uid} 9 </foreach> 10 </if> 11 </where> 12 </select>
collection:代表要遍历的集合元素,注意编写时不要写#{}
open:代表语句的开始部分
close:代表结束部分
item:代表遍历集合的每个元素,生成的变量名
sperator:代表分隔符
完