mybatis教程
Mybatis
jdbc太繁琐,Hibernate代码简介,但是性能不好,Mybatis中庸,是一个开源的持久层框架
1.开发步骤:
#1 导包(mybatis jdbc驱动)
#2 添加配置文件(mybatis.xml)
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
<configuration>
<environments default="environment">
<environment id="environment">
<transactionManager type="JDBC" />
<!--mybatis自己的datasource数据库信息,提供数据库的参数-->
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://localhost:3306/mydb" />
<property name="username" value="root" />
<property name="password" value="123456" />
</dataSource>
</environment>
</environments>
<!--映射文件地址-->
<mappers>
<mapper resource="entity/EmpMapper.xml" />
</mappers>
</configuration>
#3 实体类(属性名必须和表段名一致,大小写无所谓,有getset方法)
#4 映射文件(放哪儿都可以),把映射文件添加到配置文件中
<!--命名空间是区分不同的sql id,对应一个DAO接口,如果使用映射器,这里的namespace必须等于Mapper接口的完整名称,parameterType是参数类型,可以是基本数据类型、java类类型、Map类型,resultType是返回的结果类型,sql语句没有分号,#{name}表示Emp表中的属性值,利用的反射机制get方法读取这个属性值-->
给类取别名
<typeAliases>
<typeAlias alias="User" type="com.yiibai.pojo.User" />
<typeAlias alias="Order" type="com.yiibai.pojo.Order" />
</typeAliases>
<mapper namespace=”dao.EmpDAO”>
Insert delete update没有resultType,但是在dao的返回值可以写int,代表跟新的记录数,parameterType和resultType可以写它的简称,比如hashmap,
#{id}会根据实体类找到这个id属性值,如果是简单string int这些类型,这样写代表这个类型的值
</mapper>
#5 利用mybatis提供的api访问数据库,插入删除修改必须调用sqlsession的commit(),slect默认提交,使用完必须close()关闭,
sqlsession提供了update()、delete()、insert()、select()、selectOne()返回单行记录,selectList()返回List集合,selectMap()返回Map集合,注意在映射配置文件中的ResultType写的是单行记录返回的类型,比如返回List集合的还是写的实体类,返回Map写的是Map类
SqlSessionFactoryBuilder ssFactoryBuilder = new SqlSessionFactoryBuilder();
SqlSessionFactory ssf = ssFactoryBuilder.build(TestCase.class.getClassLoader().getResourceAsStream("SqlMapConfig.xml"));
sqlSession = ssf.openSession();
sqlSession.select(“findAll”);
sqlsession.commit();
sqlsession.close();
-
原理:
SqlSessionFactory访问配置文件,根据DataSource建立数据库连接,创建一个Map集合,配置文件中访问映射文件,集合的Key是配置文件的id,value是sql语句对应的预编译preparedStatement,调入传入的参数,然后Factory创建SqlSession,sqlsesion根据id找到map中对应的Statement并执行相关方法,SqlSession默认把查询的一条记录中的数据放在一个Map集合里(字段名作为key,字段值。![day08]()
![day08]()
2.接口返回值,增删改返回值可是是boolean long int,void,查询返回值可以是对象,map list等
-
paramertType 和resultType
Mybatisde TypeAlias机制实现了给一些映射起了别名,比如int、long、string、hashmap、list、arraylist...也可以自定义给类写别名。
paramertType 值:
3.1 基本类型:int String #{任意值}
3.2多个参数 mybatis或封装成map对象,key是param1,param2...value是参数值,所以sql应写#{param1} ,也可以在mapper的接口方法参数用@param指定key值
<select id="findByid3" resultMap="emp1">
select * from emp where id = #{param1} and lastname=#{param2}</select>
//使用@param多参数指定
public void update(@param("id") int id,@param("lastname") String lastname)
<select id="findByid3" resultMap="emp1">
select * from emp where id = #{id} and lastname=#{lastname}</select>
3.2 Map类型:map #{key} 对应map的key的值,写一样
3.3实体类: #{属性名} 匹配类的属性值
3.4 list set array类型 paramerty=”list”主要用于foreach动态sql中.sql语句中参数写#{list[0]}
resultType值:update delete insert没有这个属性值,返回类型有基本类型,map类型,实体类,把每行记录封装好了,如果有多行记录,真正在dao返回的时候是封装成List<O>类型的
-
实体类的属性名和表的字段名不一致
方法一:在映射文件中写sql语句用别名,别名写成定义的实体类的,系统自动根据查询出来的字段名(别名)匹配属性名
方法二:使用<ResultMap>元素
告诉mybatis如何将字段和实体类的属性名对应,只需要把不同的列处理即 可,resultType和resultMap不同时存在。Resultmap中的property会根据对应的column名去匹配select出来的字段名。
<!-- resultType改成ResultMap,值是下面的id -->
<select id="findByid3" parameterType="int" resultMap="emp1">
select * from emp where id = #{id}
</select>
<resultMap type="entity.Emp1" id="emp1">
<!-- property是实体类的属性值,column是表的字段名 -->
<!--id是主键的映射,result是普通字段的映射-->
<id property="eId" column="id"/>
<result property="name" column="ename"/>
</resultMap>
问题:如两个表中有相同的字段名,联合查询中resultmap根据property去匹配column会出问题,如下,a c表有相同字段name,select之后两个实体类属性去匹配字段就会出现第二个匹配的竟然是第一个的字段值,可以使用别名解决,select中就给起别名,匹配column的时候写他的别名。
select a.*,r.role_id rId,r.name rName from admin_info a join role_info r on a.role_id=r.role_id where a.admin_id=#{id}
<resultMap type="net.zy.entity.Admin" id="adminMap">
<result property="name" column="name"/>
<association property="role" javaType="net.zy.entity.Role">
<result property="name" column="rName"/>
</association> </resultMap>
-
Mapper映射器
5.1 Mybatis依据Mapper接口(DAO接口),自动生成对应的DAO 实 现类。
5.2 如何编程
Step1:写一个Mapper接口(DAO接口),该接口的方法必须和映射文件的sql定义一致,方法名和sql的id一样,参数类型和sql中定义的参数一样,方法的返回类类型和sql的返回类型一致。
Step2:使用sqlsession的getMapper方法返回Mapper映射器的实现类。再调用这个实现类的方法。
EmpDAO empDAO = sqlSession.getMapper(EmpDAO.class);
List<Emp> = empDAO.dindAll();
注意映射文件中的namespace名称必须是DAO接口的完整名称。
总结:
使用mybatis就有两种方法,第一种就是在配置好映射文件之后,直接使用sqlsession的一系列方法获取结果,第二种就是使用mapper映射器,用sqlsession创建mapper的实现类,获取结果
6.mybatis映射文件可以把重复的提取出来
<sql id="defaultSql">
select * from user
</sql>
<select id="findAll" resultType="user">
<include refid="defaultSql"></include>
</select>
7.Mybatis的延迟加载,mybatis.xml配置文件中开启
<settings>
<setting name="lazyLoadingEnabled" value="true"/>
<setting name="aggressiveLazyLoading" value="false"/>
</settings>
在assocation或者collection中使用select通过调用其他sql查询这种方式时就会延迟加载,只有对象使用的时候采取查询。
8.Mybatis 一级缓存和二级缓存
一级缓存是 SqlSession 级别的缓存,查询是先从缓存中查,查到了酒不查数据库,当调用 SqlSession 的修改,添加,删除,commit(),close()等,session缓存清空。
二级缓存多个sqlSession公用的,在配置文件中,然后再mapper映射文件中配置
<settings>
<!-- 开启二级缓存的支持 -->
<setting name="cacheEnabled" value="true"/> //默认开启
</settings>
//在mapper中开启缓存,在所要缓存的select设置useCache=true
<mapper namespace="com.itheima.dao.IUserDao">
<!-- 开启二级缓存的支持 -->
<cache></cache>
<select id="findById" resultType="user" parameterType="int" useCache="true">
select * from user where id = #{uid}
</select>
</mapper>
9.注解版的mybatis
@Insert:实现新增
@Update:实现更新
@Delete:实现删除
@Select:实现查询
@Result:实现结果集封装
@Results:可以与@Result 一起使用,封装多个结果集
@ResultMap:实现引用@Results 定义的封装
@One:实现一对一结果集封装
@Many:实现一对多结果集封装
@SelectProvider: 实现动态 SQL 映射
@CacheNamespace:实现注解二级缓存的使用
用法:在dao接口上使用,然后再mybatis.xml配置文件中定义mapper的映射是注解<mappers><package name="com.itheima.dao"/></mappers>
public interface IUserDao {
@Select("select * from user")
@Results(id="userMap",
value= {
@Result(id=true,column="id",property="userId"),
@Result(column="username",property="userName"),
})
List<User> findAll();
@Select("select * from user where id = #{uid} ")
@ResultMap("userMap")
User findById(Integer userId);
@Insert("insert into
user(username,sex,birthday,address)values(#{username},#{sex},#{birthday},#{address}
)")
@SelectKey(keyColumn="id",keyProperty="id",resultType=Integer.class})
int saveUser(User user);
@Update("update user set
username=#{username},address=#{address},sex=#{sex},birthday=#{birthday} where id
=#{id} ")
int updateUser(User user);
@Delete("delete from user where id = #{uid} ")
int deleteUser(Integer userId);
}
//多对于 one表示一对一查询,@One(select = “方法全路径) 表示我们调用的方法,fetchType=FetchType.LAZY代表延迟加载
@Select("select * from account")
@Results(id="accountMap",
value= {
@Result(id=true,column="id",property="id"),
@Result(column="uid",property="uid"),
@Result(column="money",property="money"),
@Result(column="uid",
property="user",
one=@One(select="com.itheima.dao.IUserDao.findById",
fetchType=FetchType.LAZY)
)
})
List<Account> findAll();
//一对多
@Select("select * from user")
@Results(id="userMap",
value= {
@Result(id=true,column="id",property="userId"),
@Result(column="username",property="userName"),
@Result(column="sex",property="userSex"),
@Result(column="address",property="userAddress"),
@Result(column="birthday",property="userBirthday"),
@Result(column="id",property="accounts", //id表示把这个值当做findByUid的参数穿进去查询
many=@Many(
select="com.itheima.dao.IAccountDao.findByUid",
fetchType=FetchType.LAZY
)
)
})
List<User> findAll();
//注解实现二级缓存,配置文件先要开启二级缓存支持
@CacheNamespace(blocking=true)//mybatis 基于注解方式实现配置二级缓存
public interface IUserDao {}
spring集成mybatis
编程步骤
#1 导包 spring-webmvc spring-jdbc mybatis-spring mybatis dbcp mysql驱动
#2 添加配置文件 spring的配置文件,mybatis的配置信息添加到spring配置文件中。Mybatis-spring中提供了一个Bean叫做SqlSessionFactoryBean为整合提供SqlSession的数据库的连接和映射文件的位置,配置如下
SqlSessionFactoryBean有几个属性值可以设置:
必须属性:dateSource 数据源设置
可选属性:mapperLocations 设置映射文件的地址
可选属性:configLocation 设置mybatis的xml配置文件,文件可以不是完整的,比如没有dateSource的设置
<!-- 配置DataSource -->
<!-- 从db.properties中读取属性值 -->
<util:properties id="properties" location="classpath:db.properties"/>
<!--注入BadicDataSource类 -->
<bean id="bds" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="#{properties.driver}"/>
<property name="url" value="#{properties.url}"/>
<property name="username" value="#{properties.user}"/>
<property name="password" value="#{properties.pwd}"/>
</bean>
<!-- 配置SqlSessionFactoryBean -->
<bean id="sqlsessionfactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<!-- 指定连接源 -->
<property name="dataSource" ref="bds"/>
<!-- 指定映射文件 -->
<property name="mapperLocations" value="classpath:entity/*.xml"/>
</bean>
<!--还有 个属性configLocation任选,可以指定mybatis配置文件,不用写全,比如不用配置数据源,-->
#3 实体类
#4 映射文件
#5 Mapper接口,使用mapper映射器
#6在spring配置文件中配置MapperScannerConfigurer,负责将mybatis生成的mapper接口的实现类(自动生成的)放在spring容器中。这个实现类id默认是接口的首字母小写。它会默认注入一个sqlsessionfactorybean
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<!-- 扫描指定包下的接口 -->
<property name="basePackage" value="dao"/>
<!-- 告诉scaner需要扫描的注解是anotation包下面的MybatisAnnotation注解 -->
<property name="annotationClass" value="anotation.MybatisAnnotation"/>
</bean>
注:在开发的时候可能包下有部分接口与mybatis无关,不需要MapperScannerConfigurer扫描,方式一:
Step1:自定义一个注解,在anotation包下自定义一个注解
public @interface MybatisAnnotation{}
Step2:把注解添加到需要扫描的Mapper接口里面
@MybatisAnnotation
public interface EmpDAO{public Emp select();}
Step3:给MapperScannerConfigurer设置一个属性值 annotationClass属性,告诉scaner哪些注解需要扫描
方式二:使用markInterface,
Step1:自定义一个接口,在MapperScannerConfigurer中添加属性markInterface,值是创建的接口,只有继承了这个接口的Mapper才能被注册到spring容器中
启动容器,获取容器中的dao实现类,调用dao的实现类,调用方法
7spring整合mybatis的另一中方法:使用mybatis-spring提供的一个类(了解,少用)
#1 导包
#2 添加配置文件 spring的配置文件,
#3 实体类
#4 映射文件
#5 配置SqlSessionTemplate,这个类对mybatis的api做了封装,比如不用关心如何获得sqlsession,它的关闭。
<bean id="sqlSessionTemplate" class="org.mybatis.spring.SqlSessionTemplate">
<constructor-arg index=”0” ref="sqlSessionFactory" />
</bean>
#6 写一个DAO,在实现类中注入SqlSessionTemplate,调用它的方法即可
总结:
使用spring整合mybatis就有三种方法,第一种就是在配置好映射文件之后,因为配置的sqlsessionfactorybean其实就是创建sqlsessionfactory对象,启动容器,用它创建sqlsession,进而直接调用它的方法,第二种就是使用mapper映射器,用sqlsession创建mapper的实现类,获取结果,这时最常用的,第三种就是配置好映射文件之后,配置sqlsessionTemplate,这种也不常用。
Mybatis动态sql
动态拼凑sql语句,mybatis提供了一套标签,可以实现在xml中动态构建一个sql语句,和jstl类似
<if test=””>...</if>
<choose>
<when test=””></when>
<otherwise>..</otherwise>
</choose>
<foreach></foreach> 主要用于in
<where></where>:顶替一个where关键字,过滤内容前多余的and或者or
<set></set>:和where类似,提供set关键字,过滤内容后面多余的逗号
<delete id=”delete” parameterType=”list/array”>
delete from cn_user where cn_user_id in
<!-- collection写array或者list,代表数组或者集合,id是自己起的元素,separater是定义在元素 之间的符号,,open close是循环前后的符号 -->
<foreach collection="array/list" item="id" separater="," open="(" close=")">
#{id} //这里要用el表达式获取id
</foreach>
</delete>
<where>
<if test="title!=null">
cn_note_title like #{title}
</if>
<if test="status!=null">
and cn_note_status=#{status}
</if>
<if test="creattime!=null">
and cn_note_creattime>=#{creattime}
</if>
</where>
注意:这里< > & 要用实体代替, > < &
<update id="update" parameterType="com.tarena.entity.Emp">
update t_emp
<set>
<if test="ename!=null">
ename=#{ename},
</if>
<if test="job!=null">
job=#{job},
</if>
</set>
where empno=#{empno}
</update>
<!-- 使用trim代替where -->
<!-- 查询当前部门下,大于当前收入的员工 -->
<select id="findByDeptAndSalary2"
parameterType="com.tarena.entity.Condition"
resultType="com.tarena.entity.Emp">
select * from t_emp
<trim prefix="where" prefixOverrides="and|or">
<if test="deptno != null">
and deptno=#{deptno}
</if>
<if test="salary != n bn ull">
and sal>#{salary}
</if>
</trim>
</select>
<!-- 使用trim代替set -->
<update id="update2"
parameterType="com.tarena.entity.Emp">
update t_emp
<trim prefix="set" suffixOverrides=",">
<if test="ename!=null">
ename=#{ename},
</if>
<if test="job!=null">
job=#{job},
</if>
</trim>
where empno=#{empno}
</update>
Mybatis关联映射
对象关系映射:ORM(实体对象 数据库)
实体类--<ORM>--表
实现对象关联属性,把多关联表数据封装成具有关系的对象实体,把有关系的表直接作为其他表的属性。
##单个对象关联映射:比如一个book对象一个user
案列:通过bookId查询有user属性的book
关系映射用法:
2.1 在Book类中添加一个User user属性,(查询所有book集合也同样)
在mapper配置文件中定义:
<!-- mybatis关联映射 -->
<select id="findById" parameterType="string" resultMap="usermap">
select * from cn_notebook where cn_notebook_id=#{id}
</select>
<!--自定义数据映射-->
<resultMap type="cn.tedu.cloudnote.entity.Book" id="usermap">
<!-- book的其他字段和数据表保持一致,就不用再写映射,值需要设置user的映射 -->
<!-- 这个标签表示book中的字段user是javaType这个类型,
他是通过另一个findUser的sql语句, 传入book表中cn_uer_id的这个外键字段查询cn_user表得到的,cn_user_id是数据库的字段 -->
<association property="user"
javaType="cn.tedu.cloudnote.entity.User"
select="findUser" column="cn_uer_id"></association>
</resultMap>
<!--通过userId查询user-->
<select id="findUser" parameterType="string" resultType="cn.tedu.cloudnote.entity.User">
select * from cn_user where cn_uer_id=#{id}
</select>
这里其实就是通过查询book的字段,通过book中查询道德userId,再把每个userId再用另一个dao查询user表,这里会调用n+1次sql,效率不高。
2.2 利用join多表联合查询,把关联数据和主数据联系在一起,通过联合查询,在resultmap中配置各字段即可(效率高)
<!-- 利用join把user和book关联在一起 -->
<select id="findAllbook" resultMap="bookMap">
select * from cn_notebook b join cn_note a on(b.cn_user_id=a.cn_user_id)
</select>
<resultMap type="cn.tedu.cloudnote.entity.Book" id="bookMap">
<!-- 不论book表和字段是否一致都要写映射 -->
<!-- book主键的映射 -->
<id property="cn_book_id" column="cn_book_id"/>
<result property="cn_book_name" column="cn_book_name"/>
<result property="cn_book_user" column="cn_book_user"/>
<!-- 映射user属性 -->
<association property="user" javaType="cn.tedu.cloudnote.entity.User">
<!-- user类中的属性和字段的映射 -->
<id property="cn_user_id" column=""cn_user_id/>
<result property="cn_user_name" column="cn_user_name"/>
</association>
</resultMap>
##集合对象关联映射:比如一个user对应多个book Note类增加List<Book> books属性
-
方法一:先用一个sql查询user对象,再用另一个sql加载关联属性数据,这样直接调用findById就可以得到有book属性的user了
<!-- 集合查询,一个user对应多个book -->
<!-- 利用join查询 -->
<select id="findById" parameterType="string" resultMap="maprule">
select * from cn_user where cn_user_id=#{id}
</select>
<!-- 自定义数据映射规则 -->
<resultMap type="cn.tedu.cloudnote.entity.User" id="maprule">
<!-- user原字段一致,省略,只需加载book -->
<!--表示user中的字段属性books是一个list类型,泛型是Book类,通过调用另一个dindBook的sql语句传入userId查询返回book的集合,cn_user_id是数据库的字段-->
<collection property="books" select="findBook" column="cn_user_id">
</collection>
</resultMap>
<!-- findBook的sql -->
<select id="findBook" parameterType="string" resultType="cn.tedu.cloudnote.entity.Book">
select * from cn_notebook where cn_user_id=#{id}
</select>
-
方法二:利用join查询一次性取出。(查询所有user的信息一样)
<!-- 集合join查询所有user带有book属性的信息 -->
<select id="findUser" resultMap="yserMap">
slect * from cn_user u left outer join cn_notebook b on(u.cn_user_nameid==b.cn_notebook_id)
</select>
<!-- 配置映射,user的所有属性都要配置,不论是否一致 -->
<resultMap type="cn.tedu.cloudnote.entity.User" id="yserMap">
<id property="cn_user_id" column="cn_user_id"/>
<result property="cn_user_name" column="cn_user_name"/>
<!-- 配置book的映射 -->
<collection property="book" javaType="list" ofType="cn.tedu.cloudnote.entity.Book">
<!-- 配置多有books的属性映射 -->
<id property="cn_book_id" column="cn_book_id"/>
<result property="cn_book_name" column="cn_book_name"/>
</collection>
</resultMap>
如何获取表的自动生成的主键值
Mysql在mapper映射文件中插入数据,这两个属性代表设置表的自动生成主键值为true,把生成的这个主键赋值给实体类的no属性值,这样在dao.save()插入数据后就可以通过user.getNo()获取到这个主键值
<insert id=”save” useGeneratedKeys=”true” keyProperty=”no” parameterType=”cn.entity.User”>
Inert into cn_user(name) values(#{name})
</insert>
Oracle中插入数据,先在序列获取主键值,赋值给实体类的no属性值,就可以在插入数据之后通过get方法获取主键值,
<insert id=”save” parameterType=”cn.entity.User”>
<selectKey keyProperty=”no” resultType=”int” order=”BEFORE”>
`select 序列.nextval from dual
</select>
Inert into cn_user(name) values(#{name})
</insert>
Mybaties中#与$的区别?
#{}表示一个占位符号,通过#{}可以实现preparedStatement向占位符中设置值,自动进行java类型和jdbc类型转换。#{}可以有效防止sql注入。 #{}可以接收简单类型值或pojo属性值。 如果parameterType传输单个简单类型值,#{}括号中可以是value或其它名称。
${}表示拼接sql串,通过${}可以将parameterType 传入的内容拼接在sql中且不进行jdbc类型转换, ${}可以接收简单类型值或pojo属性值,如果parameterType传输单个简单类型值,${}括号中只能是value。
5)一般能用#的就别用$

浙公网安备 33010602011771号