(本质:使用反射获得所有东西)
表现: 在接口中使用注解,相当于不需要UserMapper.xml实现类了。
在简单开发中使用,复杂开发中用xm文件方便维护。
步骤:
在接口中使用注解
@Select("select * from mybatis.user") List<User> getUsers();
在 mybatis-config.xml
文件中修改<mapper>
标签属性从resource为class,映射接口
<mappers> <mapper class="com.roy.dao.UserMapper"/> </mappers>
测试类
@Test public void test(){ SqlSession sqlSession = MybatisUtils.getSqlSession(); UserMapper mapper = sqlSession.getMapper(UserMapper.class); List<User> users = mapper.getUsers(); for (User user : users) { System.out.println(user); } sqlSession.close(); }
使用resource获取inputStream中输入的配置文件,
实例化SqlSessionFactoryBuilder构造器:
实例化SqlSessionFactory
之后流程如下:得到事务管理,创建执行器,创建SqlSession,执行sql,回滚到事务管理或者成功,查看结果,结束或者回滚。
如果要设置自动提交事务,在返回SqlSession的方法设置参数
SqlSessionFactory.openSession()函数可以传入参数,判断是否开启事务自动提交 永远不要使用
UserMapper.java接口中:
@Select("selsect * from user where id = #{id}") User getUserByID(@Param("id") int id);//基本类型全部加上这个注解,引用类型不需要加(String类型需要加,其他自定义的类不加) @Insert("insert into user(id, name, pwd) values (#{id},#{name},#{pwd})") int addUser(User user); @Update("updata user set name=#{name}, pwd=#{pwd}, where id = #{id}") int updateUser(User user); @Delete("delete from user where id=#{uid}")// 注解中取的信息名和下面注解中的一样 int deleteUser(@Param("uid") int id);//注解中的信息名优先级最高
测试中相同
@Param() 注解
在基本类型和String类型上添加,如果是引用类型不需要添加
#{}
${}
: 区别: #可以放置Sql注入,相当于PreparedStatement, $相当于直接拼接,和statement一样。
maven下载,记得导入插件啊!
常用注解:
@Data // get, set, toString, hashCode, equals 包括无参构造,但添加有参构造后,需要手动添加无参构造 @AllArgsConstructor //有参构造 @NoArgsConstructor // 无参构造
多个学生关联一个老师【多对一】
一个老师有一个学生集合 【一对多】
之前用结果集的resultType和resultMap属性描述返回值
现在用 association和 collection 处理
association: 一个复杂类型的关联 ,许多结果被包装成这种类型
collection: 一个复杂类型的集合。
这两个标签中, javaType属性: 是返回值的类型,和pojo中的属性类型相同
ofType属性: 是泛型中的类型, 如list中的student
前提:
数据库中:
Student表: id, name, tid(老师的id)
Teacher表: id, name
pojo类中:
Student: int id, String name, Teacher teacher;//第三个参数是一个类, 多个学生对应一个老师
Teacher: int id, String name
从student角度去查询学生和老师信息
根据实体类查所有的student(但数据库中的字段和实体类中的不匹配,并且实体类中有个属性是复杂类型
<!-- 思路: 1. 先查询出Student,2. 根据Student的tid查询老师信息,--> <select id="getStudent" resultMap="ST"> select * from student </select> <resultMap id="ST" type="com.roy.pojo.Student"> <id property="id" column="id"/> <result property="name" column="name"/> <!-- 复杂对象需要单独处理,--> <association property="teacher" column="tid" javaType="com.roy.pojo.Teacher" select="getTeacher"/> </resultMap> <select id="getTeacher" resultType="com.roy.pojo.Teacher" parameterType="int"> select * from teacher where id = #{id} </select>
所以: 先查询Student, 结果集映射为ST, ST中,有复杂类型association, 实体属性和数据库字段名对应,类型设置为Teacher类,结果从getTeacher方法中查出。写getTeacher方法
<select id="getStudent2" resultMap="ST2"> select s.id sid, s.name sname, t.name tname from student s, teacher t where s.tid = t.id </select> <resultMap id="ST2" type="com.roy.pojo.Student"> <id property="id" column="sid"/> <result property="name" column="sname"/> <association property="teacher" javaType="com.roy.pojo.Teacher"> <result property="name" column="tname"/> </association> </resultMap>
前提:
数据库中:
Student表: id, name, tid(老师的id)
Teacher表: id, name
pojo类中:
Student: int id, String name, int tid;// 修改为一个学生对应一个老师的id
Teacher: int id, String name, List<Student> students
//一个老师有多个学生
从Teacher角度查询:
<select id="getATeacher" resultMap="TS" parameterType="int"> select t.id tid, t.name tname, s.id sid, s.name sname from teacher t, student s where t.id = s.tid and t.id = #{id} </select> <resultMap id="TS" type="com.roy.pojo.Teacher"> <id property="id" column="tid"/> <result property="name" column="tname"/> <!-- 对于List对象来说,使用ofType属性来描述迭代对象中的泛型 --> <!-- *************和association的区别,association中使用javaType--> <collection property="students" column="" ofType="com.roy.pojo.Student"> <result property="id" column="sid"/> <result property="name" column="sname"/> </collection> </resultMap> 输出结果为: Teacher(id=1, name=秦老师, students=[Student(id=1, name=小明, tid=0), Student(id=2, name=小红, tid=0), Student(id=3, name=小张, tid=0), Student(id=4, name=小李, tid=0), Student(id=5, name=小王, tid=0)])
<select id="getATeacher" resultMap="TS2" parameterType="int"> select * from teacher where id = #{id} </select> <resultMap id="TS2" type="com.roy.pojo.Teacher"> <id property="id" column="id"/> <result property="name" column="name"/> <!-- 这里给子查询传参数用coloum, javaType是list, ofType泛型类型--> <collection property="students" column="id" javaType="ArrayList" ofType="com.roy.pojo.Student" select="getStudent"> </collection> </resultMap> <select id="getStudent" parameterType="int" resultType="com.roy.pojo.Student"> select * from student where tid=#{id} </select>
根据不同的条件,生成不同的sql语句
类似于JSTL标签
准备:
产生随机的ID序号:
import java.util.UUID; public class RandomID { public static String returnRandomID(){ return UUID.randomUUID().toString().replace("-", "");//生成UUID类,转为String类型,把中间的-替换为没有 } }
mybatis-config.xml
文件中,<Setting标签添加 mapUnderscoreToCamelCase
为true, 可以自动让数据库中的字段名create_Column
转换pojo中的CreateColumn
从下划线转换为驼峰命名法,做映射。
表blog中: 属性为: id, title, author, create_time, views
常做的是根据where子句中的一部分条件判断,例如在where后面加不加一段and +条件
where 1=1是为了当后面的条件都没有的时候,保证不出错。
当题目和作者都没有时,查询所有的用户;当有匹配的时候,只能查询出对应的一条数据
(Mybatis的BlogMapper.xml文件中的方法不能重载,只能通过传入的map参数来判断)
Mybatis接口中的方法不能重载
<select id="queryBlogIF" parameterType="map" resultType="com.roy.pojo.Blog"> select * from blog where 1=1 <if test="title!= null">and title = #{title}</if> <if test="author!= null">and author =#{author}</if> </select>
或者为了没有条件时不报错,可以使用where标签包住if选择标签,
where标签:在语句开头为and or等词时,可以自动去除
<select id="queryBlogIF" parameterType="map" resultType="com.roy.pojo.Blog"> select * from blog <where> <if test="title!= null">title = #{title}</if> <if test="author!= null">and author =#{author}</if> </where> </select>
if 的另一个模式: switch, case, otherwise
动态SQL的标签:
<select id="queryBlogIF" parameterType="map" resultType="com.roy.pojo.Blog"> select * from blog <where> <choose> <when test="title != null">title = #{title}</when> <when test="author!= null">author = #{author}</when> <otherwise>1=1</otherwise> </choose> </where> </select>
动态更新语句:
set标签可以自动识别逗号添加与否,所以所有的set语句中都写逗号
<update id="updateBlog" parameterType="map"> update blog <set> <if test="title!=null">title = #{title}, //每一个子句后面都添加逗号,set标签自动识别加不加逗号</if> <if test="author!=null">author=#{author},</if> </set> where id = #{id} </update
(了解)where, set标签的父类都是trim标签,trim标签可以自定义需要添加的内容和需要检查是否略过的内容
如: where标签等价于:
<trim prefix="WHERE" prefixOverrides="AND | OR"></trim>
prefix标签表示需要插入到sql语句中的内容, prefixOverrides表示需要删除的字段
把重复使用的sql语句提取出来复用:
最好基于单表查询提取, 不要存在where和set这种标签
<sql id="title-author"> <if test="title!=null"> title = #{title}, </if> <if test="author!=null"> author=#{author}, </if> </sql> <!-- 在使用的地方用include引用--> <update id="updateBlog" parameterType="map"> update blog <set> <include refid="title-author"></include> </set> where id = #{id} </update>
要实现: select * from blog where id in (1,3)
函数中,传入的参数为map,方便可以传入其他的参数,所以select的parameterType为map;
map中有一个key为“mapKey”, 值为一个list的 元素,
foreach标签中,collection是map中的一个key,item是list中每一个元素的别名,用于在foreach标签中的id赋值
open是开始的拼接字符串,separator是分割的字符串,close是结束的字符串
<select id="queryBlogByForEach" parameterType="map" resultType="com.roy.pojo.Blog"> select * from blog where id in <foreach collection="mapKey" item="listElement" open="(" separator="," close=")"> #{listElement} </foreach> </select>
测试
@Test public void testForEach(){ SqlSession sqlSession = MybatisUtils.getSqlSession(); BlogMapper mapper = sqlSession.getMapper(BlogMapper.class); HashMap hashMap = new HashMap(); List<String> mapKey = new ArrayList<String>(); mapKey.add("58bca5c98c9e48c0ab3f0c18f1e52791"); mapKey.add("8619ef896d9b48ec836bde5fc8367da1"); hashMap.put("mapKey", mapKey); List<Blog> blogs = mapper.queryBlogByForEach(hashMap); for (Blog blog : blogs) { System.out.println(blog); } }
把查询出来的结果放到缓存中,下次查询相同的内容走缓存而不连接数据库。解决高并发问题
读写分离,主从复制
直接写入数据库,但读的时候在服务器和数据库中间加一个缓存服务器,直接从缓存服务器读。
当数据库多时,需要主从复制。
使用缓存:经常查询但是不常改变的数据,可以减少与数据库的交互次数,减少系统开销,提高效率
mybatis默认开启本地会话缓存,即一个sqlSession从获取到close之间生效。
config中开启日志,
测试类中查询相同的id两次,观察日志中数据库只连接了一次
缓存失效的情况:
一级缓存的作用域太低,所有出现二级缓存
如果在不同的UserMapper.xml中,则不能使用,不同的namespace有不同的缓存
<cache/>
标签即可。也可以添加相关的配置:
<cache eviction="FIFO | LRU | SOFT | WEAK" 清除策略:先进先出,最近最少使用,基于gc回收,更积极的垃圾回收 flushInterval="60000" 60秒回收 size = "512" 最大缓存数目 readOnly="true" 只读 />
二级缓存(全局缓存):
每个Mapper的标签存在相应的map里,一级缓存关闭时,自动存入二级缓存。所有的数据都会先放入一级缓存,当会话提交或者关闭的时候,才会被转存到二级缓存中,所以二级缓存需要系列化实体类
常见报错:
一二级缓存查询顺序:
可以用来自定义缓存策略。现在多用redis缓存