Java框架 - Mybatis
1. Mybatis概述
mybatis是一个持久层框架,用java编写的。
它封装了jdbc操作的很多细节,使开发者只需要关注sql语句本身,而无需关注注册驱动,创建连接等繁杂过程。
它使用了ORM思想实现了结果集的封装。
ORM:
Object Relational Mapping 对象关系映射
简单的说:
就是把数据库表和实体类及实体类的属性对应起来
让我们可以操作实体类就实现操作数据库表。
user User
user_id userId
user_name userName
今天我们需要做到
实体类中的属性和数据库表的字段名称保持一致。
user user
user_id user_id
user_name user_name
2. Mybatis的环境搭建及入门案例
(1)创建maven工程并导入坐标(在pom.xml中)
(2)创建实体类和dao的接口
(3)创建Mybatis的主配置文件
SqlMapConfig.xml
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"> <!-- mybatis的主配置文件 --> <configuration> <!-- 配置环境 --> <environments default="mysql"> <!-- 配置mysql的环境--> <environment id="mysql"> <!-- 配置事务的类型--> <transactionManager type="JDBC"></transactionManager> <!-- 配置数据源(连接池) --> <dataSource type="POOLED"> <!-- 配置连接数据库的4个基本信息 --> <property name="driver" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/eesy_mybatis"/> <property name="username" value="root"/> <property name="password" value="woaini1314"/> </dataSource> </environment> </environments> <!-- 指定映射配置文件的位置,映射配置文件指的是每个dao独立的配置文件 --> <mappers> <mapper resource="com/itheima/dao/IUserDao.xml"/> </mappers> </configuration>
(4)创建映射配置文件
IUserdao.xml
<?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.itheima.dao.IUserDao"> <!-- namespace是dao包的全限定类名--> <!--配置查询所有--> <select id="findAll" resultType="com.itheima.domain.User"> <!-- id 是dao中的方法名称 resultType是返回值类型 既将返回值封装成User并且放到list集合中--> select * from user </select> </mapper>
* 环境搭建的注意事项:
第一个:创建IUserDao.xml 和 IUserDao.java时名称是为了和我们之前的知识保存一致
在mybatis中它把持久层的操作接口名称和映射文件也叫做:Mapper
所以:IUserDao 和 IUserMapper是一样的 以后可能会遇到别人叫做mapper
第二个:在idea中创建目录的时候,它和包是不一样的
包在创建时:com.itheima.dao是三级结构
目录在创建时:com.itheima.dao是一级目录
第三个:mybatis的映射配置文件必须和dao接口的包结构相同
第四个:映射配置文件的mapper标签namesapce属性的取值必须是dao接口的全限定类名
第五个:映射配置文件的操作配置(select,insert等),id属性的取值必须是dao接口的方法名
当我们遵从了第三、四、五点之后,我们在开发中就无须再写dao的实现
第一步:读取配置文件
第二步:创建SqlSessionFactory工厂
第三步:创建SqlSession
第四步:创建Dao接口的代理对象
第五步:执行dao中的方法
第六步:释放资源
注意事项:
不要忘记在映射配置中告知mybatis要封装到那个实体类
配置的方式:指定实体类的全限定类名
package com.itheima.test; import com.itheima.dao.IUserDao; import com.itheima.domain.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 java.io.InputStream; import java.util.List; /** * @author 黑马程序员 * @Company * mybatis的入门案例 */ public class MybatisTest { /** * 入门案例 * @param args */ public static void main(String[] args)throws Exception { // 1. 读取配置文件 InputStream in = Resources.getResourceAsStream("SqlMapConfig.xml"); // 2. 创建SqlSessionFactory工厂 SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder(); SqlSessionFactory factory = builder.build(in); // 工厂会封装 解析配置文件 // 3. 使用工厂生产SqlSession对象 SqlSession session = factory.openSession(); // 4. 使用SqlSession创建Dao接口的代理对象 IUserDao userDao = session.getMapper(IUserDao.class); // 5. 使用代理对象执行方法 List<User> users = userDao.findAll(); for (User user:users){ System.out.println(user); } // 6. 释放资源 session.close(); in.close(); } }
mybatis基于注解的入门案例:
把IUserDao.xml移除,在dao接口的方法上使用@Select注解,并且指定SQL语句
同时需要在SqlMaoConfig.xml中的mapper配置时使用class属性指定dao接口的全限定类名
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"> <!-- mybatis的主配置文件 --> <configuration> <!-- 配置环境 --> <environments default="mysql"> <!-- 配置mysql的环境--> <environment id="mysql"> <!-- 配置事务的类型--> <transactionManager type="JDBC"></transactionManager> <!-- 配置数据源(连接池) --> <dataSource type="POOLED"> <!-- 配置连接数据库的4个基本信息 --> <property name="driver" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/eesy_mybatis"/> <property name="username" value="root"/> <property name="password" value="woaini1314"/> </dataSource> </environment> </environments> <!-- 指定映射配置文件的位置,映射配置文件指的是每个dao独立的配置文件 如果是用注解来配置的话,此处应该使用class属性指定被注解的dao全限定类名 --> <!--<mappers>--> <!--<mapper resource="com/itheima/dao/IUserDao.xml"/>--> <!--</mappers>--> <mappers> <mapper class="com.itheima.dao.IUserDao"/> </mappers> </configuration>
package com.itheima.dao; import com.itheima.domain.User; import org.apache.ibatis.annotations.Select; import java.util.List; /** * @author 黑马程序员 * @Company * * 用户的持久层接口 */ public interface IUserDao { /** * 查询所有操作 * @return */ @Select("select * from user") List<User> findAll(); }
明确:
我们在实际开发中,都是越简便越好,所以都是采用不写dao实现类的方式。
不管使用XML还是注解配置。
但是Mybatis是支持写dao实现类的。
入门案例中mybatis的设计模式分析:
(3)自定义Mybatis的分析
mybatis在使用代理dao的方式实现增删改查时做了些什么事呢?
只有两件事:
第一:创建代理对象
第二:在代理对象中调用selectList
自定义Mybatis能通过入门案例看到类:
class Resources
class SqlSessionFactoryBuilder
interface SqlSessionFactory
interface SqlSession
3.基础CRUD操作
第一步,在IUserDao接口中写入方法
第二步,在IUserDao.xml中配置方法的参数、返回值类型以及sql语句
第三步,执行测试方法
package com.itheima.dao; import com.itheima.domain.User; import org.apache.ibatis.annotations.Select; import org.apache.ibatis.annotations.Update; import java.util.List; /** * @author 黑马程序员 * @Company * * 用户的持久层接口 */ public interface IUserDao { /** * 查询所有操作 * @return */ // @Select("select * from user") List<User> findAll(); void saveUser(User user); void updateUser(User user); void deleteUser(Integer id); /** * 根据id查询用户 */ User findById(Integer userId); /** * 模糊查询 */ List<User> findByName(String username); /** * 查询总用户数 * @return */ int findTotal(); }
<?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.itheima.dao.IUserDao"> <!--配置查询所有--> <select id="findAll" resultType="com.itheima.domain.User"> select * from user </select> <!-- 保存用户--> <!-- parameterType 表示方法的参数类型--> <insert id="saveUser" parameterType="com.itheima.domain.User"> <!-- 配置插入操作后,获取插入数据的id--> <selectKey keyProperty="id" keyColumn="id" resultType="int" order="AFTER"> select last_insert_id(); </selectKey> insert into user(username,address,sex,birthday)values(#{username},#{address},#{sex},#{birthday}); </insert> <!-- 更新用户--> <update id="updateUser" parameterType="com.itheima.domain.User"> update user set username=#{username},address=#{address},sex=#{sex},birthday=#{birthday} where id = #{id}; </update> <!-- 删除用户 占位符可以随便写,因为只有一个参数--> <delete id="deleteUser" parameterType="Integer"> delete from user where id = #{1}; </delete> <!-- 根据id 查询用户--> <select id="findById" parameterType="Integer" resultType="com.itheima.domain.User"> select * from user where id = #{id}; </select> <!-- 模糊查询 --> <select id="findByName" parameterType="string" resultType="com.itheima.domain.User"> select * from user where username like #{name}; </select> <!-- 获取用户的总记录数--> <select id="findTotal" resultType="integer"> select count(id) from user; </select> </mapper>
package com.itheima.test; import com.itheima.dao.IUserDao; import com.itheima.domain.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.InputStream; import java.util.Date; import java.util.List; /** * @author 黑马程序员 * @Company * mybatis的入门案例 */ public class MybatisTest { private InputStream in; private SqlSession sqlSession; private IUserDao userDao; @Before // 用于在测试方法之前执行 public void init() throws Exception { // 1. 读取配置文件 in = Resources.getResourceAsStream("SqlMapConfig.xml"); // 2. 创建SqlSessionFactory工厂 SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder(); SqlSessionFactory factory = builder.build(in); // 工厂会封装 解析配置文件 // 3. 使用工厂生产SqlSession对象 sqlSession = factory.openSession(); // 4. 使用SqlSession创建Dao接口的代理对象 userDao = sqlSession.getMapper(IUserDao.class); } @After // 用于在测试方法之后执行 public void destory() throws Exception { // 提交事务 sqlSession.commit(); sqlSession.close(); in.close(); } @Test public void testFindAll() throws Exception { List<User> users = userDao.findAll(); for (User user : users) { System.out.println(user); } } /** * 测试保存操作 */ @Test public void testSave() throws Exception { User user = new User(); user.setUsername("chris"); user.setAddress("星辰园"); user.setSex("男"); user.setBirthday(new Date()); System.out.println("保存操作之前"+user); //5. 执行保存方法 userDao.saveUser(user); System.out.println("保存操作之后"+user); } @Test public void updateUser() throws Exception { User user = new User(); user.setId(49); user.setUsername("mybatis updateUser02"); user.setAddress("上海市松江区"); user.setSex("女"); user.setBirthday(new Date()); //5. 执行更新方法 userDao.updateUser(user); } @Test public void testDelete() throws Exception { userDao.deleteUser(48); } @Test public void testFindById() throws Exception { User user = userDao.findById(46); System.out.println(user); } /** * 测试模糊查询 * @throws Exception */ @Test public void testFindByName() throws Exception { List<User> users = userDao.findByName("%王"); for(User user:users){ System.out.println(user); } } /** * 测试查询总记录条数 * @throws Exception */ @Test public void testFindTotal() throws Exception { int total = userDao.findTotal(); System.out.println(total); } }
4. OGNL表达式
Object Graphic Navigation Language
对象 图 导航 语言
* 它是通过对象的取值方法来获取数据。在方法上把get给省略了。
比如:我们获取用户名称
类中的写法:user.getUsername();
OGNL表达式写法:user.username
mybatis中为什么能直接写username,而不用user.呢?
因为在parameterType中已经提供了属性所属的类,所以此时不需要写对象名
对象中包含对象来进行查询:(以后用于多对象的条件查询)
IUserDao中:
List<User> findUserByVo(QueryVo vo);
IUserDao.xml中:
<!-- 根据QueryVo的条件查询用户--> <select id="findUserByVo" parameterType="com.itheima.domain.QueryVo" resultType="com.itheima.domain.User"> select * from user where username like #{user.username}; </select>
测试类中:
@Test public void testFindByVo() throws Exception { QueryVo vo = new QueryVo(); User user = new User(); user.setUsername("%王%"); vo.setUser(user); List<User> users = userDao.findUserByVo(vo); for(User u:users){ System.out.println(u); } }
5. 如果实体类的成员属性名与数据库的列名不一致怎么办? 取别名或使用resultMap
当查询时,如果查询到的数据因为无法与User实体类的属性对应,解决方案方法:
第一种:可以采用取别名的方法:(执行效率高,一次解析)
<!--配置查询所有-->
<select id="findAll" resultType="com.itheima.domain.User">
select id as userId,username as userName from user
</select>
第二种:配置查询结果的列名和实体类的属性名的对应关系:(开发效率高,两次解析)使用后,需要在下面的CRUD标签里将resultType换为resultMap,且与唯一标识一致
<!--配置 查询结果的列名和实体类的属性名的对应关系--> <!-- id表示自己给取的唯一标识 type表示结果封装的实体类--> <resultMap id="userMap" type="com.itheima.domain.User"> <!--主键字段的对应 property表示java中的属性字段 column表示数据库中的列名--> <id property="userid" column="id"></id> <!--非主键字段的对应--> <result property="userName" column="username"></result> </resultMap>
6.properties、typeAliases、package标签
properties:可以在标签内部配置连接数据库的信息。也可以通过属性引用外部配置文件信息
resource属性: 常用的用于指定配置文件的位置,是按照类路径的写法来写,并且必须存在于类路径下。
url属性:是要求按照Url的写法来写地址
URL:Uniform Resource Locator 统一资源定位符。它是可以唯一标识一个资源的位置。
它的写法:
http://localhost:8080/mybatisserver/demo1Servlet
协议 主机 端口 URI
URI:Uniform Resource Identifier 统一资源标识符。它是在应用中可以唯一定位一个资源的。
<!-- 配置环境 -->
<properties resource="jdbcConfig.properties"></properties>
<environments default="mysql">
<!-- 配置mysql的环境-->
<environment id="mysql">
<!-- 配置事务的类型-->
<transactionManager type="JDBC"></transactionManager>
<!-- 配置数据源(连接池) -->
<dataSource type="POOLED">
<!-- 配置连接数据库的4个基本信息 -->
<property name="driver" value="${jdbc.driver}"></property>
<property name="url" value="${jdbc.url}"></property>
<property name="username" value="${jdbc.username}"></property>
<property name="password" value="${jdbc.password}"></property>
</dataSource>
</environment>
(2)typeAlias用于配置别名。type属性指定的是实体类全限定类名。alias属性指定别名,当指定了别名就不再区分大小写
<typeAlias type="com.itheima.domain.User" alias="user"></typeAlias>
用于指定要配置别名的包,当指定之后,该包下的实体类都会注册别名,并且类名就是别名,不再区分大小写
在mappers标签中:package的作用
7. Mybatis连接池
mybatis连接池提供了3种方式的配置:
配置的位置:
(1)主配置文件SqlMapConfig.xml中的dataSource标签,type属性就是表示采用何种连接池方式
type属性的取值:
POOLED:采用传统的javaxsql.DataSource规范中的连接池,mybatis中有针对规范的实现
UNPOOLED:采用传统的获取连接的方式,虽然也实现了javaxsql.DataSource接口,但是并没有使用池的思想
JNDI:采用服务器提供的JNDI技术实现,来获取DataSource对象,不同的服务器所能拿到的DataSource是不一样的
注意:如果不是web或者maven的war工程,是不能使用的。
我们课程中使用的是tomcat服务器,采用连接池就是dbcp连接池
8. Mybatis中的事务
什么是事务?
事务的四大特性ACID
不考虑隔离性会产生的3个问题
解决办法:四种隔离级别
mybatis是通过sqlsession对象的commit方法和rollback方法实现事务的提交和回滚,opensession方法传入参数true就可设置为自动提交事务
9.Mybatis映射文件的SQL深入
*动态SQL:if、where、foreach标签
if标签的使用:(其中test测试的是传入参数中username是否为空、#{}中的值也是javabean的变量,其他的都是数据库变量)
<!--根据条件查询 --> <select id="findUserByCondition" parameterType="com.itheima.domain.User" resultMap="userMap"> select * from user where 1 = 1 <if test="username!=null"> and username = #{username} </if> <if test="id != null"> and id = #{id} </if> </select>
where标签的使用:(能让我们省略永远1=1的条件,帮我们自动拼接sql)
<select id="findUserByCondition" parameterType="com.itheima.domain.User" resultMap="userMap"> select * from user <where> <if test="username!=null"> and username = #{username} </if> <if test="id != null"> and id = #{id} </if> </where> </select>
foreach标签:(用于条件的子查询)
<!--根据queryvo中的id集合实现查询用户列表--> <select id="findUserInIds" parameterType="com.itheima.domain.QueryVo" resultMap="userMap"> select * from user <where> <if test="ids != null and ids.size() > 0 "> <foreach collection="ids" open="and id in (" close=")" item="uid" separator=","> #{uid} </foreach> </if> </where> </select>
sql标签:(用于重复的sql语句)
<!--了解的内容:抽取重复的sql语句--> <sql id="defaultUser"> select * from user </sql> <!--配置查询所有--> <select id="findAll" resultType="com.itheima.domain.User"> <include refid="defaultUser"></include> </select>
10.Mybatis中的多表查询
完成account一对一操作:(建立实体类关系的方式)
Account类:
package com.itheima.domain; import java.io.Serializable; public class Account implements Serializable { private Integer id; private Integer uid; private double money; //从表实体应该包含一个主表实体的对象引用 private User user; public User getUser() { return user; } public void setUser(User user) { this.user = user; } 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 + '}'; } }
IAccountDao.xml
<?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.itheima.dao.IAccountDao"> <!-- 定义封装account和user的resultMap --> <resultMap id="accountUserMap" type="account"> <id property="id" column="aid"></id> <result property="uid" column="uid"></result> <result property="money" column="money"></result> <!-- 一对一的关系映射:配置封装user的内容--> <association property="user" column="uid" javaType="user"> <id property="id" column="id"></id> <result property="username" column="username"></result> <result property="address" column="address"></result> <result property="sex" column="sex"></result> <result property="birthday" column="birthday"></result> </association> </resultMap> <!--配置查询所有--> <select id="findAll" resultMap="accountUserMap"> select u.*,a.id as aid,a.uid,a.money from account a , user u where u.id = a.uid; </select> <!--查询所有账户同时包含用户名和地址信息--> <select id="findAllAccount" resultType="accountuser"> select u.*,a.id as aid,a.uid,a.money from account a , user u where u.id = a.uid; </select> </mapper>
mybatis实现一对多查询:如查询所有用户信息,同时获取该用户下所有的账户信息
映射配置及sql语句:
<!--配置 查询结果的列名和实体类的属性名的对应关系--> <!-- id表示自己给取的唯一标识 type表示结果封装的实体类--> <resultMap id="userMap" type="com.itheima.domain.User"> <!--主键字段的对应 property表示java中的属性字段 column表示数据库中的列名--> <id property="id" column="id"></id> <!--非主键字段的对应--> <result property="username" column="username"></result> <result property="address" column="address"></result> <result property="sex" column="sex"></result> <result property="birthday" column="birthday"></result> <!-- 配置user对象中accounts集合的映射 ofType表示集合中元素的类型 --> <collection property="accounts" ofType="account"> <id property="id" column="aid"></id> <result property="uid" column="uid"></result> <result property="money" column="money"></result> </collection> </resultMap> <!--一对多查询的使用--> <select id="findMany" resultMap="userMap"> select * from user u left outer join account a on u.id = a.uid </select>
实体类User:
package com.itheima.domain; import java.io.Serializable; import java.util.Date; import java.util.List; /** * @author 黑马程序员 * @Company */ public class User implements Serializable{ private Integer id; private String username; private Date birthday; private String sex; private String address; //一对多关系映射:主表实体应该包含从表实体的集合引用 private List<Account> accounts; public List<Account> getAccounts() { return accounts; } public void setAccounts(List<Account> accounts) { this.accounts = accounts; } public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public Date getBirthday() { return birthday; } public void setBirthday(Date birthday) { this.birthday = birthday; } public String getSex() { return sex; } public void setSex(String sex) { this.sex = sex; } public String getAddress() { return address; } public void setAddress(String address) { this.address = address; } @Override public String toString() { return "User{" + "id=" + id + ", username='" + username + '\'' + ", birthday=" + birthday + ", sex='" + sex + '\'' + ", address='" + address + '\'' + '}'; } }
测试类方法:
@Test public void testFindMany() throws Exception { List<User> users = userDao.findMany(); for(User user : users){ System.out.println("----每个用户的信息---"); System.out.println(user); System.out.println(user.getAccounts()); } }
mybatis实现多对多查询:
实体类:
package com.itheima.domain; import java.io.Serializable; import java.util.List; /** * @author 黑马程序员 * @Company */ public class Role implements Serializable { private Integer roleId; private String roleName; private String roleDesc; //多对多的关系映射:一个角色可以赋予多个用户 private List<User> users; public List<User> getUsers() { return users; } public void setUsers(List<User> 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 + '\'' + '}'; } }
IRoleDao.xml:
<?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.itheima.dao.IRoleDao"> <!--定义role表的ResultMap--> <resultMap id="roleMap" type="role"> <id property="roleId" column="rid"></id> <result property="roleName" column="role_name"></result> <result property="roleDesc" column="role_desc"></result> <collection property="users" ofType="user"> <id column="id" property="id"></id> <result column="username" property="username"></result> <result column="address" property="address"></result> <result column="sex" property="sex"></result> <result column="birthday" property="birthday"></result> </collection> </resultMap> <!--查询所有--> <select id="findAll" resultMap="roleMap"> select u.*,r.id as rid,r.role_name,r.role_desc from role r left outer join user_role ur on r.id = ur.rid left outer join user u on u.id = ur.uid </select> </mapper>
从用户表查到角色的sql语句:
<!--查询所有--> <select id="findAll" resultMap="roleMap"> select u.*,r.id as rid,r.role_name,r.role_desc from role r left outer join user_role ur on r.id = ur.rid left outer join user u on u.id = ur.uid </select>
11. Mybatis中的延迟加载
问题:在一对多中,当我们有一个用户,他有100个账户
那么在查询用户的时候要不要把关联的账户查出来?
在查询账户时,要不要把关联的用户查出来呢?
* 在查询用户时,用户下的账户信息,应该是什么时候使用就什么时候查询的。
* 在查询账户时,账户的所属用户信息应该是随着用户查询时一起查询出来
(1)什么是延迟加载?
* 既在真正使用数据时才发起查询,不用的时候不查询。也被称作按需加载(懒加载)
(2)什么是立即加载?
* 不管用不用,只要一调用方法,马上发起查询
在对应的四种表关系中:一对多,多对一,一对一,多对多
一对多,多对多:通常情况下我们都是采用延迟加载
多对一,一对一:通常情况下我们都是采用立即加载
12.Mybatis中的缓存
什么是缓存?
存在于内存中的临时数据
为什么使用缓存?
减少和数据库的交互次数,提高执行效率
什么样的数据能使用缓存,什么样的数据不能使用
适用于缓存:
经常查询并且不经常改变。
数据的正确与否对最终结果影响不大的。
不适用于缓存:
经常改变的数据
数据的正确与否对最终结果影响很大的。例如:商品的库存、银行的汇率、股市的牌价
(1)Mybatis中的一级缓存
一级缓存:它指的是mybatis中SqlSession对象的缓存
当我们执行查询之后,查询的结果会同时存入到SqlSession为我们提供的一块区域中
该区域的结构是一个Map,当我们再次查询同样的数据,mybatis会先去SqlSession中查询是否有,有的话直接拿出来用。
当SqlSession对象消失时,mybatis的一级缓存就消失了。也可通过sqlsession的clearCache()方法清除缓存
* 注意:当调用Sqlsession的修改,添加,删除,commit(),close()以及clearCache()等方法时,mybatis就会清空一级缓存
(2)Mybatis的二级缓存:其存放的内容是数据,而不是对象,因此内存地址会不同
二级缓存:它指得是Mybatis中SqlSessionFactory对象缓存。由同一个SqlSessionFactory对象创建的SqlSession共享其缓存。
二级缓存的使用步骤:
第一步:让Mybatis框架支持二级缓存(在SqlMapConfig.xml中配置)
<settings> <setting name="cacheEnabled" value="true"/> </settings>
第二步:让当前的映射文件支持二级缓存(在IUserDao.xml中配置)
第三步:让当前的操作支持二级缓存(在select标签中配置)
<!-- 开启user支持二级缓存 --> <cache/> <!-- 根据id查询用户 --> <select id="findById" parameterType="INT" resultType="user" useCache="true"> select * from user where id = #{uid} </select>
13.Mybatis的注解开发
注意事项:如果采用注解开发的话,dao下不能有dao.xml的配置文件,否则mybatis会报错
package com.itheima.dao; import com.itheima.domain.User; import org.apache.ibatis.annotations.Delete; import org.apache.ibatis.annotations.Insert; import org.apache.ibatis.annotations.Select; import org.apache.ibatis.annotations.Update; import java.util.List; /** * @author 黑马程序员 * @Company * 在mybatis中针对,CRUD一共有四个注解 * @Select @Insert @Update @Delete */ public interface IUserDao { /** * 查询所有用户 * @return */ @Select("select * from user") List<User> findAll(); /** * 保存用户 * @param user */ @Insert("insert into user(username,address,sex,birthday)values(#{username},#{address},#{sex},#{birthday})") void saveUser(User user); /** * 更新用户 * @param user */ @Update("update user set username=#{username},sex=#{sex},birthday=#{birthday},address=#{address} where id=#{id}") void updateUser(User user); /** * 删除用户 * @param userId */ @Delete("delete from user where id=#{id} ") void deleteUser(Integer userId); /** * 根据id查询用户 * @param userId * @return */ @Select("select * from user where id=#{id} ") User findById(Integer userId); /** * 根据用户名称模糊查询 * @param username * @return */ // @Select("select * from user where username like #{username} ") @Select("select * from user where username like '%${value}%' ") List<User> findUserByName(String username); /** * 查询总用户数量 * @return */ @Select("select count(*) from user ") int findTotalUser(); }
mybatis注解建立实体类属性与数据库表中列的对应关系如何实现?- - - - 通过Results 和 Result注解 如果其他注解要引用,可通过ResultMap注解引用
/** * 查询所有用户 * @return */ @Select("select * from user") @Results(id="userMap",value={ @Result(id=true,column = "id",property = "userId"), @Result(column = "username",property = "userName"), @Result(column = "address",property = "userAddress"), @Result(column = "sex",property = "userSex"), @Result(column = "birthday",property = "userBirthday"), List<User> findAll(); /** * 根据id查询用户 * @param userId * @return */ @Select("select * from user where id=#{id} ") @ResultMap("userMap") User findById(Integer userId);
mybatis注解开发一对一的查询配置:one=@One表示一对一的查询配置,select属性为查询到用户表的方法,fetchType为加载时机类型
/** * 查询所有账户,并且获取每个账户所属的用户信息 * @return */ @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(property = "user",column = "uid",one=@One(select="com.itheima.dao.IUserDao.findById",fetchType= FetchType.EAGER)) }) List<Account> findAll();
mybatis注解开发一对多的查询配置:many=@Many,提供一个查询账户的方法
/** * 查询所有用户 * @return */ @Select("select * from user") @Results(id="userMap",value={ @Result(id=true,column = "id",property = "userId"), @Result(column = "username",property = "userName"), @Result(column = "address",property = "userAddress"), @Result(column = "sex",property = "userSex"), @Result(column = "birthday",property = "userBirthday"), @Result(property = "accounts",column = "id", many = @Many(select = "com.itheima.dao.IAccountDao.findAccountByUid", fetchType = FetchType.LAZY)) }) List<User> findAll();
mybatis注解配置二级缓存:为dao接口配置给注解,并在SqlMapConfig中配置二级缓存即可
@CacheNamespace(blocking = true)