本篇主要记录java的mybatis框架应用实践。其余内容请阅读: [mybatis官方文档](https://mybatis.net.cn/#google_vignette)
MyBatis缓存 MyBatis缓存类别
一级缓存 证明一级缓存的存在 import com.jwang.dao.UserDao;import com.jwang.pojo.User;import org.apache.ibatis.io.Resources;import org.apache.ibatis.session.SqlSession;import org.apache.ibatis.session.SqlSessionFactory;import org.apache.ibatis.session.SqlSessionFactoryBuilder;import org.junit.After;import org.junit.Before;import org.junit.Test;import java.io.IOException;import java.io.InputStream;import java.util.Date;import java.util.List;public class TestMybatis { private UserDao userDao; private InputStream is; private SqlSession sqlSession; @Before public void init () throws IOException { SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder (); is = Resources.getResourceAsStream("SqlMapConfig.xml" ); SqlSessionFactory sessionFactory = sqlSessionFactoryBuilder.build(is); sqlSession = sessionFactory.openSession(); userDao = sqlSession.getMapper(UserDao.class); } @Test public void testFirstLevelCache () { List<User> userList = userDao.findAll(); for (User user : userList) { System.out.println(user); } System.out.println("---------------------------------------------------" ); UserDao userDao2 = sqlSession.getMapper(UserDao.class); List<User> userList2 = userDao2.findAll(); for (User user : userList2) { System.out.println(user); } } @After public void destroy () throws IOException { sqlSession.close(); is.close(); } }
一级缓存分析
第一次发起查询用户 id 为 1 的用户信息,先去找缓存中是否有 id 为 1 的用户信息,如果没有,从数据库查询用户信息。得到用户信息,将用户信息存储到一级缓存中。第二次发起查询用户 id 为 1 的用户信息,先去找缓存中是否有 id 为 1 的用户信息,缓存中有,直接从缓存中获取用户信息。
如果 sqlSession 去执行 commit操作(执行插入、更新、删除),清空 SqlSession 中的一级缓存,这样做的目的为了让缓存中存储的是最新的信息,避免脏读。
测试一级缓存清空
调用sqlSession的commit()或者clearCache()或者close()都能清除一级缓存
更新数据也会清空一级缓存
public class TestMybatis { @Test public void test01 () { SqlSession sqlSession = SqlSessionFactoryUtil.openSqlSession(); UserDao userDao = sqlSession.getMapper(UserDao.class); System.out.println(userDao.findAll()); userDao.deleteById(26 ); System.out.println("--------------------------------------" ); System.out.println(userDao.findAll()); sqlSession.commit(); } }
-二级缓存
二级缓存的使用
在 SqlMapConfig.xml 文件开启二级缓存
<settings > <setting name ="cacheEnabled" value ="true" /> </settings >
因为 cacheEnabled 的取值默认就为 true ,所以这一步可以省略不配置。为 true 代表开启二级缓存;为 false 代表不开启二级缓存。
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" > <mapper namespace ="com.jwang.dao.UserDao" > <cache /> <select id ="findAll" resultType ="User" > select * from t_user </select > <delete id ="deleteById" parameterType ="int" > delete from t_user where uid=#{id} </delete > </mapper >
要进行二级缓存的POJO类必须实现Serializable接口
测试 public class TestMybatis { @Test public void test02 () { SqlSession sqlSession1 = SqlSessionFactoryUtil.openSqlSession(); SqlSession sqlSession2 = SqlSessionFactoryUtil.openSqlSession(); SqlSession sqlSession3 = SqlSessionFactoryUtil.openSqlSession(); UserDao userDao1 = sqlSession1.getMapper(UserDao.class); UserDao userDao2 = sqlSession2.getMapper(UserDao.class); UserDao userDao3 = sqlSession3.getMapper(UserDao.class); System.out.println(userDao1.findAll()); sqlSession1.close(); System.out.println("----------------------" ); System.out.println(userDao2.findAll()); sqlSession2.close(); userDao3.deleteById(24 ); System.out.println("----------------------" ); System.out.println(userDao3.findAll()); sqlSession3.close(); } }
Mybatis 的多表关联查询 一(多)对一
CREATE TABLE t_account ( aid INT PRIMARY KEY auto_increment, money DOUBLE, uid INT ) ;ALTER TABLE t_account ADD FOREIGN KEY (uid) REFERENCES t_user (uid) ; INSERT INTO `t_account` VALUES (null , '1000' , '1' ); INSERT INTO `t_account` VALUES (null , '2000' , '1' ); INSERT INTO `t_account` VALUES (null , '1000' , '2' ); INSERT INTO `t_account` VALUES (null , '2000' , '2' ); INSERT INTO `t_account` VALUES (null , '800' , '3' );
select * from t_account a,t_user u where a.uid=u.uid AND aid=#{aid}
修改Account.java 在 Account 类中加入 User类的对象作为 Account 类的一个属性。
@Data @AllArgsConstructor @NoArgsConstructor public class Account implements Serializable { private Integer aid; private Double money; private Integer uid; private User user; }
public interface AccountDao { Account findAccountUserByAid (int aid) ; }
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" > <mapper namespace ="com.jwang.dao.AccountDao" > <resultMap id ="accountUserMap" type ="Account" autoMapping ="true" > <association property ="user" autoMapping ="true" javaType ="User" > </association > </resultMap > <select id ="findAccountUserByAid" parameterType ="int" resultMap ="accountUserMap" > select * from t_account a,t_user u where a.uid=u.uid AND a.aid=#{aid} </select > </mapper >
一对多
查询所有用户信息及用户关联的账户信息。
sql语句
select * from t_user u,t_account a where a.uid=u.uid AND u.uid=#{uid}
public class Account { private Integer aid; private Integer uid; private Double money; }
User.java 为了能够让查询的 User 信息中,带有他的个人多个账户信息,我们就需要在 User 类中添加一个集合,用于存放他的多个账户信息,这样他们之间的关联关系就保存了。
public class User implements Serializable { private int uid; private String username; private String sex; private Date birthday; private String address; private List<Account> accountList; }
public interface UserDao { User findUserAccountListByUid (int uid) ; }
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" > <mapper namespace ="com.dao.UserDao" > <resultMap id ="userAccountListMap" type ="User" > <id column ="uid" property ="uid" /> <result column ="username" property ="username" /> <result column ="address" property ="address" /> <result column ="sex" property ="sex" /> <result column ="birthday" property ="birthday" /> <collection property ="accountList" autoMapping ="true" ofType ="Account" > </collection > </resultMap > <select id ="findUserAccountListByUid" resultMap ="userAccountListMap" parameterType ="int" > SELECT * FROM t_user u,t_account a WHERE a.uid=u.uid AND u.uid=#{uid} </select > </mapper >
多对多(可以看成俩一对多)
实现查询所有角色对象并且加载它所分配的用户信息。
建表语句
CREATE TABLE t_role( rid INT PRIMARY KEY AUTO_INCREMENT, rName varchar (40 ), rDesc varchar (40 ) ); INSERT INTO `t_role` VALUES (null , '校长' , '负责学校管理工作' );INSERT INTO `t_role` VALUES (null , '副校长' , '协助校长负责学校管理' );INSERT INTO `t_role` VALUES (null , '班主任' , '负责班级管理工作' );INSERT INTO `t_role` VALUES (null , '教务处主任' , '负责教学管理' );INSERT INTO `t_role` VALUES (null , '班主任组长' , '负责班主任小组管理' );CREATE TABLE user_role( uid INT , rid INT ); ALTER TABLE user_role ADD FOREIGN KEY(uid) REFERENCES t_user(uid);ALTER TABLE user_role ADD FOREIGN KEY(rid) REFERENCES t_role(rid);INSERT INTO `user_role` VALUES ('1' , '1' );INSERT INTO `user_role` VALUES ('3' , '3' );INSERT INTO `user_role` VALUES ('2' , '3' );INSERT INTO `user_role` VALUES ('2' , '5' );INSERT INTO `user_role` VALUES ('3' , '4' );
查询角色我们需要用到 Role 表,但角色分配的用户的信息我们并不能直接找到用户信息,而是要通过中间表(USER_ROLE 表)才能关联到用户信息。 下面是实现的 SQL 语句:
select * from t_user u,t_role r,user_role ur where ur.uid= u.uid and ur.rid= r.rid AND u.uid= #{uid}
@Data @AllArgsConstructor @NoArgsConstructor public class User implements Serializable { private Integer uid; private String username; private String sex; private Date birthday; private String address; private List<Role> roleList; }
public class Role { private Integer rid; private String rName; private String rDesc; }
public interface UserDao { User findUserRoleListByUid (int uid) ; }
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" > <mapper namespace ="com.dao.UserDao" > <resultMap id ="userRoleListMap" type ="User" > <id column ="uid" property ="uid" /> <result column ="username" property ="username" /> <result column ="address" property ="address" /> <result column ="sex" property ="sex" /> <result column ="birthday" property ="birthday" /> <collection property ="roleList" autoMapping ="true" ofType ="Role" > </collection > </resultMap > <select id ="findUserRoleListByUid" parameterType ="int" resultMap ="userRoleListMap" > SELECT * FROM t_user u,t_role r,user_role ur WHERE ur.uid=u.uid AND ur.rid=r.rid AND u.uid=#{uid} </select > </mapper >
以哪张表作为主体查询,那么就将查询到的结果封装到哪张表对应的POJO对象中
如果表的关系是一对一,那么就在一个POJO中添加另外一个POJO的对象属性
如果表的关系是一对多,那么就在一个POJO中添加另外一个POJO的集合属性
使用association标签可以进行一对一的映射
使用collection标签可以进行一对多的映射
Mybatis 延迟加载策略 什么是延迟加载 延迟加载:就是在需要用到数据时才进行加载,不需要用到数据时就不加载数据。延迟加载也称懒加载.
坏处: 执行查询的次数会增加,所以在执行批量查询的时候,查询次数比使用连接查询要多特别多
好处: 先从单表查询,需要时再从关联表去关联查询,大大提高数据库性能,因为查询单表要比关联查询多张表速度要快.
懒加载的配置
<settings > <setting name ="lazyLoadingEnabled" value ="true" /> <setting name ="aggressiveLazyLoading" value ="false" /> </settings >
使用 Assocation 实现延迟加载 (多对一,一对一)
-- 1. 查询账户 select * from t_account where aid=#{aid} -- 2, 再查询用户 -- 再根据查询结果里面的uid查询当前账户所属的用户 SELECT * FROM t_user WHERE uid = #{uid}
public class User { private int uid; private String username; private String sex; private Date birthday; private String address; }
public class Account { private Integer aid; private Integer uid; private Double money; private User user; }
public interface AccountDao { Account findAccountByAid (int aid) ; }
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" > <mapper namespace ="com.dao.AccountDao" > <resultMap id ="accountUserMap" type ="Account" autoMapping ="true" > <id column ="uid" property ="uid" /> <association property ="user" autoMapping ="true" select ="com.dao.UserDao.findUserByUid" column ="uid" > </association > </resultMap > <select id ="findAccountByAid" parameterType ="int" resultMap ="accountUserMap" > select * from t_account where aid=#{aid} </select > </mapper >
public interface UserDao { User findUserByUid (int uid) ; }
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" > <mapper namespace ="com.dao.UserDao" > <select id ="findUserByUid" parameterType ="int" resultType ="User" > select * from t_user where uid=#{uid} </select > </mapper >
public class TestMybatis { @Test public void test01 () { SqlSession sqlSession = SqlSessionFactoryUtil.openSqlSession(); AccountDao accountDao = sqlSession.getMapper(AccountDao.class); Account account = accountDao.findAccountByAid(1 ); System.out.println(account.getMoney()); SqlSessionFactoryUtil.commitAndClose(sqlSession); } }
Collection 实现延迟加载 (一对多,多对多)
完成加载用户对象时,查询该用户所拥有的账户信息。等账户信息使用的时候再查询.
SELECT * FROM t_user where uid= #{uid}SELECT * FROM t_account WHERE uid = #{uid}
public class Account { private Integer aid; private Integer uid; private Double money; }
public class User implements Serializable { private int uid; private String username; private String sex; private Date birthday; private String address; private List<Account> accounts; }
public interface UserDao { User findUserByUid (int uid) ; }
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" > <mapper namespace ="com.dao.UserDao" > <resultMap id ="userAccountListMap" type ="User" autoMapping ="true" > <id column ="uid" property ="uid" /> <collection property ="accountList" autoMapping ="true" select ="com.dao.AccountDao.findAccountListByUid" column ="uid" > </collection > </resultMap > <select id ="findUserByUid" parameterType ="int" resultMap ="userAccountListMap" > select * from t_user where uid=#{uid} </select > </mapper >
public interface AccountDao { List<Account> findAccountListByUid (int uid) ; }
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" > <mapper namespace ="com.dao.AccountDao" > <select id ="findAccountListByUid" parameterType ="int" resultType ="Account" > select * from t_account where uid=#{uid} </select > </mapper >
public class TestMybatis { @Test public void test01 () { SqlSession sqlSession = SqlSessionFactoryUtil.openSqlSession(); UserDao userDao = sqlSession.getMapper(UserDao.class); User user = userDao.findUserByUid(1 ); System.out.println(user.getUsername()); SqlSessionFactoryUtil.commitAndClose(sqlSession); } }
MyBatis注解开发 使用 Mybatis 注解实现基本CRUD
public interface UserDao { @Insert("insert into t_user (username,address,sex,birthday) values (#{username},#{address},#{sex},#{birthday})") @SelectKey(keyProperty = "uid",keyColumn = "uid",resultType = int.class,before = false,statement = "select last_insert_id()") int addUser (User user) ; @Delete("delete from t_user where uid=#{id}") int deleteById (int id) ; @Update("update t_user set username=#{username},sex=#{sex},address=#{address} where uid=#{uid}") void updateUser (User user) ; @Select("select * from t_user where uid=#{id}") User findById (int id) ; }
使用Mybatis注解实现复杂关系映射开发 复杂关系映射的注解说明
@Results({@Result(), @Result()}) 或@Results(@Result())
@Result(column="列名",property="属性名",one=@One(select="指定用来多表查询的 sqlmapper"),many=@Many(select="")) @Resutl 注解属性说明 column 数据库的列名 Property 需要装配的属性名
@Data public class UserInfo implements Serializable { private Integer userId; private String username; private String userSex; private String userAddress; private Date userBirthday; }
@Results(id = "userInfoMap",value = { @Result(column = "uid",property = "userId",id = true),//id为true表示该字段是主键 @Result(column = "sex",property = "userSex"), @Result(column = "address",property = "userAddress"), @Result(column = "birthday",property = "userBirthday") }) List<UserInfo> findAllUserInfos () ;
@One 注解(一对一),代替了标签,是多表查询的关键,在注解中用来指定子查询返回单一对象。
@Result(column="列名",property="属性名",one=@One(select="指定用来多表查询的 sqlmapper"))
@Many 注解(一对多) ,代替了标签,是是多表查询的关键,在注解中用来指定子查询返回对象集合
注意:聚集元素用来处理“一对多”的关系。需要指定映射的 Java 实体类的属性,属性的 javaType(一般 为 ArrayList) 但是注解中可以不定义;
@Result(property="",column="",many=@Many(select=""))
使用注解实现一对一复杂关系映射及延迟加载
查询账户(Account)信息并且关联查询用户(User)信息。先查询账户(Account)信息,当我们需要用到用户(User)信息时再查询用户(User)信息。
public class User implements Serializable { private int uid; private String username; private String sex; private Date birthday; private String address; }
public class Account { private Integer aid; private Integer uid; private Double money; private User user; }
public interface AccountDao { @Select("select * from t_account where aid=#{aid}") @Results( { //映射主键 @Result(column = "uid",property = "uid",id = true), //调用第二步查询进行一对一映射 @Result(property = "user",column = "uid",one = @One(select = "com.jwang.dao.UserDao.findUserByUid")) } ) Account findAccountByAid (int aid) ; }
public interface UserDao { @Select("select * from t_user where uid=#{uid}") User findUserByUid (int uid) ; }
public class TestMybatis { @Test public void test01 () { SqlSession sqlSession = SqlSessionFactoryUtil.openSqlSession(); AccountDao accountDao = sqlSession.getMapper(AccountDao.class); Account account = accountDao.findAccountByAid(1 ); System.out.println(account); SqlSessionFactoryUtil.commitAndClose(sqlSession); } }
使用注解实现一对多复杂关系映射及延迟加载
完成加载用户对象时,查询该用户所拥有的账户信息。 等账户信息使用的时候再查询.
public class User { private Integer uid; private String username; private String sex; private Date birthday; private String address; private List<Account> accounts; }
public interface UserDao { @Select("select * from t_user where uid=#{uid}") @Results({ @Result(id = true,column = "uid",property = "uid"), @Result(property = "accountList",column = "uid",many = @Many(select = "com.jwang.dao.AccountDao.findAccountListByUid")) }) User findUserByUid (int uid) ; }
public interface AccountDao { @Select("select * from t_account where uid=#{uid}") List<Account> findAccountListByUid (int uid) ; }
spring整合mybatis
引入依赖
<properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <maven.compiler.source>1.8</maven.compiler.source> <maven.compiler.target>1.8</maven.compiler.target> <!--spring--> <spring.version>5.0.2.RELEASE</spring.version> <!--日志打印框架--> <slf4j.version>1.6.6</slf4j.version> <log4j.version>1.2.12</log4j.version> <!--mysql--> <mysql.version>5.1.6</mysql.version> <!--mybatis--> <mybatis.version>3.5.3</mybatis.version> </properties> <dependencies> <!--引入依赖--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>${spring.version}</version> </dependency> <!--mysql依赖--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>${mysql.version}</version> </dependency> <!-- log start --> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>${log4j.version}</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>${slf4j.version}</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j12</artifactId> <version>${slf4j.version}</version> </dependency> <!-- log end --> <!--mybatis的依赖--> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>${mybatis.version}</version> </dependency> <!-- mybatis整合spring的依赖 --> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis-spring</artifactId> <version>1.3.0</version> </dependency> <!--lombok--> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.10</version> </dependency> <!--junit--> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> <scope>test</scope> </dependency> </dependencies>
spring.xml文件配置
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <!--1. 包扫描--> <context:component-scan base-package="com.jwang"/> <!-- 2. spring整合mybatis --> <!-- 1. 配置jdbc的环境: 1.1 配置dataSource(既可以使用spring内置的DataSource,又可以使用第三方的DataSource) 1.2 配置事务管理者(现在不做),后面学习声明式事务的时候再讲 2. 将SqlSessionFactoryBean 对象进行IOC配置到spring的核心容器中,并且将dataSource注入进去 3. 扫描dao包,创建出dao的代理对象,交给spring的核心容器管理 各种dao代理对象的id,就是接口名首字母改小写 --> <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="username" value="root"></property> <property name="password" value="123"></property> <property name="url" value="jdbc:mysql:///day20?characterEncoding=utf8"></property> <property name="driverClassName" value="com.mysql.jdbc.Driver"></property> </bean> <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource" ref="dataSource"></property> <!--加载mybatis的主配置文件--> <!--<property name="configLocation" value="classpath:SqlMapConfig.xml"></property>--> <!--别名配置的包扫描--> <property name="typeAliasesPackage" value="com.jwang.pojo"></property> </bean> <bean id="scannerConfigurer" class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <property name="basePackage" value="com.jwang.dao"></property> </bean> </beans>