列明和属性名不一致问题
在MyBatis中,是会自动映射的,当实体类的属性和数据表的列明不一致时,封装后的属性值会为null,为了解决这种问题,有以下两种方式。
一、在查询时,给字段起别名,适用于字段比较少的情况下,字段太多编写太过繁琐。
二、通过resultMap标签。
<!-- resultMap最终还是要将结果映射到pojo上,type就是指定映射到哪一个pojo -->
<!-- id:设置ResultMap的id -->
<resultMap type="order" id="orderResultMap">
<!-- 定义主键 ,非常重要。如果是多个字段,则定义多个id -->
<!-- property:主键在pojo中的属性名 -->
<!-- column:主键在数据库中的列名 -->
<id property="id" column="id" />
多表关联查询_业务装配式
优点:sql语句简单
缺点:所有业务逻辑装配都需要自己动手写。
@Override
public List<Student> selAll() {
SqlSession session = MyBatisUtil.getSession();
//学生mapper
StudentMapper smapper = session.getMapper(StudentMapper.class);
//班级mapper
ClazzMapper cmapper = session.getMapper(ClazzMapper.class);
List<Student> list = smapper.selAll();
for (Student student : list) {
//学生表设置班级信息(班级id为学生的cid信息)
student.setClazz(cmapper.selById(student.getCid()));
}
session.close();
return list;
}
<mapper namespace="com.sxt.mapper.ClazzMapper">
<select id="selById" resultType="Clazz" parameterType="int">
select * from t_class where id = #{0}
</select>
</mapper>
<mapper namespace="com.sxt.mapper.StudentMapper">
<select id="selAll" resultType="student" >
select * from t_student
</select>
</mapper>
多表关联查询_resultMap_单个对象_N+1方式实现
也就是多对1的关系,在创建实体类的时候参照数据库,可以这样理解,学生与班级之间的关系。
那么在创建实体类,将需要操作的表以对象的方式存放到该实体类中。
在配置映射文件时,需要该为手动映射,也就是将查询标签中的resultType改为resultMap。
并在外部添加resultMap标签,类型为pojo的全限定类名,id与查询标签中的resultMapid一致。
property:映射到列结果的字段或属性。
column:来自数据库的类名,或重命名的列标签。
<association property="要关联的类的名字" javaType="返回值类型,因为定义的类就是clazz,那么返回的也是clazz">
注解开发
注解是用来描述代码的代码,使用注解一般用于简化配置文件,配置文件能实现的,注解一般也能实现,但是注解有时候也会更麻烦。
(例如,动态sql。)
常用的有:
@Test(用于描述方法进行junit测试)
@Override(用于描述方法的重写)
@Param(用于描述属性的名称)
@Select
@Insert
@Update
@Delete
注意:
属性的设定方式:属性名=属性值。
属性值的类型:
基本类型和String,可以直接使用双引号的形式。
数组类型,name={值1,值2...};如果数组元素只有1个,可以省略大括号。
对象类型,name=@对象名(属性)
如果属性是该注解的默认属性 ,而且该注解只配置这一个属性,可以将属性名省略不写,只写属性值。
MyBatis运行原理
先创建的是sql工厂对象,由工厂对象创建的sqlSession。
Resources类下面的getResourceAsStream方法,通过点击来查看源码。
首先,进入的就是一个InputStream类型的静态方法,返回值是getResourceAsStream,通过点击返回值继续查找对应方法,如下图1,2,3。
在第3图中可以看到返回值中的参数由1个是getClassLoaders(classLoader),点击后进入到ClassLoaderWrapper.class类中。
继续点击返回值为getResourceAsStream,找到下一个getResourceAsStream方法,在方法中由InputStream returnValue = cl.getResourceAsStream(resource);,继续点击getResourceAsStream(resource),
之后跳转到ClassLoader类中(java.lang.ClassLoader,原生包)。其实Resources调用的getResourceAsStream本质上还是走了java.lang包下的类加载器的getResourceAsStream。
工厂接口。
我们先创建的是一个工厂对象,通过工厂对象创建的是SqlSession,那么怎么获得工厂对象呢,可以通过new SqlSessionFactoryBuilder类。
SqlSessionFactoryBuilder().builder(),点击builder方法往下查看,可以看到一个方法为类型为SqlSessionFactory,方法名为build
的方法。方法内部有一个XMLConfigBuilder的对象(配置文件),也就是说配置文件需要通过这个对象进行解析。在解析对象创建后方法内部return build(parser.parse());
又调用了一个parse()方法,通过点击后可以看到这个方法内部对文件进行了解析,最后返回的是configuration,也就是说这个configuration中存储了解析完的配置信息。
出来后点击build查看,该方法内部又new了一个DefaultSqlSessionFactory(config);
到这一步,可以理解为解析完成后的配置信息,作为参数传到了DefaultSqlSessionFactory()构造器里面,创建了DefaultSqlSessionFactory()对象,也就是SqlSessionFactory接口的实现类。
到了这一步,也就说明了如果需要SqlSessionFactory,那么需要new一个DefaultSqlSessionFactory对象。
那么新的问题又来了,如果需要SqlSessionFactory工厂对象,为什么不直接new一个DefaultSqlSessionFactory。
这是因为DefaultSqlSessionFactory的形参内需要传一个解析过的配置文件,也就是Configuration。
但是Configuration又是需要先new一个XMLConfigBuilder来创建一个解析器对象,再来解析配置文件,解析完成后再把解析结果封装成一个Configuration对象。
到此为止,工厂对象创建成功。
接下来通过工厂对象调用openSession(),通过openSession()把session创建成功。但是sqlsession是一个接口,还是需要实现类进行创建。
继续查看源码,也就是工厂对象下的openSession方法,通过查看发现,内部其实是一个数据源
再往下继续看
最后通过return new DefaultSqlSession对象给了SqlSession,这样,SqlSession也得到了。那么我们就可以执行下一步的操作了。