Mybatis入门
搭建环境
创建maven工程
Groupid:com.xuexi ArtifactId:mybatis01 Packing:jar
添加坐标
org.mybatis mybatis 3.4.5 junit junit 4.10 test mysql mysql-connector-java 5.1.6 runtime log4j log4j 1.2.12 编写User实体类
编写持久层接口IUserDao
public interface IUserDao { /** * 查询所有用户 * @return */ List
findAll(); } 编写resources中持久层接口的映射文件IUserDao.xml
要求:
- 创建位置:必须和持久层接口在相同的包中
名称:必须以持久层接口名称命名文件夹,扩展名是.xml
编写SqlMapConfig.xml配置文件
编写测试类
public class MybatisTest { public static void main(String[] args)throws Exception { //1.读取配置文件 InputStream in = Resources.getResourceAsStream("SqlMapConfig.xml"); //2.创建 SqlSessionFactory 的构建者对象 SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder(); //3.使用构建者创建工厂对象 SqlSessionFactory SqlSessionFactory factory = builder.build(in); //4.使用 SqlSessionFactory 生产 SqlSession 对象 SqlSession session = factory.openSession(); //5.使用 SqlSession 创建 dao 接口的代理对象 IUserDao userDao = session.getMapper(IUserDao.class); //6.使用代理对象执行查询所有方法 List
users = userDao.findAll(); for(User user : users) { System.out.println(user); } //7.释放资源 session.close(); in.close(); } }
基于注解的mybatis使用
在持久层接口中添加注解
public interface IUserDao { /** * 查询所有用户 * @return */ @Select("select * from user") List
findAll(); } 修改SqlMapConfig.xml
注意事项
在使用基于注解的Mybatis配置时,请移除xml的映射配置(IUserDao.xml)
基于代理Dao实现CRUD操作
使用要求:
持久层接口和持久层接口的映射配置必须在相同的包下
持久层映射配置中 mapper 标签的 namespace 属性取值必须是持久层接口的全限定类名
SQL 语句的配置标签
的 id 属性必须和持久层接口的方法名相同
根据ID查询
在持久层接口中添加findById方法
User findById(Integer userId);
在用户的映射配置文件中配置
说明:
- resultType属性:用于指定结果集的类型
- parameterType属性:用于指定传入参数的类型
- sql语句中使用
#{}
字符:代表占位符,相当于原来jdbc部分的?
,都是用于执行语句时替换实际的数据。具体的数据是由#{}
里面的内容决定的 #{}
中内容的写法:由于数据类型是基本类型,所以此处可以随意写
在测试类添加测试
public class MybastisCRUDTest { private InputStream in ; private SqlSessionFactory factory; private SqlSession session; private IUserDao userDao; @Test public void testFindOne() { //6.执行操作 User user = userDao.findById(41); System.out.println(user); } @Before//在测试方法执行之前执行 public void init()throws Exception { //1.读取配置文件 in = Resources.getResourceAsStream("SqlMapConfig.xml"); //2.创建构建者对象 SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder(); //3.创建 SqlSession 工厂对象 factory = builder.build(in); //4.创建 SqlSession 对象 session = factory.openSession(); //5.创建 Dao 的代理对象 userDao = session.getMapper(IUserDao.class); } @After//在测试方法执行完成之后执行 public void destroy() throws Exception{ session.commit(); //7.释放资源 session.close(); in.close(); } }
保存操作
在持久层接口中添加新增方法
int saveUser(User user);
在用户的映射配置文件中配置
insert into user(username,birthday,sex,address) values(#{username},#{birthday},#{sex},#{address}) 说明:
- parameterType属性:代表参数的类型,因为我们要传入的是一个类的对象,所以类型就写类的全名称
- sql语句中使用
#{}
字符:代表占位符,相当于原来jdbc部分的?
,都是用于执行语句时替换实际的数据。具体的数据是由#{}
里面的内容决定的 #{}
中内容的写法:由于保存方法的参数是一个User
对象,此处要写User
对象中的属性名称。使用的是ognl
表达式ognl
表达式:它是apache提供的一种表达式语言,全称是:Object Graphic Navigation Language
对象图导航语言。按照一定的语法格式来获取数据的,语法格式就是使用#{对象.对象}
的方式。#{user.username}
它会先去找 user 对象,然后在 user 对象中找到username
属性,并调用getUsername()
方法把值取出来。但是我们在parameterType
属性上指定了实体类名称,所以可以省略user.
而直接写username
。
添加测试方法
@Test public void testSave(){ User user = new User(); user.setUsername("modify User property"); user.setAddress("北京市顺义区"); user.setSex("男"); user.setBirthday(new Date()); System.out.println("保存操作之前: "+user); //5.执行保存方法 userDao.saveUser(user); System.out.println("保存操作之后: "+user); }
打开 Mysql 数据库发现并没有添加任何记录,原因是什么?
这一点和 jdbc 是一样的,我们在实现增删改时一定要去控制事务的提交,那么在 mybatis 中如何控制事务
提交呢?
可以使用:session.commit();
来实现事务提交。 加入事务提交后的代码如下:@After//在测试方法执行完成之后执行 public void destroy() throws Exception{ session.commit(); //7.释放资源 session.close(); in.close(); }
扩展:新增用户id的返回值
新增用户后,同时还要返回当前新增用户的id值,因为id是由数据库的自动增长来实现的,所以就相当于我们要在新增后将自动增长
auto_increment
的值返回select last_insert_id(); insert into user(username,birthday,sex,address) values(#{username},#{birthday},#{sex},#{address})
用户更新
在持久层接口中添加更新方法
int updateUser(User user);
在用户的映射配置文件中配置
update user set username=#{username},birthday=#{birthday},sex=#{sex}, address=#{address} where id=#{id} 添加测试方法
@Test public void testUpdateUser()throws Exception{ //1.根据 id 查询 User user = userDao.findById(52); //2.更新操作 user.setAddress("北京市顺义区"); int res = userDao.updateUser(user); System.out.println(res); }
用户删除
在持久层接口中添加删除方法
int deleteUser(Integer userId);
在用户的映射配置文件中配置
delete from user where id = #{uid} 添加测试方法
@Test public void testDeleteUser() throws Exception { //6.执行操作 int res = userDao.deleteUser(52); System.out.println(res); }
用户模糊查询
在持久层接口中添加模糊查询方法
List
findByName(String username); 在用户的映射配置文件中配置
添加测试方法
@Test public void testFindByName(){ //5.执行查询一个方法 List
users = userDao.findByName("%王%"); for(User user : users){ System.out.println(user); } 模糊查询的另一种配置方式
修改SQL语句的配置,如下
将原来的
#{}
占位符,改成了${value}
。注意如果用模糊查询的这种写法,那么${value}
的写法就是固定的,不能写成其它名字。测试
@Test public void testFindByName(){ //5.执行查询一个方法 List
users = userDao.findByName("王"); for(User user : users){ System.out.println(user); } }
#{}
与${}
的区别
通过#{}
表示一个占位符号#{}
可以实现preparedStatement
向占位符中设置值,自动进行 java 类型和 jdbc 类型转换,
#{}
可以有效防止 sql 注入。#{}
可以接收简单类型值或 pojo 属性值。 如果parameterType
传输单个简单类
型值,#{}
括号中可以是value
或其它名称。${}
表示拼接 sql 串通过
${}
可以将parameterType
传入的内容拼接在 sql 中且不进行 jdbc 类型转换,${}
可以接收简
单类型值或 pojo 属性值,如果parameterType
传输单个简单类型值,${}
括号中只能是value
。
查询使用聚合函数
在持久层接口中添加模糊查询方法
int findTotal();
在用户的映射配置文件中配置
添加测试方法
@Test public void testFindTotal() throws Exception { //6.执行操作 int res = userDao.findTotal(); System.out.println(res); }
Mybatis与JDBC编程的比较
数据库链接创建、释放频繁造成系统资源浪费从而影响系统性能,如果使用数据库链接池可解决此问题。
解决:
在 SqlMapConfig.xml 中配置数据链接池,使用连接池管理数据库链接。Sql 语句写在代码中造成代码不易维护,实际应用 sql 变化的可能较大, sql 变动需要改变 java 代码。
解决:
将 Sql 语句配置在 XXXXmapper.xml 文件中与 java 代码分离。向 sql 语句传参数麻烦,因为 sql 语句的 where 条件不一定,可能多也可能少,占位符需要和参数对应。
解决:
Mybatis 自动将 java 对象映射至 sql 语句,通过 statement 中的 parameterType 定义输入参数的
类型。对结果集解析麻烦, sql 变化导致解析代码变化,且解析前需要遍历,如果能将数据库记录封装成 pojo 对
象解析比较方便。解决:
Mybatis 自动将 sql 执行结果映射至 java 对象,通过 statement 中的 resultType 定义输出结果的
类型。
Mybatis的参数深入
parameterType配置参数
SQL 语句传参,使用标签的 parameterType 属性来设定。该属性的取值可以是基本类型,引用类型(例如:String 类型),还可以是实体类类型(POJO 类)。同时也可以使用实体类的包装类
注意
基本类型和String我们可以直接写类型名称,也可以使用包名.类名的方式,例如:
java.lang.String。实体类类型,目前我们只能使用全限定类名。
究其原因,是 mybaits 在加载时已经把常用的数据类型注册了别名,从而我们在使用时可以不写包名,
而我们的是实体类并没有注册别名,所以必须写全限定类名。参考 TypeAliasRegistery.class 的源码
传递 pojo 包装对象
开发中通过 pojo 传递查询条件 ,查询条件是综合的查询条件,不仅包括用户查询条件还包括其它的查询条件(比如将用户购买商品信息也作为查询条件),这时可以使用包装对象传递输入参数。
Pojo 类中包含 pojo。
需求:根据用户名查询用户信息,查询条件放到 QueryVo 的 user 属性中
编写 QueryVo
public class QueryVo implements Serializable { private User user; public User getUser() { return user; } public void setUser(User user) { this.user = user; } }
编写持久层接口
public interface IUserDao { /** * 根据 QueryVo 中的条件查询用户 * @param vo * @return */ List
findByVo(QueryVo vo); } 持久层接口的映射文件
测试包装类作为参数
@Test public void testFindByQueryVo() { QueryVo vo = new QueryVo(); User user = new User(); user.setUserName("%王%"); vo.setUser(user); List
users = userDao.findByVo(vo); for(User u : users) { System.out.println(u); } }
Mybatis的输出结果封装
resultType配置结果类型
resultType 属性可以指定结果集的类型,它支持基本类型和实体类类型。
需要注意的是,它和 parameterType 一样,如果注册过类型别名的,可以直接使用别名。没有注册过的必须
使用全限定类名。例如:我们的实体类此时必须是全限定类名(今天最后一个章节会讲解如何配置实体类的别名)同时,当是实体类名称是,还有一个要求,实体类中的属性名称必须和查询语句中的列名保持一致,否则无法实现封装
基本类型示例
Dao接口
/** * 查询总记录条数 * @return */ int findTotal();
映射配置
实体类类型示例
Dao接口
/** * 查询所有用户 * @return */ List
findAll(); 映射配置
特殊情况示例
实体类属性和数据库表的列名不一致
修改映射配置
使用别名查询
如果查询很多,都使用别名的话写起来很麻烦
resultMap结果类型
resultMap 标签可以建立查询的列名和实体类的属性名称不一致时建立对应关系。从而实现封装。
在 select 标签中使用 resultMap 属性指定引用即可。同时 resultMap 可以实现将查询结果映射为复杂类型的 pojo,比如在查询结果映射对象中包括 pojo 和 list 实现一对一查询和一对多查询
定义 resultMap
映射配置
SqlMapConfig.xml配置文件
配置内容
SqlMapConfig.xml中配置的内容和顺序
-properties(属性) --property -settings(全局配置参数) --setting -typeAliases(类型别名) --typeAliase --package -typeHandlers(类型处理器) -objectFactory(对象工厂) -plugins(插件) -environments(环境集合属性对象) --environment(环境子属性对象) ---transactionManager(事务管理) ---dataSource(数据源) -mappers(映射器) --mapper --package
properties(属性)
在使用 properties 标签配置时,我们可以采用两种方式指定属性配置。
第一种
第二种
在classpath下定义db.properties文件
jdbc.driver=com.mysql.jdbc.Driver jdbc.url=jdbc:mysql://localhost:3306/db_mybatis jdbc.username=root jdbc.password=mysql
properties标签配置
此时我们的dataSource标签就编程了引用上面的配置
typeAliases(类型别名)
采用自定义别名方式
自定义别名
在 SqlMapConfig.xml 中配置:
mappers(映射器)
使用相对于类路径的资源,如:
使用mapper接口类路径,如:
注意:此种方法要求mapper接口名称和mapper映射文件名称相同,且放在用一个目录中。
注册指定包下的所有mapper接口,如:
注意:此种方法要求mapper接口名称和mapper映射文件名称相同,且放在同一个目录中
Mybatis连接池与事务深入
Mybatis的连接池技术
SqlMapConfig.xml 配置文件中, 通过 来实现 Mybatis 中连接池的配置
Mybatis连接池的分类
Mybatis将数据源分为三类:
- UNPOOLED 不使用连接池的数据源
- POOLED 使用连接池的数据源
- JNDI 使用 JNDI 实现的数据源
Mybatis中数据源的配置
SqlMapConfig.xml
MyBatis 在初始化时, 根据 的 type 属性来创建相应类型的的数据源 DataSource,即:
type=”POOLED”: MyBatis 会创建 PooledDataSource 实例
type=”UNPOOLED” : MyBatis 会创建 UnpooledDataSource 实例
type=”JNDI”: MyBatis 会从 JNDI 服务上查找 DataSource 实例,然后返回使用
Mybatis中DataSource的存取
MyBatis是通过工厂模式来创建数据源DataSource对象的,MyBatis定义了抽象的工厂接口:
org.apache.ibatis.datasource.DataSourceFactory
,通过其getDataSource()
方法返回数据源DataSource
。Mybatis中连接的获取过程分析
当我们需要创建 SqlSession 对象并需要执行 SQL 语句时,这时候 MyBatis 才会去调用 dataSource 对象来创建
java.sql.Connection
对象。也就是说,java.sql.Connection
对象的创建一直延迟到执行SQL语句的时候。@Test public void testSql() throws Exception { InputStream in = Resources.getResourceAsStream("SqlMapConfig.xml"); SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in); SqlSession sqlSession = factory.openSession(); List
list = sqlSession.selectList("findUserById",41); System.out.println(list.size()); } 只有当第 4 句
sqlSession.selectList("findUserById")
,才会触发 MyBatis 在底层执行下面这个方法来创建java.sql.Connection
对象
Mybatis的事务控制
JDBC中的事务控制
在 JDBC 中我们可以通过手动方式将事务的提交改为手动方式,通过
setAutoCommit()
方法就可以调整。Mybatis中事务提交方式
Mybatis 中事务的提交方式,本质上就是调用 JDBC 的
setAutoCommit()
来实现事务控制。Mybatis 自动提交事务的设置
//创建 SqlSession 对象 session = factory.openSession(true);
Mybatis的动态SQL语句
标签
根据实体类的不同取值,使用不同的 SQL 语句来进行查询。比如在 id 如果不为空时可以根据 id 查询,如果 username 不同空时还要加入用户名作为条件。这种情况在多条件组合查询中经常会碰到。
持久层Dao接口
List
findByUser(User user); 持久层Dao映射配置
注意:
标签的 test 属性中写的是对象的属性名,如果是包装类的对象要使用 OGNL 表达式的写法。
另外要注意where 1=1
的作用测试
@Test public void testFindByUser() { User u = new User(); u.setUsername("%王%"); u.setAddress("%顺义%"); //6.执行操作 List
users = userDao.findByUser(u); for(User user : users) { System.out.println(user); } }
标签
为了简化上面 where 1=1 的条件拼装,我们可以采用 标签来简化开发
持久层Dao映射配置
标签需求
传入多个 id 查询用户信息,用下边两个 sql 实现:
SELECT * FROM USERS WHERE username LIKE '%张%' AND (id =10 OR id =89 OR id=16) SELECT * FROM USERS WHERE username LIKE '%张%' AND id IN (10,89,16)
这样在进行范围查询时,就要将一个集合中的值,作为参数动态添加进来。
在 QueryVo 中加入一个 List 集合用于封装参数
public class QueryVo implements Serializable { private List
ids; public List getIds() { return ids; } public void setIds(List ids) { this.ids = ids; } }
持久层接口
List
findInIds(QueryVo vo); 持久层Dao映射配置
SQL语句:
select 字段 from user where id in (?)
标签用于遍历集合,它的属性:collection:代表要遍历的集合元素,注意编写时不要写#{} open:代表语句的开始部分 close:代表结束部分 item:代表遍历集合的每个元素,生成的变量名 sperator:代表分隔符
Mybatis中简化编写的SQL片段
Sql 中可将重复的 sql 提取出来,使用时用 include 引用即可,最终达到 sql 重用的目的
定义代码片段
select * from user 引用代码片段
Mybatis多表查询之 一对多
本次案例主要以最为简单的用户和账户的模型来分析 Mybatis 多表关系。用户为 User 表,账户为Account表。一个用户(User)可以有多个账户(Account)
一对一查询(多对一)
需求
查询所有账户信息,关联查询下单用户信息。
注意:
因为一个账户信息只能供某个用户使用,所以从查询账户信息出发关联查询用户信息为一对一查询。如果从用户信息出发查询用户下的账户信息则为一对多查询,因为一个用户可以有多个账户
方式一
定义账户信息的实体类
public class Account implements Serializable { private Integer id; private Integer uid; private Double money; public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public Integer getUid() { return uid; } public void setUid(Integer uid) { this.uid = uid; } public Double getMoney() { return money; } public void setMoney(Double money) { this.money = money; } @Override public String toString() { return "Account [id=" + id + ", uid=" + uid + ", money=" + money + "]"; } }
编写sql语句
实现查询账户信息时,也要查询账户所对应的用户信息
SELECT account.*, user.username, user.address FROM account, user WHERE account.uid = user.id
定义 AccountUser 类
为了能够封装上面 SQL 语句的查询结果,定义 AccountCustomer 类中要包含账户信息同时还要包含用户信息,所以我们要在定义 AccountUser 类时可以继承 User 类。
public class AccountUser extends Account implements Serializable { private String username; private String address; public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getAddress() { return address; } public void setAddress(String address) { this.address = address; } @Override public String toString() { return super.toString() + " AccountUser [username=" + username + ", address=" + address + "]"; } }
定义账户的持久层Dao接口
List
findAll(); 定义 AccountDao.xml 文件中的查询配置信息
注意:因为上面查询的结果中包含了账户信息同时还包含了用户信息,所以我们的返回值类型 returnType 的值设置为 AccountUser 类型,这样就可以接收账户信息和用户信息了。
- 测试
小结
定义专门的 po 类作为输出类型,其中定义了 sql 查询结果集所有的字段。此方法较为简单,企业中使用普遍。
方式二
使用 resultMap,定义专门的 resultMap 用于映射一对一查询结果。
通过面向对象的(has a)关系可以得知,我们可以在 Account 类中加入一个 User 类的对象来代表这个账户是哪个用户的
修改Account类
在 Account 类中加入 User 类的对象作为 Account 类的一个属性。
修改 AccountDao 接口中的方法
List
findAll(); 注意:第二种方式,将返回值改 为了 Account 类型。因为 Account 类中包含了一个 User 类的对象,它可以封装账户所对应的用户信息
重新定义 AccountDao.xml 文件
测试
一对多查询
需求:
查询所有用户信息及用户关联的账户信息。
分析:
用户信息和他的账户信息为一对多关系,并且查询过程中如果用户没有账户信息,此时也要将用户信息查询出来,我们想到了左外连接查询比较合适
编写SQL语句
SELECT u.*, acc.id id, acc.uid, acc.money FROM user u LEFT JOIN account acc ON u.id = acc.uid
User 实体类加入
List
用户持久层 Dao 接口中加入查询方法
List
findAll(); 用户持久层 Dao 映射文件配置
测试
Mybatis多表查询之多对多
实现Role到User多对多
业务要求及实现 SQL
需求:
实现查询所有对象并且加载它所分配的用户信息。
分析:
查询角色我们需要用到Role表,但角色分配的用户的信息我们并不能直接找到用户信息,而是要通过中间表(USER_ROLE 表)才能关联到用户信息。
SQL
SELECT r.*,u.id uid, u.username username, u.birthday birthday, u.sex sex, u.address address FROM ROLE r INNER JOIN USER_ROLE ur ON ( r.id = ur.rid) INNER JOIN USER u ON (ur.uid = u.id);
编写角色实体类
public class Role implements Serializable { private Integer roleId; private String roleName; private String roleDesc; //多对多的关系映射:一个角色可以赋予多个用户 private List
users; public List getUsers() { return users; } public void setUsers(List users) { this.users = users; } public Integer getRoleId() { return roleId; } public void setRoleId(Integer roleId) { this.roleId = roleId; } public String getRoleName() { return roleName; } public void setRoleName(String roleName) { this.roleName = roleName; } public String getRoleDesc() { return roleDesc; } public void setRoleDesc(String roleDesc) { this.roleDesc = roleDesc; } @Override public String toString() { return "Role{" + "roleId=" + roleId + ", roleName='" + roleName + '\'' + ", roleDesc='" + roleDesc + '\'' + '}'; } } 编写Role持久层接口
List
findAll(); 编写映射文件
测试
实现User到Role的多对多
从 User 出发,我们也可以发现一个用户可以具有多个角色,这样用户到角色的关系也还是一对多关系。这样我们就可以认为 User 与 Role 的多对多关系,可以被拆解成两个一对多关系来实现。
Mybatis 延迟加载策略
何为延迟加载
延迟加载:
就是在需要用到数据时才进行加载,不需要用到数据时就不加载数据。延迟加载也称懒加载.好处: 先从单表查询,需要时再从关联表去关联查询,大大提高数据库性能,因为查询单表要比关联查询多张表速度要快。
坏处:
因为只有当需要用到数据时,才会进行数据库查询,这样在大批量数据查询时,因为查询工作也要消耗时间,所以可能造成用户等待时间变长,造成用户体验下降。
实现需求
查询账户(Account)信息并且关联查询用户(User)信息。如果先查询账户(Account)信息即可满足要求,当我们需要查询用户(User)信息时再查询用户(User)信息。把对用户(User)信息的按需去查询就是延迟加载。
使用 assocation 实现延迟加载
账户的持久层DAO接口
List
findAll(); 账户的持久层映射文件
用户的持久层接口和映射文件
public interface IUserDao { /** * 根据 id 查询 * @param userId * @return */ User findById(Integer userId); }
开启Mybatis的延迟加载策略
在 Mybatis 的配置文件 SqlMapConfig.xml 文件中添加延迟加载的配置。
使用 Collection 实现延迟加载
在一对多关系配置的
结点中配置延迟加载策略。
结点中也有 select 属性, column 属性。
在 User 实体类中加入
List
属性编写用户和账户持久层接口的方法
List
findByUid(Integer uid); 编写用户持久层映射配置
标签: 主要用于加载关联的集合对象 select 属性: 用于指定查询 account 列表的 sql 语句,所以填写的是该 sql 映射的 id column 属性: 用于指定 select 属性的 sql 语句的参数来源,上面的参数来自于 user 的 id 列,所以就写成 id 这一个字段名了 编写账户持久层映射配置
Mybatis缓存
Mybatis一级缓存
一级缓存是 SqlSession 级别的缓存,只要 SqlSession 没有 flush 或 close,它就存在。
一级缓存的分析
一级缓存是 SqlSession 范围的缓存,当调用 SqlSession 的修改,添加,删除, commit(), close()等方法时,就会清空一级缓存。
Mybatis二级缓存
二级缓存是 mapper 映射级别的缓存,多个 SqlSession 去操作同一个 Mapper 映射的 sql 语句,多个SqlSession 可以共用二级缓存,二级缓存是跨 SqlSession 的。
二级缓存的开启与关闭
在 SqlMapConfig.xml 文件开启二级缓存
因为 cacheEnabled 的取值默认就为 true,所以这一步可以省略不配置。为 true 代表开启二级缓存;为
false 代表不开启二级缓存。配置相关的 Mapper 映射文件
标签表示当前这个 mapper 映射将使用二级缓存,区分的标准就看 mapper 的 namespace 值。配置 statement 上面的 useCache 属性
将 UserDao.xml 映射文件中的
标签中设置 useCache=”true”代表当前这个 statement 要使用二级缓存,如果不使用二级缓存可以设置为 false。
注意: 针对每次查询都需要最新的数据 sql,要设置成 useCache=false,禁用二级缓存。
二级缓存注意事项
当我们在使用二级缓存时,所缓存的类一定要实现 java.io.Serializable 接口,这种就可以使用序列化方式来保存对象。
Mybatis 注解开发
mybatis 的常用注解说明
@Insert:实现新增
@Update:实现更新
@Delete:实现删除
@Select:实现查询
@Result:实现结果集封装
@Results:可以与@Result 一起使用,封装多个结果集
@ResultMap:实现引用@Results 定义的封装
@One:实现一对一结果集封装
@Many:实现一对多结果集封装
@SelectProvider: 实现动态 SQL 映射
@CacheNamespace:实现注解二级缓存的使用
使用 Mybatis 注解实现基本 CRUD
使用注解方式开发持久层接口
查询所有用户
@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") }) List
findAll(); 根据 id 查询一个用户
@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,before = false, statement = { "select last_insert_id()" }) 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);
查询使用聚合函数
@Select("select count(*) from user ") int findTotal();
模糊查询
@Select("select * from user where username like #{username} ") List
findByName(String name);
编写 SqlMapConfig 配置文件
使用注解实现复杂关系映射开发
实现复杂关系映射之前我们可以在映射文件中通过配置 来实现, 在使用注解开发时我们需要借助@Results 注解, @Result 注解, @One 注解, @Many 注解。
复杂关系映射的注解说明
@Results 注解 代替的是标签
该注解中可以使用单个@Result 注解,也可以使用@Result 集合 @Results({@Result(), @Result() })或@Results(@Result()) @Resutl 注解 代替了 标签和 标签 @Result 中 属性介绍: id 是否是主键字段 column 数据库的列名 property 需要装配的属性名 one 需要使用的@One 注解(@Result(one=@One)())) many 需要使用的@Many 注解(@Result(many=@many)())) @One 注解(一对一) 代替了 标签,是多表查询的关键,在注解中用来指定子查询返回单一对象。 @One 注解属性介绍: select 指定用来多表查询的 sqlmapper fetchType 会覆盖全局的配置参数 lazyLoadingEnabled。。 使用格式: @Result(column=" ",property="",one=@One(select="")) @Many 注解(多对一) 代替了 标签,是是多表查询的关键,在注解中用来指定子查询返回对象集合。 注意:聚集元素用来处理“一对多”的关系。需要指定映射的 Java 实体类的属性,属性的 javaType(一般为 ArrayList)但是注解中可以不定义; 使用格式: @Result(property="",column="",many=@Many(select="")) 使用注解实现一对一复杂关系映射及延迟加载
需求:
加载账户信息时并且加载该账户的用户信息,根据情况可实现延迟加载。(注解方式实现)
添加 User 实体类及 Account 实体类
public class User implements Serializable { private Integer userId; private String userName; private Date userBirthday; private String userSex; private String userAddress; } public class Account implements Serializable { private Integer id; private Integer uid; private Double money; //多对一关系映射:从表方应该包含一个主表方的对象引用 private User user; }
添加账户的持久层接口并使用注解配置
@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.xuexi.dao.IUserDao.findById", fetchType=FetchType.LAZY) ) }) List
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") }) List
findAll(); @Select("select * from user where id = #{uid} ") @ResultMap("userMap") User findById(Integer userId);
使用注解实现一对多复杂关系映射
需求:
查询用户信息时,也要查询他的账户列表。使用注解方式实现。
分析:
一个用户具有多个账户信息,所以形成了用户(User)与账户(Account)之间的一对多关系。
User 实体类加入
List
public class User implements Serializable { private Integer userId; private String userName; private Date userBirthday; private String userSex; private String userAddress; //一对多关系映射:主表方法应该包含一个从表方的集合引用 private List
accounts; } 编写用户的持久层接口并使用注解配置
@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", many=@Many( select="com.xuexi.dao.IAccountDao.findByUid", fetchType=FetchType.LAZY ) ) }) List
findAll(); 说明: @Many: 相当于 的配置 select 属性:代表将要执行的 sql 语句 fetchType 属性:代表加载方式,一般如果要延迟加载都设置为 LAZY 的值 编写账户的持久层接口并使用注解配置
@Select("select * from account where uid = #{uid} ") List
findByUid(Integer userId);
mybaits基于注解的二级缓存
在 SqlMapConfig 中开启二级缓存支持
在持久层接口中使用注解配置二级缓存
@CacheNamespace(blocking=true)//mybatis 基于注解方式实现配置二级缓存 public interface IUserDao {}