Mybatis
什么是框架?
他是我们软件开发中的一套解决方案,不同的框架解决不同的问题
使用框架的好处:
框架封装了很多的细节,使开发者可以使用极简的方法实现功能,大大提高开发效率
什么是三层架构?
表现层: 用于展示数据
业务层: 是处理业务需求
持久层: 和数据库进行交互
持久层技术解决方案
JDBC技术:
Connection
PreparedStatement
ResultSet
SpringJdbcTemplate:
Spring中对JDBC的简单封装
Apache的DBUtils
和Spring的JDBCTemplate很像,都是简单封装
以上都不是框架
JDBC是规范
Spring的JdbcTemplate和Apache的DBUtils都只是工具类
Mybatis框架概述:
是一个优秀的基于java的持久层框架,它内部封装了jdbc很多细节,所以我们只需关注sql语句本身,无需关注加载驱动,获取连接,创建statement对象等过程,使用ORM思想实现结果集的封装.
ORM(Object Relational Mapping) :对象关系映射
就是把数据库表和实体类的属性对应起来,让我们可以操作实体类就实现操作数据库表
mybatis的环境搭建:
1.创建maven工程并导入坐标
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/test"/>
<property name="user" value="debian-sys-maint"/>
<property name="password" value="YN6bICN3ccV5FbIn"/>
</dataSource>
</environment>
</environments>
<!--指定映射配置文件的配置,映射配置文件是指给每个Dao独立的配置文件-->
<mappers>
<mapper resource="com/tulun/dao/IUserDao.xml"></mapper>
</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.tulun.dao.IUserDao">
<!--配置查询所有-->
<select id="findAll">
select *from user
</select>
</mapper>
环境搭建的注意事项:
1.创建IUSerDao.xml 和 IUserDao.java时名称是为了和我们之前的知识保持一致,在Mybatis中,他把持久层的操作接口名称和映射文件也叫作:Mapper
所以IUserDao 和IUserMapper是一样的
2.在idea创建目录的时候,他和包是不一样的, 包在创建时:com.tulun.dao他是三级目录 目录在创建时:com.tulun.dao他是一级目录
3.mybatis的映射配置文件位置必须和dao接口的包结构相同
4.映射配置文件的mapper标签的namespace属性的取值必须是dao接口的全限定类名
5.映射配置文件的操作配置(select ),id属性必须是dao接口的方法名 当我们遵从了3,4,5点之后,我们在开发中就无需在写dao的实现类
Mybatis的基本代码:
第一步:读取配置文件
第二步:创建SqlSessionFactory工厂
第三步:创建SqlSession对象
第四步:创建Dao接口的代理对象
第五步:执行dao中的方法
第六步:释放资源
//加载SqlMapConfig.xml配置文件
InputStream in = Resources.getResourceAsStream("SqlMapConfig.xml");
//创建SqlSessionFactory会话工厂
SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(in);
//创建会话
SqlSession sqlSession = sessionFactory.openSession();
//获取代理对象
AccountDao accountDao = sqlSession.getMapper(AccountDao.class);
//查询
List<Account> accounts = accountDao.findAll();
for(Account account : accounts) {
System.out.println(account);
}
in.close();
sqlSession.close();
Mybatis的CRUD操作
<mapper namespace="com.tulun.dao.IUserDao">
<!--配置查询所有的结果的列名和实体类属性名的对应关系-->
<resultMap id="userMap" type="com.tulun.domain.User">
<!--配置主键字段的对应-->
<!-- <id property="" column=""></id> -->
<!--非主键字段的对应-->
<!-- <result property="userId" column="id"></result> -->
<result property="userName" column="username"></result>
<result property="userSex" column="sex"></result>
<result property="UserBirthday" column="birthday"></result>
<result property="userAddress" column="address"></result>
</resultMap>
<!--查询所有-->
<select id="findAll" resultMap="userMap">
select *from user;
</select>
<!--保存用户 parameterType:参数的类型-->
<insert id="saveUser" parameterType="com.tulun.domain.User">
<!--配置插入操作后,获取插入数据的id-->
<selectKey keyProperty="userId" keyColumn="id" resultType="Integer" order="AFTER">
select last_insert_id();
</selectKey>
insert into user(id,username,birthday,sex,address)values (#{userId},#{userName},#{UserBirthday},#{UserSex},#{UserAddress});
</insert>
<!--更新用户-->
<update id="updateUser" parameterType="com.tulun.domain.User">
update user set username=#{userName},birthday=#{UserBirthday},address=#{UserAddress} where id=#{id};
</update>
<!--删除用户-->
<delete id="deleteUser" parameterType="Integer">
delete from user where id=#{id};
</delete>
<!--根据id查询用户-->
<select id="findById" parameterType="Integer" resultMap="userMap">
select * from user where id=#{id};
</select>
<!--根据用户名查询 模糊查询-->
<select id="findByName" parameterType="java.lang.String" resultMap="userMap">
<!--select * from user where username like #{username}; -->
select * from user where username like '%${value}%'
</select>
<!--查询总用户数-->
<select id="findTotal" resultMap="userMap">
select count(id) from user;
</select>
<!--根据queryVo的条件查询用户-->
<select id="findUserByVo" parameterType="com.tulun.domain.QueryVo" resultMap="userMap">
select * from user where username like #{user.userName};
</select>
</mapper>
Mybatis的参数深入:
parameterType(输入类型):传递简单类型
传递pojo(实体类)对象
传递pojo包装对象 什么是ognl表达式:Object(对象) Graphic(图) Navigation(导航) Language(语言)
它是通过对象的取值方法来获取数据.在写法上吧get省略了.比如:获取用户的名称:
类中的写法:user.getUsername();
ognl表达式写法:user.username; mybatis中为什么可以直接写username而不用user.呢
因为在parameterType中已经提供了属性所属的类,所以比用些对象名.
Mybatis的标签使用:
properties标签:
<!--配置properties
可以在标签内部配置连接数据库的信息.也可以通过属性引用外部配置文件信息
resource属性:
用于指定配置文件的位置,是按照类路径的写法来写,必须存在类路径下.
url属性:
是要求按照Url的写法来写地址
URL: Uniform Resource Locator 统一资源定位符.他是可以唯一标识一个资源的位置
它的写法:
http://localhost:8080/mybatisserver/demoSerlet
协议 主机 端口 URI
URI:Uniform Resource Identifier 统一资源标识符.他是在应用中可以唯一定位一个资源的
-->
<!--配置properties 1.通过加载配置文件的方式 配置resource属性-->
<properties resource="jdbcConfig.properties"></properties>
<!--配置properties 2.通过配置url的方式-->
<properties url="file:///home/fly/IdeaProjects/mybatis/mybatis_CRUD/src/main/resources/jdbcConfig.properties"></properties>
typeAliases标签:
<!--使用typeAliases 配置别名.只能配置domain中的类的别名-->
<typeAliases>
一. <!--typeAlias用于配置别名 type属性指定的是实体类全限定类名 ,alias属性指定别名,当指定了别名之后就不在区分大小写-->
<typeAlias type="com.tulun.domain.User" alias="user"></typeAlias>
二. <!--用于指定配置别名的包 ,当指定之后,该包下的实体类都会注册别名,并且类名就是别名,不区分大小写-->
<package name="com.tulun.domain"/>
</typeAliases>
<!--配置映射文件-->
<mappers>
<mapper resource="com/tulun/dao/IUserDao.xml"></mapper>
<!--package标签是指定dao接口所在的包,当指定之后就不需要再写mapper 以及resource 或者class-->
<package name="com.tulun.dao"/>\
</mappers>
mapper里的标签(可以实现动态sql)
if标签
<!--根据条件查询-->
<select id="findUserByCondition" parameterType="user" resultMap="userMap">
select *from user where 1=1
<if test="userName != null ">
and username =#{userName}
</if>
</select>
where标签
<select id="findUserByCondition" parameterType="user" resultMap="userMap">
select *from user
<where>
<if test="userName != null ">
and username =#{userName}
</if>
<if test="UserSex !=null">
and sex =#{UserSex}
</if>
</where>
</select>
sql标签
<!--了解的内容 用于抽取重复的语句-->
<sql id="defaultUser">
select *from user;
</sql>
<!--查询所有-->
<select id="findAll" resultMap="userMap">
<include refid="defaultUser"></include>
</select>
foreach标签
<!--根据queryVo的id集合 实现查询用户列表-->
<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="id" separator=",">
<!--这个id 由 item的 id 决定-->
#{id}
</foreach>
</if>
</where>
</select>
连接池:
可以减少我们获取连接所消耗的时间. (连接池就是一个用于存储连接的容器,容器就是一个集合对象,该集合必须是线程安全的,不能两个线程拿到同一个连接,该集合还必须实现队列的特性:先进先出) ```
mybatis中的连接池:
mybatis连接池提供了三种方式的配置
配置的位置:
主配置文件SqlMapConfig.xml中的datasource标签,type属性表示采用那种连接池的方式
type的取值:
POOLED :采用传统的javax.sql.DataSource规范中的连接池 ,mybatis中有针对规范的实现(相当于我们手动创造一个连接)
UNPOOLED :采用传统的获取连接的方式,虽然也实现了javax.sql.DataSource接口,但是并没有采用池的思想.
(拓展) JNDI :采用服务器提供的JNDI技术实现来获取DataSource对象,不同的 服务器拿到的dataSource是不一样的.
注意:如果不是web或者maven的war工程,是不能使用的.
我们测试时候使用tomcat服务器,采用的连接池:dbcp连接池
Mybatis的延迟加载
问题:在一对多中,有一个用户,他有100个账户,在查询的时候,要不要吧关联的账户查出来?
在查询用户时,用户下的账户信息应该是什么时候使用,什么时候查询
在查询账户时,账户的所属用户信息应随着账户查询时一起查出来
什么是延迟加载
在真正使用数据时才发起查询,不用的时候不查询(按需加载,懒加载)
什么是立即加载
不管用不用,只要调用方法,马上发起查询
在对应的四种表关系中:一对一 多对一 一对多 多对多
一对多,多对多:通常情况下采用延迟加载.
多对一,一对一:通常情况下采用立即加载.
//SqlMapConfig文件:
<!-配置参数--->
<settings>
<!--开启Mybatis支持延迟加载-->
<setting name ="lazyLoadingEnable" value = "true"/> //默认值为true
<!--当开启时,任何方法的调用都会加载该对象的所有属性-->
<setting name ="aggressiveLazyLoading" value = "false"/></setting>
</settings>
mybatis中的一级缓存和二级缓存
一级缓存:
mybatis的sqlSession对象的缓存
但我们执行查询之后,查询的结果会同时存入到sqlSession为我们提供的区域,该区域的结构是一个map.
当我们再一次查询同样的数据,mybatis会先去sqlSession中查看是否有,有的话直接拿出来用,
当sqlSession对象消失后,mybatis的一级缓存也就消失了.
sqlSession.clearCache();可以清空缓存
当调用sqlSession的修改,添加,删除,commit()等方法时,就会清空一级缓存
User user1 = userDao.findById(1);
System.out.println(user1);
sqlSession.clearCache(); //清空缓存
//update(),commit(),delete();
User user2 = sqlSession.getMapper(IUserDao.class).findById(1);
System.out.println(user2);
System.out.println(user1 == user2);
打印结果:
com.tulun.domain.User@6c64cb25
com.tulun.domain.User@5158b42f
false
二级缓存
指的是mybatis中的sqlSessionFactory对象的缓存,由同一个sqlSessionFactory对象创建的sqlSession共享其缓存. 二级缓存存放的是数据,不是对象
二级缓存的使用步骤:
1.让mybatis框架支持二级缓存(在sqlMapConfig.xml中配置)
<!--配置二级缓存-->
<setting name="cacheEnabled" value="true"/>
2.让当前的映射文件支持二级缓存(在IUserDao.xml中配置)
<!--开启user支持二级缓存-->
<cache/>
3.让当前的操作支持二级缓存(在select标签中配置)
<select id="findById" resultType="com.tulun.domain.User" useCache="true">
select *from user where id =#{id}
</select>
Mybatis的注解开发:
注解代码:
SqlMapConfig.xml
<mappers>
<package name="com.tulun.dao"/>
</mappers>
IUserDao ( Interface )
/**
* 查询所有
* @return
*/
@Select("select *from user")
List<User> findAll();
/**
* 保存用户
* @param user
*/
@Insert("insert into user(id,username,address,sex,birthday) value(#{id},#{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 id
*/
@Delete("delete from user where id =#{id}")
void deleteUser(Integer id);
/**
* 查询一个用户
* @param id
* @return
*/
@Select("select * from user where id=#{id}")
User findUserById(Integer id);
/**
* 根据名字查询用户
* @param name
* @return
*/
@Select("select * from user where username like #{username}")
List<User> findUserByName(String name);
注意:使用注解开发的时候,就不必配置com.tulun.dao.IUserDao.xml文件,因为如果两个都有,mybatis加载的时候,就不知道该怎么办.
IUserDao(Interface)
举例://如果User实体类属性名和数据库属性名不一样,可以使用以下标签
@Results(id = "userMap",value = {
@Result(id = true, column = "id",property = "userId"),//id为主键
@Result(column = "username",property = "userName"),
@Result(column = "sex",property = "userSex"),
@Result(column = "birthday",property = "userBirthday"),
@Result(column = "address",property = "userAddress"),
})
/**
* 查询所有
* @return
*/
@Select("select *from user")
@ResultMap(value = {"userMap"}) //标准写法
List<User> findAll();
多表查询操作
多对一(账户查用户)
在mybatis中,多对一的查询就是一对一(多个账户对应一个用户 mybatis认为:一个账户对应一个用户)
在Account实体类中就应该有一个private User user; 属性
IAccountDao(Interface)
@Results(id = "accountMap",value = {
@Result(id = true, column = "id",property = "userId"),//id为主键
@Result(column = "uid",property = "uid"),
@Result(column = "money",property = "money"),
@Result(property = "user",column = "uid",one = @One(select = "com.tulun.dao.IUserDao.findById",fetchType = FetchType.EAGER))
})
property: user属性
cloumn:根据uid在user库进行查找
one=@One(select ="在user库查找的方式")
fetchType.EAGER:立即加载方式 (还有lazy懒加载,default默认)
一对多(用户查账户)
在User实体类中就应该有一个 private List<Account> accounts; 属性
IUserDao(Interface)
@Results(id = "userMap",value = {
@Result(id = true, column = "id",property = "userId"),//id为主键
@Result(column = "username",property = "userName"),
@Result(column = "sex",property = "userSex"),
@Result(column = "birthday",property = "userBirthday"),
@Result(column = "address",property = "userAddress"),
@Result(property = "accounts",column = "id",many =@Many(select = "com.tulun.dao.IAccount.findAccountByUid",fetchType = FetchType.LAZY))
})
property: accounts属性
cloumn:根据id在account库进行查找
many=@Many(select ="在account库查找的方式")
fetchType.LAZY:懒加载方式
缓存的配置
mybatis中不管xml还是注解,一级缓存都是本身具备的.
二级缓存:
SqlSession sqlSession = factory.openSession();
User user1 = sqlSession.getMapper(IUserDao.class).findById(1);
System.out.println(user1);
sqlSession.close(); //释放一级缓存
SqlSession sqlSession = factory.openSession();// 再次打开sqlSession
User user2 = sqlSession.getMapper(IUserDao.class).findById(1);
System.out.println(user2);
使开启二级缓存:
SqlMapConfig.xml文件
<settings>
<!--配置二级缓存-->
<setting name="cacheEnabled" value="true"/>
</settings>
IUserDao接口
@CacheNamespace(blocking = true) //开启二级缓存
public interface IUserDao {
}