*mybatis
一、
1.resources的配置
1)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">
<!--配置连接数据库的四个基本信息-->
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybaits? useSSL=FALSE&serverTimezone=UTC&characterEncoding=utf-8"/>
<property name="username" value="root"/>
<property name="password" value="123"/>
</dataSource>
</environment>
</environments>
<!--指定映射配置文件的位置,映射配置文件指的是每个dao独立的配置文件-->
<mappers>
<mapper resource="com/itheima/dao/IUserDao.xml"/>
</mappers>
</configuration>
2)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">
<!--配置查询所有-->
<select id="findAll" resultType="com.itheima.domain.User">
select * from user
</select>
</mapper>
2.入门案例
第一步:读取配置文件
第二步:创建SqlSessionFactory工厂
第三步:创建SqlSession
第四步:创建Dao接口的代理对象
第五步:执行dao中的方法
第六步:释放资源
注意事项:
不要忘记在映射配置中告知mybatis要封装到哪个实体类中
配置的方式:指定实体类的全限定类名
【 mybatis基于注解的入门案例:】
把IUserDao.xml移除,在dao接口的方法上使用@Select("sql语句")注解,并且指定SQL语句
同时需要在SqlMapConfig.xml中的mapper配置时,使用class属性指定dao接口的全限定类名。
<mapper class="com.itheima.dao.IUserDao">
明确:
我们在实际开发中,都是越简便越好,所以都是采用不写dao实现类的方式。
不管使用XML还是注解配置。
但是Mybatis它是支持写dao实现类的。
代码:
@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 destroy() throws Exception{
// sqlSession.commit();
//6.释放资源
sqlSession.close();
in.close();
}
3.自定义Mybatis的分析:
mybatis在使用代理dao的方式实现增删改查时做什么事呢?
只有两件事:
第一:创建代理对象
第二:在代理对象中调用selectList
自定义mybatis能通过入门案例看到类
class Resources
class SqlSessionFactoryBuilder
interface SqlSessionFactory
interface SqlSession
二、
IUserDao.xml中的insert标签内还可以写子标签
<!-- 配置插入操作后,获取插入数据的id -->
<selectKey keyProperty="userId" keyColumn="id" resultType="int" order="AFTER">
select last_insert_id();
</selectKey>
OGNL表达式:
Object Graphic Navigation Language
对象 图 导航 语言
它是通过对象的取值方法来获取数据。在写法上把get给省略了。
比如:我们获取用户的名称
类中的写法:user.getUsername();
OGNL表达式写法:user.username
mybatis中为什么能直接写username,而不用user.呢:
因为在parameterType中已经提供了属性所属的类,所以此时不需要写对象名
数据库列名与java实体类属性不对应:
<resultMap id="userMap(任意,但要与下面标签对应)" type="对应实体类全类名">
<!--主键字段的对应-->
<id property="userId" column="id"></id>
<!--非主键字段对应-->
<result property="userName" colum="username"></result>
<result property="userAddress" colum="address"></result>
<result property="userSex" colum="sex"></result>
<result property="userBirthday" colum="birthday"></result>
</resultMap>
然后在具体sql实现的标签内也要修改
<select id="findAll" resultMap="userMap">select * from user;</select>
可以在SqlMapConfig.xml中加入properties属性!(在Resource中添加jdbcConfig.properties文件)
<?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>
<!--配置properties
可以在标签内部配置连接数据库信息。也可以通过属性引用外部配置文件信息
resource属性:常用
用于配置文件的位置,是按照类路径的写法来写,并且必须存在于类路径下
url属性:
是按照url的写法来写地址
URL:Uniform Resource Locator 统一资源定位符,可以唯一标识一个资源的位置
它的写法:
协议 主机 端口 URI:Uniform Resouce Identifier 统一资源标识符,它是应用中可以唯一定位
url="file:///D:/jdbcConfig.properties
-->
<properties resource="jdbcConfig.properties"></properties>
<!--配置环境-->
<environments default="mysql">
<!--配置mysql的环境-->
<environment id="mysql">
<!--配置事务的类型-->
<transactionManager type="JDBC"></transactionManager>
<!--配置数据源(连接池)-->
<dataSource type="POOLED">
<!--配置连接数据库的四个基本信息-->
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
</environment>
</environments>
<!--指定映射配置文件的位置,映射配置文件指的是每个dao独立的配置文件-->
<mappers>
<mapper resource="com/itheima/dao/IUserDao.xml"/>
</mappers>
</configuration>
使用typeAliases配置别名,它只能配置domain中类的别名
<typeAliases>
<!--typeAlias 用于配置别名,type属性指定的是实体类全限定类名 alias指定别名,当指定了别名就不区分大小写-->
<!--<typeAlias type="com.itheima.domain.User" alias="user"><typeAlias>-->
<!--用于指定别名的包,当指定之后,该包下的类实体类都会注册别名,并且类名就是别名,不再区分带小写-->
<package name="com.itheim.domain"></package>
</typeAliases>
mappers标签内也有package标签
<mappers>
<!--package标签是用于指定dao接口所在的包,当指定完之后就不需要写mapper以及resouce或者class属性了-->
<package name="com.itheima.dao"></package>
</mappers>
三、
1、mybatis中的连接池
mybatis连接池提供了3种方式的配置:
配置的位置:
主配置文件SqlMapConfig.xml中的dataSource标签,type属性就是表示采用何种连接池方式。
type属性的取值:
POOLED 采用传统的javax.sql.DataSource规范中的连接池,mybatis中有针对规范的实现
【先看池内有没有空闲的,如果没有就判断活动连接数量是不是小于最大数量,如果小于就创建新的连接去用,如果不小 于就取一个最老的连接拿来使用】
UNPOOLED 采用传统的获取连接的方式,虽然也实现Javax.sql.DataSource接口,但是并没有使用池的思想。
JNDI 采用服务器提供的JNDI技术实现,来获取DataSource对象,不同的服务器所能拿到DataSource是不一样。
注意:如果不是web或者maven的war工程,是不能使用的。
我们课程中使用的是tomcat服务器,采用连接池就是dbcp连接池。
2、mybatis中的事务
什么是事务
事务的四大特性ACID
不考虑隔离性会产生的3个问题
解决办法:四种隔离级别
它是通过sqlsession对象的commit方法和rollback方法实现事务的提交和回滚
sqlsession.opensession(true)是自动提交事务!
3、动态SQL语句
比如我们不知道查询时用户输入的到底是用户名还是性别还是地址。。。。
【if标签】
<select id="findUserByCondition" resultMap="userMap" parameterType="user">
select * from user where 1=1
<if test="userName != null">
and username= #{userName}
</if>
<if test="userSex != null">
and sex=#{userSex}
</if>
</select>
【where标签,不用写where 1=1】
<select id="findUserByCondition" resultMap="userMap" parameterType="user">
select * from user
<where>
<if test="userName != null">
and username= #{userName}
</if>
<if test="userSex != null">
and sex=#{userSex}
</if>
</where>
</select>
【foreach标签】
根据QueryVo中的id集合实现查询用户列表类似于:select * from user where id in(41,42,45)
<select id="findUserInIds" resultMap="userMap" parameterType="queryvo">
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 id="defaultUser">
select * from user
</sql>
<select id="findAll" resultMap="userMap">
<include refid="defaultUser"></include>
</select>
4、mybatis中的多表查询
表之间的关系有几种:
一对多
多对一
一对一
多对多
举例:
用户和订单就是一对多
订单和用户就是多对一
一个用户可以下多个订单
多个订单属于同一个用户
人和身份证号就是一对一
一个人只能有一个身份证号
一个身份证号只能属于一个人
老师和学生之间就是多对多
一个学生可以被多个老师教过
一个老师可以交多个学生
特例:
如果拿出每一个订单,他都只能属于一个用户。
所以Mybatis就把多对一看成了一对一。
*一对多 mybatis中的多表查询:
示例:用户和账户
一个用户可以有多个账户
一个账户只能属于一个用户(多个账户也可以属于同一个用户)
步骤:
1、建立两张表:用户表,账户表
让用户表和账户表之间具备一对多的关系:需要使用外键在账户表中添加
2、建立两个实体类:用户实体类和账户实体类
让用户和账户的实体类能体现出来一对多的关系
3、建立两个配置文件
用户的配置文件
账户的配置文件
4、实现配置:
当我们查询用户时,可以同时得到用户下所包含的账户信息
当我们查询账户时,可以同时得到账户的所属用户信息
【不常用】
<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>
【常用方式,是将user封装在account表中】
<resultMap id="accountUserMap" type="account">
<id property="id" column="aid"></id> //sql语句重命名aid
<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="usernmae"></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>
*一对多:主表实体应该包含从表实体的集合引用(User内含有acounts集合)
<resultMap id="userAccountMap" type="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>
<!--配置user对象中accounts集合的映射-->
<collection property="accounts" ofType="account"> //ofType是集合的类型,如果没有别名要写全限定名
<id column="aid" property="id"></id> //因为我们在查询语句中起别名了所以用aid
<result column="uid" property="uid"></result>
<result column="money" property="money"></result>
</collection>
</resultMap>
<select id="findAll" resultMap="userAccountMap">
select * from user u left outer join account a on u.id=a.uid
<select>
*多对多
示例:用户和角色
一个用户可以有多个角色
一个角色可以赋予多个用户
步骤:
1、建立两张表:用户表,角色表
让用户表和角色表具有多对多的关系。需要使用中间表,中间表中包含各自的主键,在中间表中是外键。
2、建立两个实体类:用户实体类和角色实体类
让用户和角色的实体类能体现出来多对多的关系
各自包含对方一个集合引用
3、建立两个配置文件
用户的配置文件
角色的配置文件
4、实现配置:
当我们查询用户时,可以同时得到用户所包含的角色信息
当我们查询角色时,可以同时得到角色的所赋予的用户信息
【多对多:多对多的映射:一个角色可以赋予多个用户】
<!--定义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>
【用户到角色的多对多】
<!-- 定义User的resultMap-->
<resultMap id="userMap" type="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>
<!-- 配置角色集合的映射 -->
<collection property="roles" ofType="role">
<id property="roleId" column="rid"></id>
<result property="roleName" column="role_name"></result>
<result property="roleDesc" column="role_desc"></result>
</collection>
</resultMap>
<!-- 查询所有 -->
<select id="findAll" resultMap="userMap">
select u.*,r.id as rid,r.role_name,r.role_desc from user u
left outer join user_role ur on u.id = ur.uid
left outer join role r on r.id = ur.rid
</select>
<!-- 根据id查询用户 -->
<select id="findById" parameterType="INT" resultType="user">
select * from user where id = #{uid}
</select>
四、
1、Mybatis中的延迟加载
问题:在一对多中,当我们有一个用户,它有100个账户。
在查询用户的时候,要不要把关联的账户查出来?
在查询账户的时候,要不要把关联的用户查出来?
在查询用户时,用户下的账户信息应该是,什么时候使用,什么时候查询的。
在查询账户时,账户的所属用户信息应该是随着账户查询时一起查询出来。
什么是延迟加载
在真正使用数据时才发起查询,不用的时候不查询。按需加载(懒加载)
什么是立即加载
不管用不用,只要一调用方法,马上发起查询。
在对应的四种表关系中:一对多,多对一,一对一,多对多
一对多,多对多:通常情况下我们都是采用延迟加载。
多对一,一对一:通常情况下我们都是采用立即加载。
【1对1】 在SqlMapConfig.xml中添加配置:
<!--配置参数-->
<settings>
<!--开启Mybatis支持延迟加载-->
<setting name="lazyLoadingEnabled" value="true"/>
<setting name="aggressiveLazyLoading" value="false"></setting>
</settings>
在IAccount.xml文件中配置
<!-- 定义封装account和user的resultMap -->
<resultMap id="accountUserMap" type="account">
<id property="id" column="id"></id>
<result property="uid" column="uid"></result>
<result property="money" column="money"></result>
<!-- 一对一的关系映射:配置封装user的内容
select属性指定的内容:查询用户的唯一标识:
column属性指定的内容:用户根据id查询时,所需要的参数的值
-->
<association property="user" column="uid" javaType="user" select="com.itheima.dao.IUserDao.findById"></association>
</resultMap>
<!-- 查询所有 -->
<select id="findAll" resultMap="accountUserMap">
select * from account
</select>
<!-- 根据用户id查询账户列表 -->
<select id="findAccountByUid" resultType="account">
select * from account where uid = #{uid}
</select>
代码分析:
List<Account> accounts = accountDao.findAll(); //执行到这的时候只执行select * from account
// for(Account account : accounts){ //若要执行for循环内的语句则查询一个account执行一遍select语句
// System.out.println("--------每个account的信息------------");
// System.out.println(account);
// System.out.println(account.getUser());
// }
【1对多】
<!-- 定义User的resultMap-->
<resultMap id="userAccountMap" type="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>
<!-- 配置user对象中accounts集合的映射 -->
<collection property="accounts" ofType="account" select="com.itheima.dao.IAccountDao.findAccountByUid" column="id"></collection>
</resultMap>
<!-- 查询所有 -->
<select id="findAll" resultMap="userAccountMap">
select * from user
</select>
<!-- 根据id查询用户 -->
<select id="findById" parameterType="INT" resultType="user">
select * from user where id = #{uid}
</select>
2、Mybatis中的缓存
什么是缓存
存在于内存中的临时数据。
为什么使用缓存
减少和数据库的交互次数,提高执行效率。
什么样的数据能使用缓存,什么样的数据不能使用
适用于缓存:
经常查询并且不经常改变的。
数据的正确与否对最终结果影响不大的。
不适用于缓存:
经常改变的数据
数据的正确与否对最终结果影响很大的。
例如:商品的库存,银行的汇率,股市的牌价。
Mybatis中的一级缓存和二级缓存
一级缓存:
它指的是Mybatis中SqlSession对象的缓存。
当我们执行查询之后,查询的结果会同时存入到SqlSession为我们提供一块区域中。
该区域的结构是一个Map。当我们再次查询同样的数据,mybatis会先去sqlsession中
查询是否有,有的话直接拿出来用。
当SqlSession对象消失时,mybatis的一级缓存也就消失了。
【代码】
User user1 = userDao.findById(41);
System.out.println(user1);
//sqlSession.close();
//再次获取SqlSession对象
//sqlSession = factory.openSession();
sqlSession.clearCache();//此方法也可以清空缓存
userDao = sqlSession.getMapper(IUserDao.class);
User user2 = userDao.findById(41);
System.out.println(user2);
System.out.println(user1 == user2);
注意: 如果更新了数据库第二次查询也不会从缓存中查询而是直接从数据库中查询,当调用SqlSession的修改,添加,删除, commit(),close()等方法时就会清空一级缓存。
二级缓存:【不常用】
它指的是Mybatis中SqlSessionFactory对象的缓存。由同一个SqlSessionFactory对象创建的SqlSession共享其缓存。
一个SqlSessionFactory可以生产多个SqlSession对象而这些对象共享二级缓存内容。
二级缓存的使用步骤:
第一步:让Mybatis框架支持二级缓存(在SqlMapConfig.xml中配置)
<settings>
<setting name="cacheEnabled" value="true"/>
</settings>
第二步:让当前的映射文件支持二级缓存(在IUserDao.xml中配置)
<!--开启user支持二级缓存-->
<cache/>
第三步:让当前的操作支持二级缓存(在select标签中配置)
<!-- 根据id查询用户 -->
<select id="findById" parameterType="INT" resultType="user" useCache="true">
select * from user where id = #{uid}
</select>
注意:二级缓存中存放的内容是数据而不是对象,所以两次查询的对象不是同一个对象
3、Mybatis中的注解开发
环境搭建:不用写对应(dao)的xml文件了,如果存在xml文件就不能用注解开发了
单表CRUD操作(代理Dao方式)
@Select("select * from user")
@Insert("insert into user(username,address,sex,birthday) values(#{username},#{address},#{sex},# {birthday})")
@Update("update user set username=#{username},sex=#{sex},address=#{address}.....where id=#{id}")
@Delete("delete from user where id=#{id}")
模糊查询:
@Select("select * from user where username like #{username}")//在语句中要加入%abc% 参数占位符方式
@Select("select * from user where username like'%${value}%'")//在与剧中就不用加%了
数据库表列跟实体类属性不一致也可以用注解(不需要写resultMap了,写在接口方法上):
@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"),
})
之后再别的方法上直接引用@ResultMap(value={"userMap"})//如果只有一个属性value 和{}是可以省略的
多表查询操作(Account表)
【一对一】
在@Results中添加一个@Result(property="user",column="uid",
one=@One(select="com.itheima.dao.IUserDao.findById",fetchType=FetchType.EAGER))
【一对多】(一个user多个account)
在@Results中添加一个@Result(property="accounts",column="id", many=@Many(select="com.itheima.dao.IAccount.findAccountByUid",fetchType=FetchType.LAZY))
缓存的配置
一级缓存是默认开启的(SqlSession)
二级缓存需要开启:(SqlSessionFactory)
在SqlMapConfig.xml中配置
<settings>
<setting name="cacheEnabled" value="true"/>
</settings>
在IUserDao接口上使用@CacheNamespace(blocking = true)