MyBatis框架

框架:
三层架构:
表现层:是用于展示数据的
业务层:是处理业务需求的
持久层:是和数据库交互的
3 持久层技术解决方案:
JDBC技术:
Connection
PreparedStatement
ResultSet
Spring的JDBCTemplate:
Spring中对jdbc的简单封装
Apache的DBUtils:
它和Spring的jdbcTemplate很像,也是对jdbc的简单封装
以上这些都不是框架
JDBC是规范
Spring的JDBCTemplate和Apache的DBUtils 都是工具类
4 mybatis的概述:
mybatis是一个持久层框架,用java编写的,它封装了jdbc操作的很多细节,使开发只需要关注SQL语句本身,而无需关注注册驱动,创建连接等复杂过程,它使用了ORM思想实现了结果集的封装。

ORM:
Object Relational Mapping 对象关系映射
简单的说:
就是把数据库表和实体类及实体类的属性对应起来
让我们可以操作实体类就实现操作数据库表。
Mybatis的环境搭建:
第一步:创建maven工程并导入坐标
第二步:创建实体类和dao的接口
第三步:创建Mybatis的主配置文件 SqlMapConfig.xml
第四部:创建映射配置文件:IUserDao.xml
环境搭建的注意事项:
第一个:创建IUserDao.xml和IUserDao.java时名称是为了和我们之前的知识保持一致。所以Mybatis中它把持久层的操作接口名称和映射文件也叫做:Mapper,所以IUserDao和IUserMapper是一样的。
第二个:
在idea中创建目录的时候,它和包的是不一样的,包在创建时:com.Mybatis.dao它是三级目录,目录在创建时:com.itheima.dao是一级目录。
环境搭建的
第三个:mybatis的映射文件位置必须和dao接口的包结构相同
第四个:映射配置文件的mapper标签namespace属性的取值必须是dao接口的全限定类名
第五个:映射配置文件的操作配置(select),id属性的取值必须是dao接口的方法名。
当我们遵从了第三,四,五点之后,我们在开发中就无需再用dao的实现类
MyBatis配置文件:
在这里插入图片描述
在这里插入图片描述
注意:在映射配置文件中一定要指定查询的返回结果要封装到哪个实体类中,指定resultType的。
注意:这里的namespace 和 select标签的 id 里的值,组合起来就能唯一定位到执行的方法

入门开始:
//1读取配置文件
InputStream in=Resources.getResourceAsStream(Mybatis的主配置文件);
//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 users=userDao.findAll();
for(User user:users){
System.out.println(user);
}
//6释放资源
session.close();
in.close();
注意:在读配置陪文件时,一定要用:
第一种:使用类加载器,但只能读取类路径下的配置文件
第二种:使用ServletContext对象的getRealPath();
SqlSessionFactoryBuilder builder=new SqlSessionFactoryBuilder();
在创建工厂时,mybatis使用了构建者模式,这里的builder就是构建者,
构建者模式:就相当于你要修房子的时候,你自己不用修,你找来了包工队,由这个包工队修,你只需要给钱。这个包工队就相当于是构建者。
构建者模式的优势:把对象的创建细节隐藏,使得使用者直接调用方法即可拿到对象
SqlSessionFactory factory=builder.build(in);
SqlSession session=factory.openSession();//这里使用了工厂模式生产了SqlSession
工厂模式的优势:解耦(降低类之间的依赖关系)

IuserDao userdao=session.getMapper(IUserDao.class);这里创建Dao接口实现类使用了代理模式,代理模式的优势:不修改源码的基础上对已有方法增强。
xml方式的总结:
第一步:读取配置文件
第二步:创建SqlSessionFactory工厂
第三步:创建SqlSession
第四步:创建Dao接口的代理对象
第五步:执行dao中的方法
第六步:释放资源
注意事项:
不要忘记在映射配置中告知mybatis要封装到哪个实体类中,配置的方式:指定实体类的全限定类名。

mybatis注解开发:
直接在dao层的接口的抽象方法上添加注解,例如:
public interface IUserDao{
@Select(“select * from user”)
List< User> findAll();

}

主配置文件,连接数据库的配置文件还是要有的但是要该映射标签里的resource属性,改为class属性指定被注解的dao全限定类名
注意:把IUserDao.xml移除,在dao接口的方法上使用@Select注解,并且指定SQL语句,同时需要在SqlMapConfig.xml中的mapper配置时,使用class属性指定dao接口的全限定类名。
例如:
< mapperr>
< mapper class=“com.mybatis.dao.IUserDao”
< /mapper>
源码分析:
mybatis在使用代理dao 的方式实现增删改查时做了什么事呢?
第一: 创建代理对象
第二:在代理对象中调用selectList
分析:
1 以下是连接数据库的信息,有了它就可以创建Connection对象在这里插入图片描述2 有了以下信息就有了映射配置信息:
在这里插入图片描述3 有了以下信息就有了执行的SQL语句,就可以获取PreparedStatement,同时此配置文件中还有封装的实体类的全限定类名
在这里插入图片描述读取以上三个配置文件,用到的技术就是解析XML的技术。此处用的事dom4j解析xml技术。
解析:
Select方法:
1 根据配置文件的信息创建connection对象:注册驱动 获取链接等
2 获取预处理对象PreparedStatement,此时需要SQL语句,从mapper中获取。相当于这句:conn.preparedStatement(sql);
3 执行查询:
ResultSet resultSet=preparedStatement.executeQuery();
4 遍历结果集用于封装
List< E> list=new ArrayList();
while(resultSet.next()){
E element=(E)Class.forName(配置的全限定类名).newInstance();(这里从mapper配置文件中的select中的resultType属性的值中获取返回值的类信息,利用反射实例化对象)
进行封装,把每个rs的内容添加到element中
我们的实体类属性和表中的列名是一致的。于是我们就可以把表的列名看成是实体类的属性名称,就可以通过反射的方式来根据名称获取每个属性,并把值赋进去。
把element加入到list中
list.add(element);
5 返回list
return list;
要想让上面的方法执行,我们需要给方法提供两个信息:
第一个:连接信息
第二个:映射信息
其中映射信息包含两个部分:
第一:执行的SQL语句
第二:封装结果的实体类的全限定类名
把这两个信息组合在一起定义成一个对象
由于存在多个实体类对象,所以这里需要使用Mapper集合来存放这些对象并加以区分
Mapper的Key存放的是,String类型的字符串,这个字符串是Mappers标签里的目标接口的全限定类型和映射文件中方法名的组合。
而这个Mapper的值,存放的就是Mapper对象:String sql和String domainClassPath.

4使用SqlSession创建Dao接口的代理对象
IUserDao userDao=session.getMapper(IUserDao.class);
//根据dao接口的字节码创建dao的代理对象
public < T > T getMapper(Class< T > daoInterfaceClass ){
/*类加载器:它使用的和被代理对象是相同的类加载器
代理对象实现的接口:和被代理对象实现实现相同的接口
如何代理:
它就是增强的方法,我们需要自己提供。此处是一个InvocationHandler的接口,我们需要写一个该接口的实现类在实现类中调用seletLise方法。
Proxy.newProxyInstance(类加载器,代理对象要实现的接口字节码数组,如何处理)
}
public void setMappers(Map< String,Mapper> mappers){
this.mappers.putAll(mappers);//此处需要使用追加的方式。
}

先来看自定义MyBatis都需要写哪些类和接口:
1 Resource 类:使用类加载器读取配置文件的类
此类中有一个方法:根据传入的参数,获取一个字节输入流
public static InputStream getResourceAsStream(String filePath)
2 SqlSessionFactoryBuilder类:用于创建一个SqlSessionFactory对象
此类中有一个返回值为SqlSessionFactory(接口)类型的方法:
下面这个方法根据参数的字节输入流来创建一个SqlSessionFactory工厂
public SqlSessionFactory builder(InputStream config){

}
3 这个SqlSessionFactory接口中有一个返回值为SqlSession的openSession()方法,这个方法用来打开一个新的SqlSession对象
SqlSession openSession();
4 这是一个Mybatis中和数据库交互的核心类,它里面可以创建dao接口的代理对象
public interface SqlSession{
这个getMapper()方法,就是根据参数创建一个代理对象
参数为:daoInterfaceClass dao的接口字节码
< T > T getMapper(Class daoInterfaceClass);
}
这个接口中还有一个close()方法用来释放资源:
void close();
5 这是一个mybatis的配置类:
public class Configuration{
private String driver;
private String url;
private String username;
private String password
private Map< String,Mapper> mappers=new HashMap< String,Mapper>();

相应的get set方法
注意此处的setMappers的方法一定要这样写:
 public void setMappers(Map< String,Mapper> mappers){
	this.mappers.putALL(mappers);//此处需要使用追加的方式。

}
public void setDriver(String driver)

}
6 Mapper类,用于封装执行的SQL语句和结果集类型的全限定类名
public class Mapper{
private String queryString;//SQL语句
private String resultType;//实体类的权限定类名
添加相应的get set 方法

}
下面来解读
解析MyBatis配置文件的工具类
它里面有一个方法专门用来解析配置文件中的mappers标签中的 reourse 或 class的值,那么到底是resource 还是 class呢?那么在这个工具类中采用了遍历所有的mapper来判断,如果是resource那就采用xml方式解析,否则就是class则采用注解,请看以下方法:
//取出mappers中的所有mapper标签,判断他们使用了resource还是class属性
List< Element > mapperElements=root.selectNodes("//mappers/mapper");
//遍历集合
for(Element mapperElement:mapperElement){
//判断mapperElement使用的是哪个属性
Attribute attribute=mapperElement.attribute(“resource”);
if(attribute!=null){
System.out.println(“使用的是xml”);
//表示有resource属性,用的是xml
//取出属性的值
String mapperpath=attribute.getValue();//获取属性的值"com/wb/do/IuserDao.xml"
Map< String,Mapper> mappers=loadMapperConfiguration(mapperpath);
//给configuration中的mappers赋值
}else{
System.out.println(“使用注解”);
//表示没有resource属性,用的是注解
//获取class属性的值
String doaClassPath=mapperElement.attributeValue(“class”);
根据daoClassPath获取封装的必要信息
Map< String,Mapper> mappers=LoadMapperAnnotation(daoClassPath);
//给configuration中的mapper赋值
cfg.setMappers(mappers);

}
}
//根据传入的参数,得到dao中所有的select注解标注的方法
根据方法名称和类名,以及方法上注解value属性的值,组成Mapper的必要信息
private static Map< String,Mapper> loadMapperAnnotation( String daoClassPath)throws Exception{
//定义返回值对象
Map< String,Mapper> mappers=new HashMap< String,Mapper>();
1//得到dao接口的字节码对象
Class daoClass=Class.forName(daoClassPath);
2//得到dao接口中的方法数组
Method[] methods=daoClass.getMethods();
3//遍历Method数组
for(Method method:methods){
//取出每一个方法,判断是否有select注解
boolean isAnnotated=method.isAnnotationPresent(Select.class);
if(isAnnotated){
//创建Mapper对象
Mapper mapper=new Mapper();
//取出注解的value属性值
Select selectAnno=method.getAnnotation(Select.class);//拿到Select注解这个对象
String queryString=selectAnno.value();//取出这个Select注解的value值
mapper.setQueryString(queryString);
//获取当前方法的返回值,还要求必须带有泛型信息
Type type=method.getGenericReturnType();//List< User>
//判断type是不是参数化的类型
if(type instanceof ParameterizedType){
//强转
ParameterizedType ptype=(ParameterizedType)type;
//得到参数化类型中的实际类型参数
Type[] types=ptype.getActualTypeArguments();
//取出第一个
Class domainClass=(Class)type[0];
//获取domainClass的类名
String resultType=domainClass.getName();
//给Mapper赋值
mapper.setResultType(resultType);
}
//组装key的信息
//获取方法的名称
String methodName=method.getName();
String className=method.getDeclaringClass().getName();//获取该方法发所属于的类的类名
String key=className+"."+methodName;
//给maps赋值
mappers.put(key,mapper);
}
}

return mappers
}
private static Map< String,Mapper> loadMapperAnnotation( String daoClassPath)throws Exception{

}
方法:根据传入的参数,解析xml,并且封装到Map中
@Param:mapperPath 映射配置文件的位置
@return:map中包含了获取的唯一标识(key是由dao的全限定类名和方法名组成)
以及执行所需的必要信息(value是一个Mapper对象,里面存放的是执行的SQL语句和要封装的实体类全限定类名)
private static Map< String,Mapper> loadMapperConfiguration(String,mapperPath) {
InputStream in=null;
try{
//定义返回值对象
Map< String,Mapper> mappers=new HashMap< String,Mapper>();
//根据路径路径获取字节输入流
in=Resources.getResourceAsStream(mapperPaht);
//根据字节输入流获取Document对象
SAXReader reader=new SAXReader();
Document document=reader.read(in);
//获取根节点
Element root=document.getRootElement();
//根据根节点的namespace属性取值
String namespace=root.attributeValue(“namespace”);//是组成map中key的部分
//获取所有的select节点
List< Element> selectElements=root.selectNode("//select");
//遍历select节点集合
for(Element selectElement:selectElements){
//取出id属性的值 组成map中key的部分
String id=selectElement.attributeValue(“id”);
//取出resultType属性的值 组成map中value的部分
String resultType=selectElement.attributeValue(“resultType”);
//取出文本内容 组成map中value的部分
String queryString=selectElement.getText();
//创建Value
Mapper mapper=new Mapper();
mapper.setQueryString(queryString);Sql语句
mapper.setResultType(resultType);//返回值类型的全限定类名
//把key和value存入mappers中
mappers.put(key,mapper);
}
return mappers;
}
}

下面来看SqlSessionBuilder类中的方法:
public SqlSessionFactory build(InputStream config){
Configuration cfg=XMLConfigBuilder.LoadConfiguration(config);
}

创建SqlSessionFactory接口的实现类:
public class DefaultSqlSessionFactory implements SqlSessionFactory{
private Configuration cfg;
public DefaultSqlSessionFactory(Configuration cfg){
this.cfg=cfg;
}
//用于创建一个新的操作数据库对象
public SqlSession openSession(){
return new DefaultSqlSession(cfg);
}

}
创建一个SqlSession接口的实现类DefaultSqlSession
public class DefaultSqlSession implements SqlSession{
private Configuration cfg;
private Connection connection;
public DefaultSqlSession(Configuration cfg){
this.cfg=cfg;
connection=DataSourceUtil.getConnection(cfg);
}
//用于创建代理对象的方法
public < T > getMapper(Class< T > daoInterfaceClass){
Proxy.newProxyInstance(daoInterfaceClass.getClassLoader(),new Class[]{daoInterfaceClass},new MapperProxy(cfg.getMappers(),connection))

}
//用于释放资源的方法
public void close(){
connection.close();
}

}
创建一个实现InvocationHandler接口的类,用来具体实现代理对象要增强的方法
public class MapperProxy implements InvocationHandler{
//这个map的key是全限定类名+方法名
private Map< String,Mapper> mapper;
private Connection conn;
public MapperProxy(Map< String,Mapper> mappers,Connection conn){
this.mappers=mappers;
this.conn=conn;
}
//用于对方法进行增强的,我们的增强其实就是调用selectList方法。
public Object invoke(Object proxy,Method method,Object[] args) throws Throwable{
//获取方法名
String methodName=method.getName();
//获取方法所在类的名称
String className=method.getDeclaringClass().getName();
//组合key
String key=className+"."+methodName;
//获取mappers中的Mapper对象
Mapper mapper=mapper.get(key);
//判断是否有mapper
if(mapper==null){
throw new IllegalArgumentException(“传入的参数有误”);
}
//使用工具类执行查询所有
return new Executor().selectList(mapper,conn);
}
}
由于以上类中需要用到DataSourceUtil,所以我们在下面创建一个DataSourceUtil的类,用于创建数据源的工具类
public class DataSourceUtil{
//用于获取一个连接
public static Connection getConnection(Configuration cfg){
try{
Class.forName(cfg.getDriver());
return DriverManager.getConnection(cfg.getUrl(),cfg.getUsername(),cfg.getPassword());
}
}
}

基于注解方式的解析
1 定义一个查询的注解 @Interface Select
@Retention(RetentionPolicy.RUNTIME);//该注解的生命周期
@Target(ElementType.METHOD);//该注解的作用域
public @interface Select{
//配置SQL语句的属性
String value();
}
MyBatis的增删改查实例:
增加数据到数据库中:
1在dao接口中添加增加数据的抽象方法
2在相应的mappers映射配置文件中添加配置信息例如:
//保存用户
< insert id="saveUser’’ parameterType="com.wb.model.User’’>注意:这里的parameterType里的值是SQL语句中values里面传入的参数字段所属的类的全类名
insert into user(username,address,sex,birthday)values(#(username),#(address),#(sex),#(birthday));
< /insert>
注意在测试类中测试保存方法时,要手动提交事务,才能成功将数据插入到数据库中
SqlSession.commit();
2更新操作:
1首先在dao接口中添加相应的抽象方法
2 配置更新的SQL
< update id=“updateUser” parameterType=“com.wb.model.User”>
update user set username=#{ username},address=#{ address},sex=#{ sex},birthday=#{ birthday} where id=#{ id}
< /update>
3 根据id删除用户
1 添加抽象方法: void deleteUser(Integer userId);
2 配SQL映射文件
< delete id="deleteUser’’ parameterType=“java.lang.Integer”>
delete from user where id=#{id} 注意当参数类型是一个基本类型或者基本类型的包装类的类型时,且当这个参数只有一个时,参数可以随便起名,相当于一个占位符。
< /delete>
模糊查询:
首先在dao接口中定义 List< User> findByName( String username);
2 在mapper映射文件中定义sql:
< select parameterType="String’’ resultType=“com.wb.model.User”>
select * from user where username like=#{name} 这种方式采用 PreparedStatement预处理的方式
另一种写法:
select * from user where username=like ‘%${value}%’ 注意这种方式这里必须写value,不能随便写。这种方式采用statement的拼接字符创的方式
< /select>
注意这里的sql语句中并没有提供%,所以在测试方法中需要我们自己提供%到参数中
如果是在自增长类型的表中插入数据,当想要拿到这个id的值时,我们可以使用这句SQL:select last_insert_id();
MyBatis
OGNL表达式:
Object Graphic Navigation Language(对象 图 导航 语言)
它是通过对象的取值方法来获取数据,在写法上把get给省略了。
比如:我们以前在获取用户的名称:
类中的写法:user.getUserName
OGNL表达式写法:user.userName
Mybatis之所以可以直接使用userName,是因为我们在parameterType中已经指明了所属类的名称。
假如现在有这样一个需求:
在查询到的数据库中,先把数据库中的数据封装到一个实体类中,这样的实体类有很多,现在我们需要把这些个实体类,也用一个类给他们封装起来,那么久需要在定义一个类把这些返回的类作为保存实体类的类的成员变量。那么在mapper配置文件中我们的参数就不能是普通的那样,这里就是一OGNL表达式的应用
< Select id=“findByName” parameterType="com.wb.dao.QueryVo’’ resultType=“com.wb.User”>
select * from user where username= like #{user.username}
</ select>
这种由多个对象组成查询条件,来实现查询在实际的开发中经常用到。
Mybatis的结果类型封装。
注意:如果在封装结果集到一个对象中,出现了数据库中的字段和JAVABean中的属性名不一致的情况我们有两种解决方案:
1 给数据库中的字段起别名,别名和JAVABean中的属性要一致
2 配置 查询结果的列名和实体类的属性名的对应关系
< resultMap id="userMap’’ type=“com.wb.model.user”>
//主键字段的对应

//非主键字段的对应
< result>< /result>
</ result>
< !–配置properties
可以在标签内部连接数据库的信息,也可以通过引用外部配置文件信息
resource 属性:常用的
用于指定配置文件的位置,是按照相关类路径的写法来写,并且必须存在于类路径下。
url属性:
是要求按照url的写法来写地址
URL:Uniform Resource Locator 统一资源定位符,它可以唯一标识一个资源的位置
它的写法: http://localhost:8080/mybatisserver/demoIservlet
URI:统一资源定位符,它是在应用中可以唯一定位一个资源的。
使用typeAliases配置别名,它只能配置domain中类的别名
例如:
< typeAlias>
< typeAlias type=“com.wb.do.User” alias=“user”></ typeAlias> 配置了别名不区分大小写。
< /typeAlias>
用于指定要配置别名的包,当指定之后,该包下的实体类都会注册别名,并且类名就是别名,不再区分大小写。
< package name=“com.wb.dao”>< /package>

< mappers>

< package> 标签是用于指定dao接口所在的包,当指定了之后就不需要在写mapper以及resource或者class了。
< package name=“com.wb.do”></ package>
</ mappers>

Mybatis连接池: 连接池就是用于存储连接的一个容器,容器其实就是一个集合对象,该集合必须是线程安全的,不能两个线程拿到同一个连接,该集合还必须实现队列的特性:先进先出。

mybatis连接池提供了3种方式的配置:
配置的位置:
主配置文件SqlMapConfig.xml中的dataSource标签,type属性就是表示采用何种连接池方式。
type属性的取值:
POOLED 采用传统的javax.sql.DataSource规范中的连接池,mybatis中有针对规范的实现
UNPOOLEN:采用传统的获取连接的方式,虽然也实现Javax.sql.DataSource接口,但是并没有使用池的思想。
JNDI 采用服务器提供的JNDI技术实现,来获取DataSource对象,不同的服务器拿到的DataSource是不一样的。注意:如果不是web或者maven的war工程,是不能使用的。tomcat服务器,采用连接池就是dbcp连接池。

Mabatis中的事物
什么是事物
事物的四大特性ACID
不考虑隔离性会产生的3个问题
解决办法:四种隔离级别。
设置Mybatis自动提交事物的方法:
openSession(boolean autoCommit);
Mybatis的动态SQL:
需求:根据查询的条件,查询条件有可能是用用户名,有可能是性别,也有可能死地址,还有可能是都有。
那么在写配置时的时候我们就需要这样写:
< 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> } 等价于下面代码:
select * from user
< where>

and username=#{userName}
</ if>
< if test=“userSex !=null”>
and sex=#{userSex}

            < /if>

    </where>

带in的子查询:
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>
mabatis中的多表查询:
实例:用户和账户
一个用户可以有多个账户
一个账户只能属于一个用户(多个账户也可以属于同一个用户)
步骤:
1 建立两张表:用户表,账户表
让用户表和账户表之间具备一对多的关系:需要使用外键在账户表中添加
2 建立两个实体类::用户实体类和账户实体类
让用户和账户的实体类之间能体现出一对多的关系
3 建立两个配置文件:
用户配置文件
账户配置文件
4 实现配置:
当我们查询用户时,可以同时得到用户下所包含的所有账户信息
当我们查询账户时,可以同时得到账户的所属用户信息。
mybatis中完成一对一操作-建立实体类关系的方式
< resultMap id="userMap’’ type=“com.wb.model.user”>
//主键字段的对应

//非主键字段的对应
< result>< /result>
</ result>
< 定义封装account和user的resultMap>
< resultMap id=“accountUserMap” type=“account”>
< id property=“id” colum=“uid”>< /id>
< result property=“uid” column=“uid”>< result>
< result property=“money” column=“money”>< /result>
< 一对一的关系映射,配置封装user的内容>
< association property=“user” column=“uid” javaType="com.wb.dao.user>
< id property=“id” column=“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>
< /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>
一对多查询操作:
需求:查询所有用户,同时获取到用户下所有账户的信息
在IuserDao接口中定义方法:List< User> findALL();
同时要在实体类中加上:
一对多关系映射,主表实体应该包含从表实体的集合引用。
private List< Account> accounts:
在IuserDaos.xml中:
定义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”>(这个ofType表示集合中元素的类型。)
< id column=“id” property=“id”>< /id>
< result column=“uid” property=“uid”>< /result>
< result column=“money” property=“money”> < /result>
< /collections>
< /resultMap>

< – 查询所有–>
< select id=“findAll” resultMap=“userAccountMap”>
select * from user u left outer join account a on u.id=a.uid
< /select>

MyBatis维护多对多
实例:用户和角色
一个用户可以有多个角色,一个角色可以赋予多个用户
1 建立两张表:用户表,角色表
让用户表和角色表具有多对多的关系,需要使用中间表,中间表中包含各自的主键,在中间表中是外键。
2 建立两个实体类:用户实体类和角色实体类
让用户和角色的实体类能体现出来多对多的关系,各自包含对方一个集合引用。
3 建立两个配置文件
用户配置文件 角色的配置文件
4 实现配置:
当我们查询用户时,可以同时得到用户所包含的角色信息
当我们查询时,可以同时得到角色的所属用户信息。
1 在两张表的实体类中分别定义一个多对多的关系映射,就是分别在每个实体表中定义另一张表的集合对象引用。
例如:
public class Role implements Serializable{

private List< Users> users;
//生成相应的get和set方法.

}
public class Users implements Serializable{

private List< Role > role;
//生成相应的get 和 set方法
}

配置IRoleDao.xml文件:
mapper namespace=“com.wb.dao.IRoleDao”
//定义role表的ResultMap
< resultMap id=“roleMap” type=“role”>
< id property=“roleId” column=“id”></ id>
< result property=“roleName” column=“role_name”>< /result>
< result property=“roleDesc” column=“role_desc”>< /result>
< collection proper=“users” ofType=“com.wb.dao.user”>
< id column=“id” property=“id”>< /id>
< result column=“username” property=“username”>< /result>
< result column=“address” property=“address”>< /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>
JNDI数据源:
JNDI数据的存放格式就好比windows系统系下的注册表格式:
在这里插入图片描述
1 Mybatis 中的延迟加载
思考:在一对多中,当我们有一个用户,它有100个账户?
在查询用户的时候,我们不需要把这个用户的账户查出来因为会造成内存耗费过大。应该是,什么时候使用,什么时候查询出来。
在查询账户的时候,要不要把关联的用户查出来?
在查询账户时,账户的所属用户信息应该是随着账户查询时一起查询出来。
什么是延迟加载:
在真正使用数据时才发起查询,不用的时候不查询。按需加载(懒加载)
什么是立即加载
不管用不用,只要一调用方法,马上发起查询
在对应的四种关系中:一对多, 多对一,一对一,多对多
一对多,多对多:通常情况下我们都是采用延迟加载: 因为比如说有这么一个需求,当用户查询他的基本信息,这是加入用户表,对应多个角色表,就是我们所说的一对多的关系,那么这个时候如果,才用立即查询,由于用户的角色有很多,那么就会造成内存的浪费,所以我们采用延迟加载,有需要的时候在加载。

多对一,一对一:通常情况下我们都是采用立即加载,

实例:延迟加载:
<mapper namespace=“com.wb.do.IAccountDao”
< !–定义封装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属性指定的内容,查询用户的唯一标识,因为这时候,是延迟加载,就不能之使用 < association> 这个标签来立即封装了.,就需要在association这个标签上加上属性select=“要封装的表对应的mapper 的查询方法名”—>
< assocation property=“user” column=“uid” javaType=“user” select="com.wb.dao.IuserDao.findById></ association>

因为是延迟查询所以我们不用封装Map,只需要下下方封装user就可以了
< select id=“findById” parameterType="INT’’ resultType=“user”>
select * from user where id=#{uid}
< select>
< /mapper>
重要的一步,需要在mybatis的主配置文件中开启延迟查询:
在< configuration> 的标签下配置:
< settings>
< setting name=“lazyLoadingEnabled” value=“true” /> < !-- 当开启此配置后延迟加载的全局开关。当开启时,所有关联对象都会延迟加载。 特定关联关系中可通过设置 fetchType 属性来覆盖该项的开关状态。–>
< setting name=“aggressiveLazyLoading” value=“false”></ setting> <! – 当开启时,任何方法的调用都会加载该对象的所有属性。 否则,每个属性会按需加载(参考 lazyLoadTriggerMethods)。 -->
</ settings>

mybatis一对多实现延迟加载:
首先需要在IusereMapper.xml配置文件中需要这样配置:
< !–定义User的resultMap–>
< resultMap id=“userAccountMap” type=“user”>
< id property=“id” column=“id”>< /id>
< result property=“username” column=“username”>< /result>


<!-- 配置user对象中account集合的映射— >
< collection property=“accounts" ofType=“account” select=“com.wb.dao.IAccountDao.findAccountByUid” column=“id”>
< /resultMap>
< !–查询所有>
< select id=“findAll” resultMap=“userAccountMap”>
这时需要在IRoleDao的接口中添加方法:
List< Role> findAccountByUid(Integer uid);//根据用户id查询账户信息.

之后需要在IRoleDao.xml中添加配置:
< resultMap id=“RoleRuserMap” type=“com.wb.Entity.Role”>
< id>< /id>
< result>< /result>

< association property=“user” column=“uid” javaType=“com.wb.Entity.user” select=“com.wb.dao.IuserDao.findAllUserByID”>< /association>
< resultMap>
< !–根据用户id查询账户列表 -->
< select id=“findAccountByUid” resultType=“account”>
select * from account
< /select>

MyBatis中的缓存
什么是缓存
存在于内存中的临时数据。
为什么使用缓存
减少和数据库的交互次数,提高执行效率。
什么样的数据能用缓存,什么样的数据不能使用
适用于缓存:
经常查询并且不经常改变的
数据的正确与否对最终结果影响不大的
不适用于缓存:
经常改变的数据
数据的正确与否对最终结果影响很大的
例如:商品的库存,银行的汇率,股市的牌价。
mybatis中的一级缓存:
一级缓存:
它指的是,当我们执行一个查询语句时,查询的结果会同时存入到SqlSession为我们提供一块区域中。
该区域的结构是一个Map。当我们再次查询同样的数据,mybatis会先去sqlsession中查询是否有,有的话就直接拿出来用。
当SqlSession对象消失时,mybatis的一级缓存也就消失了。
sqlSession.clearCache();//此方法也可以清空缓存.
一级缓存是SqlSession范围的缓存,当调用SqlSession的修改,添加,删除,commit(),close()等方法时,就会清空一级缓存.
Mybatis的二级缓存:它指的是Mybatis中SqlSessionFactory对象的缓存,由同一个SqlSessionFactory对象创建的SqlSession共享其缓存。
二级缓存中存放的是数据而不是对象。
二级缓存的使用步骤:
第一步:让Mybatis框架支持二级缓存(在SqlMapConfig.xml)中配置
< settings>
< setting name=“cacheEnabled” value=“true” />
< /settings>
第二步:让当前的映射文件支持二级缓存(在IUserDao.xml中配置)
开启user支持二级缓存
< cache/>
第三步:让当前的操作支持二级缓存(在select标签中配置)
< select id=“findById” parameterType=“INT” resultType=“user” useCache=“true”>
MyBatis中的注解开发:
首先说明:
在MyBatis中针对,CRUD(增删该查)一共有四个注解
1 @Select 2 Insert 3 Update 4 Delete
例如在IUserDao中有这么一个需求,要查询用户的所有信息那么:
@Select("select * from user’’) 这里的value属性写查询语句,当只有一个的时候,就可以不用写value.
@Select(“select * from user”)
List< User> findAll();
MyBatis中要么全用注解开发,要么全用xml方式开发,不能混合使用,会报错。
注意:同时需要javaBean中的实体类实现序列化接口。

在这里插入图片描述

MyBatis中的多表:
首先了解一下:MyBatis中的一个Results注解,这个注解就是将mysql中的列和实体类的属性一一映射的配置:
@Select(“select * from user”)
@Results(id=“userMap” value={
@Result(id=true,column=“id”,property=“userId”),这个代表主键,当加上id=true代表这个属性是主键。
@Result(column=“username”,property=“userName”),
@Result(column=“address”,property=“userAddress”),
@Result(column=“sex”,property=“userSex”),
@Result(column=“birthday”,property=“birthday”),
})
List< User> findAll();
以上的id=userMap 唯一定位了那个Result,实现了可以复用。

一对多的注解配置:
例如:
public interface IAccountDao{
/*
查询所有账户,并且获取每个账户所属的用户信息
*/
@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.wb.do.IuserDao.finById”,fetchType=FetchType.EAGER))
})
List< Account > findAll();
}

一对多的多表查询:
@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”),
@Restult(column=“sex”,propety=“userSex”),
@Result(property=“accounts”,column=“id”,many=@Many(select=“com.wb.do.IAccountDao.findAccountByUid”,fetchType=FetchType.LAZY))

})
在MyBatis中在一对多查询多的时候通常采用延迟加载,在一对一查询一通常情况下采用立即加载。

MyBatis缓存的注解配置。
在全局配置文件中setting上 cacheEnable=true;
在dao的接口上注解上:@CacheNamespace(blocking=true)
public interface IUserDao{


}

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

qq_43557743

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值