目录
一、mybatis注解开发
1、搭建注解开发的Mybatis环境
1、插入
2、删除
3、更新
4、查询
5、自增主键回填
6、结果集映射(@Results实现映射)
二、mybatis基于注解实现动态SQL
1、动态sql(脚本标签)@ResultMap指定映射
2、动态SQL @SelectProvider
三、注解开发 延迟加载
四、Mybatis缓存
1、一级缓存
1.1 测试一级缓存
1.2 一级缓存失效
2、二级缓存
1.1 二级缓存原理
1.2 二级缓存条件
1.3 测试二级缓存
mybatis的SQL映射文件可以使用xml的方式配置,但不同的用户模块接口都对应一个映射文件,并且在映射文件中书写sql语句很麻烦。所以Mybatis为用户提供了快速的开发方式,基于注解(Annnotation)的配置方式。
@Insert:保存
Value:sql语句(和xml的配置方式一模一样)
@Update:更新
Value:sql语句
@Delete: 删除
Value:sql语句
@Select: 查询
Value:sql语句
@Options:可选配置(获取主键)
userGeneratedKeys:开关,值为true表示可以获取主键 相当于select last_insert_id()
keyProperty :对象属性
keyColumn : 列名
将mybatis全局配置文件mybatis-config.xml中的mapper路径改为包扫描或者class路径
说明:因为没有了映射文件,所以我们这里采用加载接口方式,需要告知mybatis哪个接口的方法上的注解需要被执行。
public interface UserMapper {
@Insert("insert into tb_user values(null,#{userName},#{password},#{name},#{age},#{sex})")
Integer addUser(User user);
}
定义接口后,编写测试
//myBatis注解开发@insert
@Test
public void t1() {
final SqlSession session = SessionFactoryUtils.getSession();
final UserMapper mapper = session.getMapper(UserMapper.class);
final User2 user2 = new User2(null,null,"buzz","666","巴斯光年",18,1);
final Integer integer = mapper.addUser(user2);
System.out.println("添加用户 = " + integer);
session.close();
}
在根据id删除数据的方法上面编写注解@Delete
@Delete("delete from tb_user where id = #{id}")
Integer deleteUser(@Param("id") Integer id);
//myBatis注解开发@delete
@Test
public void t2() {
final SqlSession session = SessionFactoryUtils.getSession();
final UserMapper mapper = session.getMapper(UserMapper.class);
final Integer integer = mapper.deleteUser(13);
System.out.println("添加用户 = " + integer);
session.commit();
session.close();
}
//在根据id修改用户数据方法上面添加注解@Update,然后在其value属性值中编写具体的SQL
@Update("update tb_user set user_name=#{userName},password=#{password},name=#{name},age=#{age},sex=#{sex} where id=#{id}")
Integer updateUser(User2 user2);
@Test
public void t3() {
final SqlSession session = SessionFactoryUtils.getSession();
final UserMapper mapper = session.getMapper(UserMapper.class);
final User2 user2 = new User2(null,14L,"buzzLightYear","666","巴斯光年飞向太空",18,1);
final Integer integer = mapper.updateUser(user2);
System.out.println("integer = " + integer);
session.commit();
session.close();
}
若不开启驼峰映射,有些值拿不到。
//查询所有用户
@Select("select * from tb_user")
List findAll();
//myBatis注解开发@select
@Test
public void t4() {
final SqlSession session = SessionFactoryUtils.getSession();
final UserMapper mapper = session.getMapper(UserMapper.class);
final List all = mapper.findAll();
System.out.println("all = " + all);
session.commit();
session.close();
}
自增主键回填(了解):使用注解完成数据新增,新增成功后返回数据的主键id值
@Insert("insert into tb_user values(null,#{userName},#{password},#{name},#{age},#{sex})")
@Options(useGeneratedKeys = true,keyColumn = "id" ,keyProperty = "id")
Integer addUserAndGetFk(User2 user);
//myBatis注解开发:自增主键回填
@Test
public void t5() {
final SqlSession session = SessionFactoryUtils.getSession();
final UserMapper mapper = session.getMapper(UserMapper.class);
final User2 user2 = new User2(null,null,"wudy","666","胡迪",18,1);
final Integer integer = mapper.addUserAndGetFk(user2);
System.out.println("integer = " + integer);
//此时已经拿到返回的id
System.out.println(user2.getId());
session.commit();
session.close();
}
根据之前的学习,如果数据表的列名和pojo实体类的属性名不一致,会导致数据表的数据无法封装到实体类属性值中,对此我们有如下解决方案:
使用注解:@Results实现映射(等价于
目标:使用注解的方式给取别名后的字段,映射到实体类中,并查询所有用户信息;
注意:为方便演示效果,可将之前核心配置文件中的开启驼峰自动映射设置为false。
注意:此处 @Results的id="userMap2",id名不要冲突。
@Select("select * from tb_user where id=#{id}")
@Results(id="userMap2",value = {
@Result(column = "id",property = "id",id=true),
@Result(column = "user_name",property = "userName")})
User2 findById(Long id);
//myBatis注解开发:自增主键回填
@Test
public void t6() {
final SqlSession session = SessionFactoryUtils.getSession();
final UserMapper mapper = session.getMapper(UserMapper.class);
final User2 byId = mapper.findById(1L);
System.out.println("byId = " + byId);
session.commit();
session.close();
}
查询男性用户,如果输入了用户名,按用户名模糊查询,如果没有输入用户名,就查询所有男性用户:
在接口上添加: 标签,标签体中编写sql语句,sql语句与之前XML配置文件中的一致即可;这种方式在写法上面和 XML 中的写法是一样,支持 XML 的动态SQL语法,可以在上面的字符串中写
注意:接口注解中要指定mapper.xml的映射id@ResultMap(value = "userAndOrderMap")
@Select("")
@ResultMap(value = "userAndOrderMap")
List findUserByName(@Param("name") String name);
//myBatis注解开发:脚本标签:查询男性用户,如果输入了用户名,按用户名模糊查询,如果没有输入用户名,就查询所有男性用户
@Test
public void t7() {
final SqlSession session = SessionFactoryUtils.getSession();
final UserMapper mapper = session.getMapper(UserMapper.class);
final List userByName = mapper.findUserByName("%zhang%");
System.out.println("userByName = " + userByName);
session.commit();
session.close();
}
查询男性用户,如果输入了用户名,按用户名模糊查询,如果没有输入用户名,就查询所有男性用户:
使用 @SelectProvider 注解,注解中的type 参数是提供构建 SQL 的类,method 是构建 SQL 的方法。 构建 SQL 的方法的参数要和接口的参数一致,并且多个参数要使用@Param命名参数。
@SelectProvider(type = SqlProvider.class,method = "findUserByName")
@ResultMap(value = "userAndOrderMap")
List findUserByName2(@Param("name") String name);
sql类:
public class SqlProvider {
public String findUserByName(@Param("name") String name){
String sql="select * from tb_user where sex=1";
if(name!=null){
sql+=" and user_name like #{name}";
}
return sql;
}
}
//myBatis注解开发:SelectProvider查询男性用户,如果输入了用户名,按用户名模糊查询,如果没有输入用户名,就查询所有男性用户
@Test
public void t8() {
final SqlSession session = SessionFactoryUtils.getSession();
final UserMapper mapper = session.getMapper(UserMapper.class);
final List userByName = mapper.findUserByName2("%zhang%");
System.out.println("userByName = " + userByName);
session.commit();
session.close();
}
略
1.概念:一级缓存属于本地缓存,SqlSession级别的;
2.原理:
1)在同一个SqlSession,发生了一次查询,查询的结果会存入一级缓存;
2)第二次再发生相同查询时,直接从缓存中获取数据,不再与数据库建立连接查询数据;
3.注意:一级缓存,默认是开启的,无法关闭。
完成根据id查询用户数据:
测试步骤:用同一个session根据id查询用户2次,然后查看日志打印sql的情况进行判断判断;
1)在相同的sqlsession下调用mapper接口执行2次查询,第二次数据从一级缓存中获取;
@Test
public void t8() {
final SqlSession session = SessionFactoryUtils.getSession();
final UserMapper mapper = session.getMapper(UserMapper.class);
final List userByName = mapper.findUserByName2("%zhang%");
System.out.println("userByName = " + userByName);
final List userByName2 = mapper.findUserByName2("%zhang%");
System.out.println("userByName2 = " + userByName2);
session.commit();
session.close();
}
仅执行一遍。
2)开启两个session,调用相同的方法,查看sql执行次数;
public class TestAll2 {
private static SqlSession session1;
private static SqlSession session2;
static {
InputStream in = null;
try {
in = Resources.getResourceAsStream("mybatisZhuJie.xml");
} catch (IOException e) {
e.printStackTrace();
}
SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(in);
session1=sessionFactory.openSession();
session2=sessionFactory.openSession();
}
@Test
public void test1(){
UserMapper mapper1 = session1.getMapper(UserMapper.class);
UserMapper mapper2 = session2.getMapper(UserMapper.class);
final User2 byUserId = mapper1.findByUserId(1L);
System.out.println("byUserId = " + byUserId);
final User2 userById = mapper2.findByUserId(1L);
System.out.println("userById = " + userById);
}
}
说明:一级缓存是session级别,sessin与session之间不能共享缓存;
1)查询条件不一致
说明:相同的session下可根据id=1和id=2去查询,发现需要查询2次;
@Test
public void test3(){
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
User user = userMapper.findById(1l);
System.out.println(user);
//说明:两次查询,条件不一样,不会被缓存名称,此时应该打印两次sql
User user2 = userMapper.findById(2l);
System.out.println(user2);
}
2)sqlSession不同
说明:获取两个sesion,然后在两个sesion中获取相同类型的动态代理对象,进行测试;
3)两次查询期间存在增删改操作
说明:在执行update、insert、delete的时候,即使操作的不是和一级缓存中的是同一条记录,都会清空一级缓存。
@Test
public void test15(){
UserMapper userMapper = MybatisUtil.getMapper(UserMapper.class);
//1.根据用户ID查询用户信息 一级缓存中没有数据,那么就会去数据库查询(发送sql)
User user = userMapper.findById(1L);
//2.当当前session发生增删改操作,就会清除一级缓存
userMapper.deleteByUserId(13);
//3.此时因为一级缓存中数据被清空了,所以需要从数据库再次查询
User user2 = userMapper.findById(1L);
System.out.println(user);
System.out.println(user2);
}
4)手动清除一级缓存
使用:sqlSession.clearCache();可以强制清除缓存;
@Test
public void test6(){
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
//共打印2次sql
User user = userMapper.findById(12l);
System.out.println(user);
//清空当前session下的一级缓存
sqlSession.clearCache();
//此时需要再次去数据库查询
User user2 = userMapper.findById(12l);
System.out.println(user2);
}
概念:全局缓存 ,namespace级别;
mybatis 的二级缓存的作用域:mapper范围的(即映射文件级别的,多个sqlSession可以共享二级缓存数据);
1)手动开启
1.全局配置中: 默认已开启。
映射文件中:
2.注解方式:或者在接口上添加注解 @CacheNamespace
2)第一个session必须关闭
说明:由于缓存数据是在sqlSession调用close方法时,放入二级缓存的,因此在测试二级缓存时必须先将第一个sqlSession关闭;
3)二级缓存的对象必须序列化,如:User对象必须实现Serializable接口。
说明:因为二级缓存的原理就是将对象进行序列化。
开启二级缓存方式:
测试思路:session1获取mapper进行查询,然后关闭session1,session2获取mapper相同条件二次查询;
1)在核心配置文件下开启二级缓存
2)接口下标注开启namespace二级缓存
@CacheNamespace(blocking = true)
public interface UserDao {
.....
}
@Test
public void t8() {
final SqlSession session = SessionFactoryUtils.getSession();
final UserMapper mapper = session.getMapper(UserMapper.class);
final List userByName = mapper.findUserByName2("%zhang%");
System.out.println("userByName = " + userByName);
session.close();
final SqlSession session2 = SessionFactoryUtils.getSession();
final UserMapper mapper2 = session2.getMapper(UserMapper.class);
final List userByName2 = mapper2.findUserByName2("%zhang%");
System.out.println("userByName2 = " + userByName2);
session2.close();
}