框架概述
1、三层架构
-
三层架构包含的三层:
- 界面层(User Interface layer)
- 业务逻辑层(Business Logic Layer)
- 数据访问层(Data access layer)
-
三层的职责
- 界面层(表示层,视图层):主要功能是接受用户的数据,显示请求的处理结果。使用 web 页面和用户交互,手机 app也就是表示层的,用户在app中操作,业务逻辑在服务器端处理。
- 业务逻辑层:接收表示传递过来的数据,检查数据,计算业务逻辑,调用数据访问层获取数据。
- 数据访问层:与数据库打交道。主要实现对数据的增、删、改、查。将存储在数据库中的数据提交给业务层,同时将业务层处理的数据保存到数据库.
-
三层对应的包
- 界面层: controller包(servlet)
- 业务逻辑层:service包(xxxService)
- 薮据访问层:dao包( xxxDao类)
-
三层中类的交互
- 用户使用界面层–>业务逻辑层—>数据访问层(持久层)–>数据库(mysql)
-
三层对应的处理框架
- 界面层—servlet—springavc(框架)
- 业务逻辑层—service类–spring(框架)
- 薮据访问层—dao类–mybatis(框架)
2、什么是框架
- 框架(Framework)是整个或部分系统的可重用设计,表现为一组抽象构件及构件实例间交互的方法;
- 另一种定义认为,框架是可被应用开发者定制的应用骨架。
- 前者是从应用方面而后者是从目的方面给出的定义。
- 简而言之,框架其实就是某种应用的半成品,就是一组组件,供你选用完成你自己的系统。简单说就是使用别人搭好的舞台,你来做表演。而且,框架一般是成熟的,不断升级的软件。
3、框架要解决的问题
- 框架要解决的最重要的一个问题是技术整合的问题,在J2EE的 框架中,有着各种各样的技术,不同的软件企业需要从J2EE中选择不同的技术,这就使得软件企业最终的应用依赖于这些技术,技术自身的复杂性和技术的风险性将会直接对应用造成冲击。而应用是软件企业的核心,是竞争力的关键所在,因此应该将应用自身的设计和具体的实现技术解耦。这样,软件企业的研发将集中在应用的设计上,而不是具体的技术实现,技术实现是应用的底层支撑,它不应该直接对应用产生影响。
4、软件开发的分层重要性
- 框架的重要性在于它实现了部分功能,并且能够很好的将低层应用平台和高层业务逻辑进行了缓和。为了实现软件工程中的“高内聚、低耦合”。把问题划分开来各个解决,易于控制,易于延展,易于分配资源。我们常见的MVC软件设计思想就是很好的分层思想。
Mybatis
一、Mybatis的概述
1、MyBatis框架概述
- mybatis是一个优秀的基于java的持久层框架,它内部封装了jdbc,使开发者只需要关注sql语句本身,而不需要花费精力去处理加载驱动、创建连接、创建statement等繁杂的过程。
- mybatis通过xml或注解的方式将要执行的各种statement配置起来,并通过java对象和statement中sql的动态参数进行映射生成最终执行的sql语句,最后由mybatis框架执行sql并将结果映射为java对象并返回。
2、JDBC的缺陷
public static void main(String[] args) {
Connection connection = null;
PreparedStatement preparedStatement = null;
ResultSet resultSet = null;
try {
Class.forName("com.mysql.jdbc.Driver");
connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/mybatis?characterEncoding=utf-8","root", "root");
String sql = "select * from user where username = ?";
statement preparedStatement = connection.prepareStatement(sql);
preparedStatement.setString(1, "王五");
resultSet = preparedStatement.executeQuery();
while(resultSet.next()){
System.out.println(resultSet.getString("id")+" "+resultSet.getString("username"));
}
} catch (Exception e) {
e.printStackTrace();
}finally{
if(resultSet!=null){
try {
resultSet.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(preparedStatement!=null){
try {
preparedStatement.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(connection!=null){
try {
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
从上述代码可以看出:
- 数据库链接创建、释放频繁造成系统资源浪费从而影响系统性能,如果使用数据库链接池可解决此问题。
- Sql语句在代码中硬编码,造成代码不易维护,实际应用sql变化的可能较大,sql变动需要改变java代码。
- 使用preparedStatement向占有位符号传参数存在硬编码,因为sql语句的where条件不一定,可能多也可能少,修改sql还要修改代码,系统不易维护。
- 对结果集解析存在硬编码(查询列名),sql变化导致解析代码变化,系统不易维护,如果能将数据库记录封装成pojo对象解析比较方便。
简单地说就是:
- 代码比较多,开发效率低
- 需要关注Connection ,Statement, ResultSet对象创建和销毁
- 对 ResultSet查询的结果,需要自己封装为List
- 重复的代码比较多些
- 业务代码和数据库的操作混在一起
3、Mybatis解决的主要问题
- mybatis提供了哪些功能:
- 提供了创建connection ,statement,Resultset的能力
- 提供了执行sql语句的能力
- 提供了循环sql,把sql的结果转为java对象,List集合的能力
- 提供了关闭资源的能力,不用你关闭connection,statement,Resultset
- 总结:
- 减轻使用JDBC的复杂性,不用编写重复的创建Connetion , Statement;不用编写关闭资源代码。直接使用java对象,表示结果数据。让开发者专注 SQL的处理。其他分心的工作由 MyBatis 代劳。
二、Mybatis入门
1、构建Mybatis项目
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>x.x.x</version>
</dependency>
- 入门案例使用Maven来构建项目,所以需要有Maven相关知识的基础
- 关于如何创建Maven工程不是这里的重点,在学习Maven工程时会有教程
入门案例
- 在Idea中新建一个Maven工程
- 在pom.xml中导入依赖坐标
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.5</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.22</version>
</dependency>
CREATE DATABASE `student`;
USE `student`;
DROP TABLE IF EXISTS `student`;
CREATE TABLE `student` (
`id` int NOT NULL,
`name` varchar(20) DEFAULT NULL,
`email` varchar(50) DEFAULT NULL,
`age` int DEFAULT NULL,
PRIMARY KEY (`id`)
)
insert into `student`(`id`,`name`,`email`,`age`) values
(1001,'张三','zhangsan@qq.com',20);
insert into `student`(`id`,`name`,`email`,`age`) values
(1002,'李四','lisi@qq.com',18);
- 创建实体类,student
package com.bin.domain;
public class Student {
private Integer id;
private String name;
private String email;
private Integer age;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
@Override
public String toString() {
return "Student{" +
"id=" + id +
", name='" + name + '\'' +
", email='" + email + '\'' +
", age=" + age +
'}';
}
}
- 创建持久层的dao接口,定义操作数据库的方法
package com.bin.dao;
import com.bin.domain.Student;
import java.util.List;
public interface StudentDao {
List<Student> findAll();
}
- 创建一个mybatis使用的配置文件, 叫做sql映射文件:写sql语句的。一般一个表一个sql映射文件。
- 这个文件的位置并不是固定的,而是我为了文件管理的方便放在resource文件夹下了,注意resource一定要标识为资源文件夹
<?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.bin.dao.StudentDao">
<select id="findAll" resultType="com.bin.domain.Student">
select * from Student
</select>
</mapper>
- 创建mybatis的主配置文件:
一个项目就一个主配置文件。 主配置文件提供了数据库的连接信息和sql映射文件的位置信息
<?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">
<configuration>
<environments default="mysql">
<environment id="mysql">
<transactionManager type="JDBC" />
<dataSource type="POOLED">
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql:///student?characterEncoding=UTF-8&serverTimezone=UTC"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="dao/StudentDao.xml"/>
</mappers>
</configuration>
- 编写一个测试类,对其进行测试
import com.bin.domain.Student;
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.Test;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
public class test {
@Test
public void testFindAll() throws IOException {
InputStream inputStream = Resources.getResourceAsStream("SqlMapConfig.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
String path = "com.bin.dao.StudentDao.findAll";
List<Student> students = sqlSession.selectList(path);
for (Student s :students) {
System.out.println(s);
}
inputStream.close();
sqlSession.close();
}
}
2、插入操作
- 在主配置文件中配置日志,方便我们更好地对我们的操作进行解读
<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
package com.bin.dao;
import com.bin.domain.Student;
import java.util.List;
public interface StudentDao {
List<Student> findAll();
void insert(Student student);
}
<insert id="insert">
insert into Student(id,name,email,age) values (#{id},#{name},#{email},#{age})
</insert>
import com.bin.domain.Student;
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.IOException;
import java.io.InputStream;
import java.util.List;
public class test {
InputStream inputStream;
SqlSessionFactory sqlSessionFactory;
SqlSession sqlSession;
@Before
public void init() throws IOException {
inputStream = Resources.getResourceAsStream("SqlMapConfig.xml");
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
sqlSession = sqlSessionFactory.openSession();
}
@After
public void destroy() throws IOException {
sqlSession.commit();
inputStream.close();
sqlSession.close();
}
@Test
public void testFindAll(){
String path = "com.bin.dao.StudentDao.findAll";
List<Student> students = sqlSession.selectList(path);
for (Student s :students) {
System.out.println(s);
}
}
@Test
public void testInsert(){
Student student = new Student();
student.setId(1003);
student.setName("王五");
student.setEmail("wangwu@qq.com");
student.setAge(20);
String path = "com.bin.dao.StudentDao.insert";
sqlSession.insert(path,student);
}
}
3、更新操作
void update(Student student);
<update id="update">
update Student set name = #{name},email = #{email},age = #{age} where id = #{id}
</update>
@Test
public void testUpdate(){
Student student = new Student();
student.setId(1003);
student.setName("李五");
student.setEmail("liwu@qq.com");
student.setAge(20);
studentDao.update(student);
}
4、删除操作
void delete(Integer id);
<delete id="delete">
delete from Student where id = #{id}
</delete>
@Test
public void testDelete(){
studentDao.delete(1003);
}
5、主要的类的介绍
- Resources: mybatis中的一个类,负贲读取主配置文件
- InputStream in=Resources.getResourceAsStream(“SqlMapConfig.xml”);
- SqlSessionFactoryBuilder :创建SqlSessionFactory对象,
- SqlSessionFactory Factory = new SqlSessionFactoryBuilder().build(inputStream);
- SqlSessionFactory :重量级对象,程序创建一个对象耗时比较长,使用资源比较多。在整个项目中,有一个就够用了.
- SqlSessionFactory:接口,接口实现类:DefaultSqlSessionFactory
- SqlSessionFactory作用:获取SqlSession对象
SqlSession session = sqlSessionFactory.openSession(); openSession ()方法说明:
- openSession() :无参数的,获取是非自动提交事务的SqlSession对象
- openSession(boolean): openSession(true)获取自动提交事务的SqlSession. openSession (false)非自动提交事务的SqlSession对象
- SqlSession :
- SqlSession接口︰定义了操作数据的方法
- 例如SelectOne() ,SelectList() ,insert() ,update () , delete() , commit() , rollback()
- SqlSession接口的实现类DefaultSqlSession.
- 使用要求: sqlSession对象不是线程安全的,需要在方法内部使用,在执行sql语句之前,使用openSession()获取SqlSession在执行完sql语句后,需要关闭它,执行sqlSession.close().这样能保证他的使用是线程安全的。
三、Mybatis的Dao代理
1、动态代理getMapper
- 通过上面的代码我们可以发现,其实StudentDao接口我们一直都是没有使用的,我们是直接通过SqlSession的方法直接使用了mapper配置文件来进行查询与插入操作的。
- 然而我们在实际的开发时,包括最开始使用Servlet开发时,按照三层架构的思想来说,我们是通过创建对应接口的实现类,通过在方法中定义相应的Sql语句来进行相应的查询和插入操作的
- 通过学习了Mybatis入门案例后,我们知道,Sql语句可以将其抽取出来放在配置文件中,那么此时的实现类将不再是最开始这样写了。
- 借助在测试类test介绍的主要的相关类,我们就可以将获取这些类的方法封装成为一个工具类,通过工具类获取SqlSession对象,在Dao的实现类中定义相应的方法通过获取相应的SqlSession对象调用其方法执行配置文件中的sql语句。
- 此时在测试类中,我们就可以通过创建对应的实现了对象,通过这个对象调用实现类中的方法执行相应的操作了
- 但是,在此时,通过分析Dao的实现类,我们发现这样的操作让仍旧是存在这很多重复的工作的,比如每个方法都要写获取对应的SqlSession对象这一部分,除了最核心的执行部分不相同外,其他的操作基本大同小异,所以,这些工作应该是交给框架来做的
- SqlSession中的getMapper方法就帮我们做了这些工作了
Student studentDao = sqlSession.getMapper(StudentDao.class);
- List<student> studentList = dao. findAll();
- 调用
- dao对象,类型是studentDao,全限定名称是:com. bin.dao.studentDao全限定名称和namespace 是一样的。
- 方法名称,findAll ,这个方法就是mapper文件中的 id值 findAll
- 通过dao中方法的返回值也可以确定MyBatis要调用的sqlsession的方法
- 如果返回值是List,调用的是SqlSession.selectList()方法。
- 如果返回值int ,或是非list的,看mapper文件中的标签是<insert> , <update>就会调用SqlSession的insert , update等方法
- mybatis的动态代理:
- mybatis根据dao的方法调用,获取执行sqL语句的信息。
- mybatis根据你的dao接口,创建出一个dao接口的实现类,并创建这个类的对象。完成sqLSession调用方法,访问数据库。
2、parameterType配置参数
- SQL语句传参,使用标签的parameterType属性来设定。该属性的取值可以是基本类型,引用类型(例如:String类型),还可以是实体类类型(POJO类)。同时也可以使用实体类的包装类。
- parameterType :写在mapper文件中的一个属性。表示dao接口中方法的参数的数据类型。
- 例如studentDao接口
public student findById(Integer id)
1)传入一个简单参数
- 一个简单类型的参数:
- 简单类型:mybatis把java的基本数据类型和string都叫简单类型。
- 在mapper文件获取简单类型的一个参数的值,使用#{任意字符}
- 在StudentDao增加一个findById的方法
package com.bin.dao;
import com.bin.domain.Student;
import java.util.List;
public interface StudentDao {
List<Student> findAll();
void insert(Student student);
Student findById(Integer id);
}
<?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.bin.dao.StudentDao">
<select id="findAll" resultType="com.bin.domain.Student">
select * from Student
</select>
<insert id="insert">
insert into Student(id,name,email,age) values (#{id},#{name},#{email},#{age})
</insert>
<select id="findById" parameterType="java.lang.Integer" resultType="com.bin.domain.Student">
select * from Student where id = #{id}
</select>
</mapper>
- 注意事项,关于paramteType的别名
- 基本类型和String我们可以直接写类型名称,也可以使用包名.类名的方式,例如:java.lang.String。
- 实体类类型,目前我们只能使用全限定类名。 究其原因,是mybaits在加载时已经把常用的数据类型注册了别名,从而我们在使用时可以不写包名,而我们的是实体类并没有注册别名,所以必须写全限定类名。
- 在Mybatis文档中可以查询得到可以写别名的类型
- 在测试类中写相应的测试方法
@Test
public void testFindById(){
System.out.println(studentDao.findById(1001));
}
- 注意: parameterType不是强制的,mybatis通过反射机制能够发现接口参数的数类型。所以可以没有。一般我们也不写。
2)传入多个参数-使用@Param
- 当Dao接口方法多个参数,需要通过名称使用参数。在方法形参前面加入@Param(“自定义参数名"),mapper文件使用#{自定义参数名}。
- Dao文件
- List<Student> selectStudent(@Param(“personName”) String name ) { …}
- mapper文件
- select* from student where name = #{ personName}
- 在StudentDao中添加selectStudent方法
List<Student> selectMultiParam(@Param("sid") Integer id,
@Param("sage") Integer age);
}
<select id="selectMultiParam" resultType="com.bin.domain.Student">
select * from Student where id > #{sid} or age = #{sage}
</select>
@Test
public void testSelectStudent(){
List<Student> lists = studentDao.selectMultiParam(1002,20);
for (Student student : lists){
System.out.println(student);
}
}
}
3)传入多个参数-使用对象
- 使用java对象传递参数,java对象的属性值就是sql需要的参数值。每一个属性就是一个参数。
- 语法格式:#{ property,javaType=java中数据类型名,jdbcType=数据类型名称}
- javaType, jdbcType 的类型MyBatis可以检测出来,一般不需要设置。
- 常用格式#{ property }
- 在StudentDao中加入对应的方法
List<Student> selectMultiObject(Student student);
<select id="selectMultiObject" resultType="com.bin.domain.Student">
select * from Student where id > #{id} or age = #{age}
</select>
@Test
public void selectMultiObject(){
Student student = new Student();
student.setId(1002);
student.setAge(20);
List<Student> lists = studentDao.selectMultiObject(student);
for (Student s : lists){
System.out.println(s);
}
}
4)传入多个参数-按位置传入(了解)
- 参数位置从0开始,引用参数语法#arg{位置},第一个参数是#{arg0},第二个是#{arg1}
- 注意: mybatis-3.3版本和之前的版本使用#{0},#{1}方式,从mybatis3.4开始使用#{arg0}方式。
- 在StudentDao中加入对应的方法
List<Student> selectMultiPosition( Integer id, Integer age);
<select id="selectMultiPosition" resultType="com.bin.domain.Student">
select * from Student where id > #{arg0} or age = #{arg1}
</select>
@Test
public void selectMultiPosition(){
List<Student> lists = studentDao.selectMultiPosition(1002,20);
for (Student student : lists){
System.out.println(student);
}
}
5)传入多个参数-使用Map(了解)
- Map集合可以存储多个值,使用Map向mapper文件一次传入多个参数.
- Map集合使用String的 key,Object类型的值存储参数。
- mapper文件使用# { key }引用参数值。
- 在StudentDao中加入相应的方法
List<Student> selectMultiMap(Map<String,Object> map);
<select id="selectMultiMap" resultType="com.bin.domain.Student">
select * from Student where id > #{myid} or age = #{myage}
</select>
@Test
public void selectMultiMap(){
Map<String,Object> map = new HashMap<>();
map.put("myid",1002);
map.put("myage",20);
List<Student> lists = studentDao.selectMultiMap(map);
for (Student student : lists){
System.out.println(student);
}
}
6)#和$两个占位符的比较
- #:占位符,告诉mybatis使用实际的参数值代替。并使用PrepareStatement对象执行sql语句, #{…}代替sql语句的“?”。这样做更安全,更迅速,通常也是首选做法
- $字符串替换,告诉mybatis使用$包含的“字符串”替换所在位置。使用Statement 把 sql语句和${…}的内容连接起来。主要用在替换表名,列名,不同列排序等操作。
- 使用$替换列名
- 在StudentDao中加入对应的方法
List<Student> findAllOrder(@Param("colName") String colName);
<select id="findAllOrder" resultType="com.bin.domain.Student">
select * from Student order by ${colName}
</select>
@Test
public void testFindAllOrder(){
List<Student> students = studentDao.findAllOrder("age");
for (Student s :students) {
System.out.println(s);
}
}
- 总结:#和$区别
- #使用?在sql语句中做占位的,使用preparedstatement执行sql,效率高
- #能够避免sql注入,更安全
- $不使用占位符,是字符串连接方式,使用statement对象执行sql,效率低
- $有sql注入的风险,缺乏安全性
- $ :可以替换表名或者列名
3、resultType配置结果类型
- resultType属性可以指定结果集的类型,它支持基本类型和实体类类型。
- 我们在前面的案例中已经对此属性进行过应用了。
- 需要注意的是,它和parameterType一样,如果注册过类型别名的,可以直接使用别名。没有注册过的必须使用全限定类名。
- 例如:我们的实体类此时必须是全限定类名, 同时,还有一个要求,实体类中的属性名称必须和查询语句中的列名保持一致,否则无法实现封装。
1)返回的结果是对象类型
- 以最开始的查询全部学生信息为例
2)返回的结果是简单类型
int count();
<select id="count" resultType="java.lang.Integer">
select count(*) from Student
</select>
@Test
public void testCount(){
int count = studentDao.count();
System.out.println(count);
}
3)返回的结果是Map类型
Map<Object,Object> map(Integer id);
<select id="map" resultType="java.util.Map">
select * from Student where id = #{id}
</select>
@Test
public void testMap(){
Map<Object,Object> map = studentDao.map(1001);
System.out.println(map);
}
4、定义别名
- 定义自定义类型的别名
- 1、在mybati主配置文件中定义,使<\typeAlias>定义别名
- 2、可以在resultType中使用自定义别名
- 定义别名有两种方式
- 第一种方式
<typeAliases>
<typeAlias type="com.bin.domain.student" alias="stu"/>
</typeAliases>
<typeAliases>
<package name="com.bin"/>
</typeAliases>
- 第二种方式需要注意,如果包中存在同名的类将不能使用这种方式,这样无法找到具体的类,运行时会报错
5、resultMap结果类型
- resultMap标签可以建立查询的列名和实体类的属性名称不一致时建立对应关系。从而实现封装。 在select标签中使用resultMap属性指定引用即可。
- 同时resultMap可以实现将查询结果映射为复杂类型的pojo,比如在查询结果映射对象中包括pojo和list实现一对一查询和一对多查询。
- 使用方式:
- 1.先定义resultMap,指定列名和属性的对应关系。
- 2.在.<select>中把resultType替换为resultMap
- 在mapper配置文件中配置resultMap
<resultMap id="StudentMap" type="com.bin.domain.Student">
<id property="id" column="id"/>
<result property="name" column="name"/>
<result property="email" column="email"/>
<result property="age" column="age"/>
</resultMap>
模拟实体类的属性名与列名不一样
package com.bin.domain;
public class Student {
private Integer sid;
private String sname;
private String semail;
private Integer sage;
public Integer getSid() {
return sid;
}
public void setSid(Integer sid) {
this.sid = sid;
}
public String getSname() {
return sname;
}
public void setSname(String sname) {
this.sname = sname;
}
public String getSemail() {
return semail;
}
public void setSemail(String semail) {
this.semail = semail;
}
public Integer getSage() {
return sage;
}
public void setSage(Integer sage) {
this.sage = sage;
}
@Override
public String toString() {
return "Student{" +
"sid=" + sid +
", sname='" + sname + '\'' +
", semail='" + semail + '\'' +
", sage=" + sage +
'}';
}
}
<resultMap id="StudentMap" type="com.bin.domain.Student">
<id property="sid" column="id"/>
<result property="sname" column="name"/>
<result property="semail" column="email"/>
<result property="sage" column="age"/>
</resultMap>
- 第二种解决方案,可以不配做resultMap进行解决
- 更改mapper中的配置,通过更改查询结果的列名,使其与实体类的属性名相同可以解决
<select id="findAll" resultType="Student" >
select id as sid,name as sname,email as semail , age as sage from Student
</select>
- 两种方式解决,各有利弊,具体选择哪一种,具体情况具体分析
6、模糊查询
List<Student> findByName(String name);
<select id="findByName" resultMap="StudentMap">
select * from Student where name like #{name}
</select>
@Test
public void testFindByName(){
List<Student> students = studentDao.findByName("%李%");
for (Student s :students) {
System.out.println(s);
}
}
四、动态SQL
- 动态sql : sql的内容是变化的,可以根据条件获取到不同的sql语句
- 主要是where部分发生变化
1、动态SQL之if标签
- 对于该标签的执行,当test的值为true时,会将其包含的sQL片断拼接到其所在的sQL语句中。
- 语法:<if test=“条件">sql语句的部分</if>
- 重新新建一个Maven工程,除了主配置文件,其他代码保持与入门案例相同
- 在StudentDao加入相应的方法
List<Student> selectStudentIf(Student student);
<select id="selectStudentIf" resultType="Student">
select * from Student where
<if test="name != null and name!=''">
name = #{name}
</if>
<if test="age > 0">
or age > #{age}
</if>
</select>
@Test
public void testSelectStudentIf(){
Student student = new Student();
student.setName("李四");
student.setAge(18);
List<Student> students = studentDao.selectStudentIf(student);
for (Student s :students) {
System.out.println(s);
}
}
2、动态SQL之where标签
- 分析上面的运行结果
- 在StudentDao加入相应的方法
List<Student> selectStudentWhere(Student student);
<select id="selectStudentWhere" resultType="Student">
select * from Student
<where>
<if test="name != null and name!=''">
name = #{name}
</if>
<if test="age > 0">
or age > #{age}
</if>
</where>
</select>
@Test
public void testSelectStudentWhere(){
Student student = new Student();
student.setAge(18);
List<Student> students = studentDao.selectStudentWhere(student);
for (Student s :students) {
System.out.println(s);
}
}
3、动态SQL之foreach标签
- <foreach/>标签用于实现对于数组与集合的遍历。对其使用,需要注意:
- collection表示要遍历的集合类型, list , array等。
- open、close、 separator为对遍历内容的SQL拼接。
- 在StudentDao中添加对应的方法
List<Student> selectStudentList(List<Integer> list);
<select id="selectStudentList" resultType="Student">
select * from Student where id in
<foreach collection="list" open="(" close=")" separator="," item="id">
#{id}
</foreach>
</select>
@Test
public void selectStudentList(){
List<Integer> list = new ArrayList<Integer>();
list.add(1002);
list.add(1003);
List<Student> students = studentDao.selectStudentList(list);
for (Student s :students) {
System.out.println(s);
}
}
4、动态SQL之代码片段
- <sql/>标签用于定义SQL片段,以便其它 SQL标签复用。而其它标签使用该SQL片段,需要使用<include/>子标签。该<sql/>标签可以定义SQL语句中的任何部分,所以<include/>子标签可以放在动态SQL的任何位置。
- Sql中可将重复的sql提取出来,使用时用include引用即可,最终达到sql重用的目的。
- 在mapper文件中定义
<sql id="selectStudent">
select * from Student
</sql>
五、配置文件
1、主配置文件
- SqlMapConfig.xml中配置的内容和顺序
- properties(属性)
- settings(全局配置参数)
- typeAliases(类型别名)
- typeHandlers(类型处理器)
- objectFactory(对象工厂)
- plugins(插件)
- environments(环境集合属性对象)
- environment(环境子属性对象)
- transactionManager(事务管理)
- dataSource(数据源)
- mappers(映射器)
2、properties(属性)
- 在properties中可以配置数据库的相关连接信息,相当于将dataSource中的信息提取出来方便管理
- 有两种写法:一种是直接写在当前的配置文件里,另一种是新建一个专门存放数据库相关连接信息的配置文件,比较推荐的是第二种写法
- 第一种:在mapper中配置
<properties>
<property name="jdbc.driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="jdbc.url" value="jdbc:mysql:///student?characterEncoding=UTF-8&serverTimezone=UTC"/>
<property name="jdbc.username" value="root"/>
<property name="jdbc.password" value="root"/>
</properties>
- 第二种:通过配置文件引入
- 创建一个jdbc.properties文件
jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql:///student?characterEncoding=UTF-8&serverTimezone=UTC
jdbc.username=root
jdbc.password=root
<properties resource="jdbc.properties"/>
3、typeAliases(类型别名)
<typeAliases>
<typeAlias alias="user" type="com.bin.domain.User"/>
<package name="com.bin.domain"/>
<package name="其它包"/>
</typeAliases>
4、mappers(映射器)
- <mapper resource=" " />
- 使用相对于类路径的资源 如:
<mapper resource=“com/bin/dao/IUserDao.xml” /> - <mapper class=" " />
- 使用mapper接口类路径 如:<mapper class=“com.bin.dao.UserDao”/>
注意:此种方法要求mapper接口名称和mapper映射文件名称相同,且放在同一个目录中。 - <package name=""/>
- 注册指定包下的所有mapper接口 如:<package name=“com.bin”/>
注意:此种方法要求mapper接口名称和mapper映射文件名称相同,且放在同一个目录中。
六、多表查询
- 前提准备:创建两张表:一张User表,一张Account表
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
`id` INT (11),
`username` VARCHAR (96),
`birthday` DATETIME ,
`sex` CHAR (3),
`address` VARCHAR (768)
);
INSERT INTO `user` (`id`, `username`, `birthday`, `sex`, `address`) VALUES('41','老王','2018-02-27 17:47:08','男','beijing');
INSERT INTO `user` (`id`, `username`, `birthday`, `sex`, `address`) VALUES('42','小二王','2018-03-02 15:09:37','女','北京');
INSERT INTO `user` (`id`, `username`, `birthday`, `sex`, `address`) VALUES('43','小二王','2018-03-04 11:34:34','女','北京');
INSERT INTO `user` (`id`, `username`, `birthday`, `sex`, `address`) VALUES('45','老李','2018-03-04 12:04:06','男','北京');
INSERT INTO `user` (`id`, `username`, `birthday`, `sex`, `address`) VALUES('46','老王','2018-03-07 17:37:26','男','北京');
INSERT INTO `user` (`id`, `username`, `birthday`, `sex`, `address`) VALUES('48','小马宝莉','2018-03-08 11:44:00','女','北京');
CREATE TABLE `account` (
`ID` INT(11) NOT NULL COMMENT '编号',
`UID` INT(11) DEFAULT NULL COMMENT '用户编号',
`MONEY` DOUBLE DEFAULT NULL COMMENT '金额',
PRIMARY KEY (`ID`),
KEY `FK_Reference_8` (`UID`),
CONSTRAINT `FK_Reference_8` FOREIGN KEY (`UID`) REFERENCES `user` (`id`)
) ENGINE=INNODB DEFAULT CHARSET=utf8;
INSERT INTO `account`(`ID`,`UID`,`MONEY`) VALUES (1,46,1000),(2,45,1000),(3,46,2000);
1、一对一查询(多对一)
- 一个用户(User)可以有多个账户(Account)。具体关系如下:
- 需求:查询所有账户信息,关联查询下的用户信息。
- 分析:
- 一个账户信息只能供某个用户使用,所以从查询账户信息出发关联查询用户信息为一对一查询。
- 从用户信息出发查询用户下的账户信息则为一对多查询,因为一个用户可以有多个账户。
实现一对一可以有两种方式 (新建一个maven工程,参照入门案例创建好对应的配置文件)
<?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">
<configuration>
<properties resource="MySQL.properties"/>
<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
<environments default="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>
<mappers>
<mapper resource="dao/UserDao.xml"/>
<mapper resource="dao/AccountDao.xml"/>
</mappers>
</configuration>
- 方式一(通过一个AccountUser类同时接收Account和User的共同信息)
- 创建Account的实体类
package domain;
public class Account{
private Integer id;
private Integer uid;
private Double money;
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 +
"]";
}
}
package domain;
import java.util.Date;
import java.util.List;
public class User{
private Integer id;
private String username;
private Date birthday;
private String sex;
private String address;
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 + '\'' +
'}';
}
}
- Sql语句的编写
实现查询账户信息时,也要查询账户所对应的用户信息。
SELECT account.*, user.username, user.address FROM account, user WHERE account.uid = user.id
- 创建一个AccountUser类
- 为了能够封装上面SQL语句的查询结果,定义 AccountUser类中要包含账户信息同时还要包含用户信息,所以我们要在定义AccountUser类时可以继承User类。
package domain;
public class AccountUser extends Account{
private String username;
private String address;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
@Override
public String toString() {
return super.toString() + "AccountUser{" +
"username='" + username + '\'' +
", address='" + address + '\'' +
'}';
}
}
package dao;
import domain.Account;
import domain.AccountUser;
import java.util.List;
public interface AccountDao {
List<AccountUser> findAllAccount();
}
- 在AccountDao.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.bin.dao.AccountDao">
<select id="findAll" resultType="domain.AccountUser">
select a.*,u.username,u.address from account a,user u where a.uid =u.id;
</select>
</mapper>
package AccountDaoTest;
import dao.AccountDao;
import domain.Account;
import domain.AccountUser;
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.IOException;
import java.io.InputStream;
import java.util.List;
public class test {
InputStream inputStream;
SqlSession sqlSession;
AccountDao accountDao;
@Before
public void init() throws IOException {
inputStream = Resources.getResourceAsStream("SqlMapConfig.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
sqlSession = sqlSessionFactory.openSession();
accountDao = sqlSession.getMapper(AccountDao.class);
}
@After
public void destroy() throws IOException {
sqlSession.commit();
inputStream.close();
sqlSession.close();
}
@Test
public void testFindAllAccount(){
List<AccountUser> accountUsers = accountDao.findAllAccount();
for (AccountUser a:accountUsers) {
System.out.println(a);
}
}
- 方式二
- 使用resultMap,定义专门的resultMap用于映射一对一查询结果。 通过面向对象的(has a)关系可以得知,我们可以在Account类中加入一个User类的对象来代表这个账户是哪个用户的。
- 此时不需要AccountUser类了
- 修改Account类
package domain;
public class Account {
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 +
'}';
}
}
- 修改AccountDao接口
- 第二种方式,将返回值改为了Account类型。 因为Account类中包含了一个User类的对象,它可以封装账户所对应的用户信息。
package dao;
import domain.Account;
import domain.AccountUser;
import java.util.List;
public interface AccountDao {
List<Account> findAllAccount();
}
<?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="dao.AccountDao">
<resultMap id="accountUserMap" type="domain.Account">
<id property="id" column="aid"></id>
<result property="uid" column="uid"></result>
<result property="money" column="money"></result>
<association property="user" column="uid" javaType="domain.User">
<id property="id" column="id"></id>
<result property="username" column="username"></result>
<result property="birthday" column="birthday"></result>
<result property="sex" column="sex"></result>
<result property="address" column="address"></result>
</association>
</resultMap>
<select id="findAll" resultMap="accountUserMap">
select u.*,a.id as aid,a.money from account a,user u where a.uid = u.id
</select>
</mapper>
@Test
public void testFindAll(){
List<Account> accounts = accountDao.findAll();
for (Account account: accounts) {
System.out.println(account);
System.out.println(account.getUser());
}
}
2、一对多查询
- 需求:查询所有用户信息及用户关联的账户信息。
- 分析:用户信息和他的账户信息为一对多关系,并且查询过程中如果用户没有账户信息,此时也要将用户信息查询出来,我们想到了左外连接查询比较合适。
- Sql语句的编写
select u.*,a.id as aid ,a.uid,a.money from user u left outer join account a on u.id =a.uid
package domain;
import java.util.Date;
import java.util.List;
public class User{
private Integer id;
private String username;
private Date birthday;
private String sex;
private String address;
private List<Account> account;
public List<Account> getAccount() {
return account;
}
public void setAccount(List<Account> account) {
this.account = account;
}
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 + '\'' +
'}';
}
}
package dao;
import domain.User;
import java.util.List;
public interface UserDao {
List<User> findAll();
}
<?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="dao.UserDao">
<resultMap id="UserAccountMap" type="domain.User">
<id column="id" property="id"></id>
<result column="username" property="username"></result>
<result column="birthday" property="birthday"></result>
<result column="sex" property="sex"></result>
<collection property="account" ofType="domain.Account">
<id column="aid" property="id"></id>
<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>
</mapper>
package UserDaoTest;
import dao.UserDao;
import 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.IOException;
import java.io.InputStream;
import java.util.List;
public class test {
InputStream inputStream;
SqlSession sqlSession;
UserDao userDao;
@Before
public void init() throws IOException {
inputStream = Resources.getResourceAsStream("SqlMapConfig.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
sqlSession = sqlSessionFactory.openSession();
userDao = sqlSession.getMapper(UserDao.class);
}
@After
public void destroy() throws IOException {
sqlSession.commit();
inputStream.close();
sqlSession.close();
}
@Test
public void testFindAll(){
List<User> users = userDao.findAll();
for (User user:users) {
System.out.println(user);
System.out.println(user.getAccount());
}
}
}
3、多对多查询
新建一个maven工程,配置好主配置文件
- 前期准备:创建一个Role表,一张user_role中间表
DROP TABLE IF EXISTS `role`;
CREATE TABLE `role` (
`ID` INT(11) NOT NULL COMMENT '编号',
`ROLE_NAME` VARCHAR(30) DEFAULT NULL COMMENT '角色名称',
`ROLE_DESC` VARCHAR(60) DEFAULT NULL COMMENT '角色描述',
PRIMARY KEY (`ID`)
) ENGINE=INNODB DEFAULT CHARSET=utf8;
INSERT INTO `role`(`ID`,`ROLE_NAME`,`ROLE_DESC`) VALUES (1,'院长','管理整个学院'),(2,'总裁','管理整个公司'),(3,'校长','管理整个学校');
CREATE TABLE `user_role` (
`UID` INT(11) NOT NULL COMMENT '用户编号',
`RID` INT(11) NOT NULL COMMENT '角色编号',
PRIMARY KEY (`UID`,`RID`),
KEY `FK_Reference_10` (`RID`),
CONSTRAINT `FK_Reference_10` FOREIGN KEY (`RID`) REFERENCES `role` (`ID`),
CONSTRAINT `FK_Reference_9` FOREIGN KEY (`UID`) REFERENCES `user` (`id`)
) ENGINE=INNODB DEFAULT CHARSET=utf8;
INSERT INTO `user_role`(`UID`,`RID`) VALUES (41,1),(45,1),(41,2);
- 用户与角色的关系模型
- 需求:实现查询所有对象并且加载它所分配的用户信息。
实现Role 到User 多对多
- 分析:查询角色我们需要用到Role表,但角色分配的用户的信息我们并不能直接找到用户信息,而是要通过中间表(USER_ROLE表)才能关联到用户信息。
- Sql语句实现
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;
package domain;
import java.util.List;
public class Role {
private Integer id;
private String roleName;
private String roleDesc;
private List<User> user;
public List<User> getUser() {
return user;
}
public void setUser(List<User> user) {
this.user = user;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
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{" +
"id=" + id +
", roleName='" + roleName + '\'' +
", roleDesc='" + roleDesc + '\'' +
'}';
}
}
package domain;
import java.util.Date;
import java.util.List;
public class User{
private Integer id;
private String username;
private Date birthday;
private String sex;
private String address;
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 + '\'' +
'}';
}
}
package dao;
import domain.Role;
import java.util.List;
public interface RoleDao {
List<Role> findAll();
}
<?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="dao.RoleDao">
<resultMap id="RoleMap" type="domain.Role">
<id property="id" column="rid"></id>
<result column="ROLE_NAME" property="roleName"></result>
<result column="ROLE_DESC" property="roleDesc"></result>
<collection property="user" ofType="domain.User">
<id property="id" column="id"></id>
<result column="username" property="username"></result>
<result column="address" property="address"></result>
<result column="birthday" property="birthday"></result>
<result column="sex" property="sex"></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>
package RoleDaoTest;
import dao.RoleDao;
import dao.UserDao;
import domain.Role;
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.IOException;
import java.io.InputStream;
import java.util.List;
public class test {
InputStream inputStream;
SqlSession sqlSession;
RoleDao roleDao;
@Before
public void init() throws IOException {
inputStream = Resources.getResourceAsStream("SqlMapConfig.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
sqlSession = sqlSessionFactory.openSession();
roleDao = sqlSession.getMapper(RoleDao.class);
}
@After
public void destroy() throws IOException {
sqlSession.commit();
inputStream.close();
sqlSession.close();
}
@Test
public void testFindAll(){
List<Role> roles = roleDao.findAll();
for (Role role: roles) {
System.out.println(role);
System.out.println(role.getUser());
}
}
}
实现User到Role的多对多
- 需求:实现查询所有用户信息并关联查询出每个用户的角色列表。
- Sql语句的实现
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
package domain;
import java.io.Serializable;
import java.util.Date;
import java.util.List;
public class User implements Serializable {
private Integer id;
private String username;
private Date birthday;
private String sex;
private String address;
private List<Role> roles;
public List<Role> getRoles() {
return roles;
}
public void setRoles(List<Role> roles) {
this.roles = roles;
}
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 + '\'' +
'}';
}
}
package dao;
import domain.User;
import java.util.List;
public interface UserDao {
List<User> findAll();
}
<?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="dao.UserDao">
<resultMap id="UserAccountMap" type="domain.User">
<id column="id" property="id"></id>
<result column="username" property="username"></result>
<result column="birthday" property="birthday"></result>
<result column="sex" property="sex"></result>
<collection property="roles" ofType="domain.Role">
<id property="id" column="rid"></id>
<result property="roleName" column="ROLE_NAME"></result>
<result property="roleDesc" column="ROLE_DESC"></result>
</collection>
</resultMap>
<select id="findAll" resultMap="UserAccountMap">
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>
</mapper>
package UserDaoTest;
import dao.UserDao;
import 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.IOException;
import java.io.InputStream;
import java.util.List;
public class test {
InputStream inputStream;
SqlSession sqlSession;
UserDao userDao;
@Before
public void init() throws IOException {
inputStream = Resources.getResourceAsStream("SqlMapConfig.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
sqlSession = sqlSessionFactory.openSession();
userDao = sqlSession.getMapper(UserDao.class);
}
@After
public void destroy() throws IOException {
sqlSession.commit();
inputStream.close();
sqlSession.close();
}
@Test
public void testFindAll(){
List<User> users = userDao.findAll();
for (User user:users) {
System.out.println(user);
System.out.println(user.getRoles());
}
}
}
七、延迟加载策略
1、什么是延迟加载
- 延迟加载: 就是在需要用到数据时才进行加载,不需要用到数据时就不加载数据。延迟加载也称懒加载.
- 好处:先从单表查询,需要时再从关联表去关联查询,大大提高数据库性能,因为查询单表要比关联查询多张表速度要快。
- 坏处: 因为只有当需要用到数据时,才会进行数据库查询,这样在大批量数据查询时,因为查询工作也要消耗时间,所以可能造成用户等待时间变长,造成用户体验下降。
2、实现延迟加载
新建maven工程,将一对一、一对多的内容复制过来
- 需求: 查询账户(Account)信息并且关联查询用户(User)信息。如果先查询账户(Account)信息即可满足要求,当我们需要查询用户(User)信息时再查询用户(User)信息。把对用户(User)信息的按需去查询就是延迟加载。
- 通过association、collection实现一对一及一对多映射。association、collection具备延迟加载功能。
使用association实现延迟加载
- 需求: 查询账户信息同时查询用户信息。
- 修改AccountDao.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="dao.AccountDao">
<resultMap id="accountUserMap" type="domain.Account">
<id property="id" column="id"/>
<result property="uid" column="uid"/>
<result property="money" column="money"/>
<association property="user" column="uid" javaType="domain.User" select="dao.UserDao.findByID"/>
</resultMap>
<select id="findAll" resultMap="accountUserMap">
select * from account
</select>
</mapper>
- 在官方文档中可以查询settings关键字,可以看到有关延迟加载,当然还有很多其他的配置,包括下面要讲的缓存
- 在主配置文件中进行配置
<?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">
<configuration>
<properties resource="MySQL.properties"/>
<settings>
<setting name="lazyLoadingEnabled" value="true"/>
<setting name="aggressiveLazyLoading" value="false"/>
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
<environments default="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>
<mappers>
<mapper resource="dao/UserDao.xml"/>
<mapper resource="dao/AccountDao.xml"/>
</mappers>
</configuration>
package AccountDaoTest;
import dao.AccountDao;
import domain.Account;
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.IOException;
import java.io.InputStream;
import java.util.List;
public class test {
InputStream inputStream;
SqlSession sqlSession;
AccountDao accountDao;
@Before
public void init() throws IOException {
inputStream = Resources.getResourceAsStream("SqlMapConfig.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
sqlSession = sqlSessionFactory.openSession();
accountDao = sqlSession.getMapper(AccountDao.class);
}
@After
public void destroy() throws IOException {
sqlSession.commit();
inputStream.close();
sqlSession.close();
}
@Test
public void testFindAll(){
List<Account> accounts = accountDao.findAll();
}
}
使用Collection实现延迟加载
<?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="dao.UserDao">
<resultMap id="UserAccountMap" type="domain.User">
<id column="id" property="id"></id>
<result column="username" property="username"></result>
<result column="birthday" property="birthday"></result>
<result column="sex" property="sex"></result>
<collection property="account" ofType="domain.Account" select="dao.AccountDao.findAccountByID" column="id"></collection>
</resultMap>
<select id="findAll" resultMap="UserAccountMap">
select * from user
</select>
</mapper>
package UserDaoTest;
import dao.UserDao;
import 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.IOException;
import java.io.InputStream;
import java.util.List;
public class test {
InputStream inputStream;
SqlSession sqlSession;
UserDao userDao;
@Before
public void init() throws IOException {
inputStream = Resources.getResourceAsStream("SqlMapConfig.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
sqlSession = sqlSessionFactory.openSession();
userDao = sqlSession.getMapper(UserDao.class);
}
@After
public void destroy() throws IOException {
sqlSession.commit();
inputStream.close();
sqlSession.close();
}
@Test
public void testFindAll(){
List<User> users = userDao.findAll();
}
}
八、缓存
- 像大多数的持久化框架一样,Mybatis也提供了缓存策略,通过缓存策略来减少数据库的查询次数,从而提高性能。
- Mybatis中缓存分为一级缓存,二级缓存。
1、一级缓存
- 证明一级缓存的存在:一级缓存是SqlSession级别的缓存,只要SqlSession没有flush或close,它就存在。
- 新建一个Maven工程,复制延迟加载的内存,将Account的相关内容删除,只留下User的内容
- User实体类
package domain;
import java.io.Serializable;
import java.util.Date;
public class User implements Serializable {
private Integer id;
private String username;
private Date birthday;
private String sex;
private String address;
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;
}
}
package dao;
import domain.User;
public interface UserDao {
User findByID(Integer id);
}
<?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="dao.UserDao">
<cache/>
<select id="findByID" parameterType="Integer" resultType="domain.User">
select * from user where id = #{id}
</select>
</mapper>
package UserDaoTest;
import dao.UserDao;
import 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.IOException;
import java.io.InputStream;
import java.util.List;
public class test {
InputStream inputStream;
SqlSession sqlSession;
SqlSessionFactory sqlSessionFactory;
UserDao userDao;
@Before
public void init() throws IOException {
inputStream = Resources.getResourceAsStream("SqlMapConfig.xml");
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
sqlSession = sqlSessionFactory.openSession();
userDao = sqlSession.getMapper(UserDao.class);
}
@After
public void destroy() throws IOException {
sqlSession.commit();
inputStream.close();
sqlSession.close();
}
@Test
public void testFirstLevelCache(){
User user = userDao.findByID(41);
User user1 = userDao.findByID(41);
System.out.println(user);
System.out.println(user1);
System.out.println(user==user1);
sqlSession.clearCache();
userDao = sqlSession.getMapper(UserDao.class);
System.out.println(user);
System.out.println(user1);
System.out.println(user==user1);
}
}
一级缓存的分析
- 一级缓存是SqlSession范围的缓存,当调用SqlSession的修改,添加,删除,commit(),close()等方法时,就会清空一级缓存。
- 第一次发起查询用户id为1的用户信息,先去找缓存中是否有id为1的用户信息,如果没有,从数据库查询用户信息。
- 得到用户信息,将用户信息存储到一级缓存中。
- 如果sqlSession去执行commit操作(执行插入、更新、删除),清空SqlSession中的一级缓存,这样做的目的为了让缓存中存储的是最新的信息,避免脏读。
- 第二次发起查询用户id为1的用户信息,先去找缓存中是否有id为1的用户信息,缓存中有,直接从缓存中获取用户信息。
2、二级缓存
- 二级缓存是mapper映射级别的缓存,多个SqlSession去操作同一个Mapper映射的sql语句,多个SqlSession可以共用二级缓存,二级缓存是跨SqlSession的。
- 首先开启mybatis的二级缓存。 sqlSession1去查询用户信息,查询到用户信息会将查询数据存储到二级缓存中。
- 如果SqlSession3去执行相同 mapper映射下sql,执行commit提交,将会清空该 mapper映射下的二级缓存区域的数据。
- sqlSession2去查询与sqlSession1相同的用户信息,首先会去缓存中找是否存在数据,如果存在直接从缓存中取出数据。
二级缓存的开启与关闭
- 第一步:在SqlMapConfig.xml文件开启二级缓存
- 因为cacheEnabled的取值默认就为true,所以这一步可以省略不配置。为true代表开启二级缓存;为false代表不开启二级缓存。
<settings>
<setting name="cacheEnabled" value="true"/>
</settings>
- 第二步:配置相关的Mapper映射文件
- <cache>标签表示当前这个mapper映射将使用二级缓存,区分的标准就看mapper的namespace值。
<?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="dao.UserDao">
<cache></cache>
</mapper>
- 第三步:配置statement上面的useCache属性
- 将UserDao.xml映射文件中的<select>标签中设置useCache=”true”代表当前这个statement要使用二级缓存,如果不使用二级缓存可以设置为false。
- 注意:针对每次查询都需要最新的数据sql,要设置成useCache=false,禁用二级缓存
<select id="findById" resultType="domain.User" parameterType="int" useCache="true">
select * from user where id = #{uid}
</select>
@Test
public void testSecondLevelCache(){
SqlSession sqlSession1 = sqlSessionFactory.openSession();
UserDao userDao1 = sqlSession1.getMapper(UserDao.class);
User user1 = userDao1.findByID(41);
System.out.println(user1);
sqlSession1.close();
SqlSession sqlSession2 = sqlSessionFactory.openSession();
UserDao userDao2 = sqlSession2.getMapper(UserDao.class);
User user2 = userDao2.findByID(41);
System.out.println(user2);
sqlSession1.close();
System.out.println(user1==user2);
}
- 二级缓存注意事项:当我们在使用二级缓存时,所缓存的类一定要实现java.io.Serializable接口,这种就可以使用序列化方式来保存对象。
九、注解开发
1、使用注解实现CRUD
- 使用注解开发:就可以不编写对应mapper配置文件
- mybatis的常用注解说明
@Insert:实现新增
@Update:实现更新
@Delete:实现删除
@Select:实现查询
@Result:实现结果集封装
@Results:可以与@Result一起使用,封装多个结果集
@ResultMap:实现引用@Results定义的封装
@One:实现一对一结果集封装
@Many:实现一对多结果集封装
@SelectProvider: 实现动态SQL映射
@CacheNamespace:实现注解二级缓存的使用
使用注解实现CRUD操作(即将入门案例改为注解的方式)
<?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">
<configuration>
<properties resource="MySQL.properties"/>
<settings>
<setting name="cacheEnabled" value="true"/>
</settings>
<typeAliases>
<package name="com.bin.domain"/>
</typeAliases>
<environments default="mysql">
<environment id="mysql">
<transactionManager type="JDBC"/>
<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>
<mappers>
<package name="com.bin.dao"/>
</mappers>
</configuration>
package com.bin.dao;
import com.bin.domain.Student;
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;
public interface StudentDao {
@Select("select * from Student")
List<Student> findAll();
@Insert("insert into Student(id,name,email,age) values (#{id},#{name},#{email},#{age})")
void insert(Student student);
@Update("update Student set name = #{name},email = #{email},age = #{age} where id = #{id}")
void update(Student student);
@Delete("delete from Student where id = #{id}")
void delete(Integer id);
}
2、使用注解实现复杂关系映射开发
- 实现复杂关系映射之前我们可以在映射文件中通过配置<resultMap>来实现,在使用注解开发时我们需要借助@Results注解,@Result注解,@One注解,@Many注解。
- 复杂关系映射的注解说明
@Results注解
代替的是标签<resultMap>
该注解中可以使用单个@Result注解,也可以使用@Result集合
@Results({@Result(),@Result()})或@Results(@Result())
@Resutl注解
代替了 <id>标签和<result>标签
@Result 中 属性介绍:
id 是否是主键字段
column 数据库的列名
property需要装配的属性名
one 需要使用的@One注解(@Result(one=@One)()))
many 需要使用的@Many注解(@Result(many=@many)()))
@One注解(一对一)
代替了<assocation>标签,是多表查询的关键,在注解中用来指定子查询返回单一对象。
@One注解属性介绍:
select 指定用来多表查询的sqlmapper
fetchType会覆盖全局的配置参数lazyLoadingEnabled。
使用格式:
@Result(column=" ",property="",one=@One(select=""))
@Many注解(多对一)
代替了<collection>标签,是是多表查询的关键,在注解中用来指定子查询返回对象集合。
注意:聚集元素用来处理“一对多”的关系。需要指定映射的Java实体类的属性,属性的javaType(一般为ArrayList)但是注解中可以不定义;
使用格式:
@Result(property="",column="",many=@Many(select=""))
1)、使用注解实现一对一复杂关系映射及延迟加载
- 需求: 加载账户信息时并且加载该账户的用户信息,根据情况可实现延迟加载。(注解方式实现)
- 更改User类和Account类
package com.bin.domain;
import java.io.Serializable;
import java.util.Date;
import java.util.List;
public class User implements Serializable {
private Integer UserId;
private String userName;
private Date userBirthday;
private String userSex;
private String userAddress;
public Integer getUserId() {
return UserId;
}
public void setUserId(Integer userId) {
UserId = userId;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public Date getUserBirthday() {
return userBirthday;
}
public void setUserBirthday(Date userBirthday) {
this.userBirthday = userBirthday;
}
public String getUserSex() {
return userSex;
}
public void setUserSex(String userSex) {
this.userSex = userSex;
}
public String getUserAddress() {
return userAddress;
}
public void setUserAddress(String userAddress) {
this.userAddress = userAddress;
}
@Override
public String toString() {
return "User{" +
"UserId=" + UserId +
", userName='" + userName + '\'' +
", userBirthday=" + userBirthday +
", userSex='" + userSex + '\'' +
", userAddress='" + userAddress + '\'' +
'}';
}
}
package com.bin.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 +
'}';
}
}
import com.bin.domain.Account;
import org.apache.ibatis.annotations.One;
import org.apache.ibatis.annotations.Result;
import org.apache.ibatis.annotations.Results;
import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.mapping.FetchType;
import java.util.List;
public interface AccountDao {
@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(column = "uid",property = "user",one = @One(select = "com.bin.dao.UserDao.findByID",fetchType = FetchType.LAZY))
})
List<Account> findAll();
}
package com.bin.dao;
import com.bin.domain.User;
import org.apache.ibatis.annotations.*;
import org.apache.ibatis.mapping.FetchType;
import java.util.List;
public interface UserDao {
@Select("select * from user")
@Results(id="UserMap",value = {
@Result(id = true,column = "id",property = "userId"),
@Result(column = "username",property = "userName"),
@Result(column = "birthday",property = "userBirthday"),
@Result(column = "sex",property = "userSex"),
@Result(column = "address",property = "userAddress"),
})
List<User> findAll();
@Select("select * from user where id = #{uid}")
@ResultMap("UserMap")
User findByID(Integer id);
}
import com.bin.dao.AccountDao;
import com.bin.domain.Account;
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.IOException;
import java.io.InputStream;
import java.util.List;
public class test {
InputStream inputStream;
SqlSession sqlSession;
AccountDao accountDao;
@Before
public void init() throws IOException {
inputStream = Resources.getResourceAsStream("SqlMapConfig.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
sqlSession = sqlSessionFactory.openSession();
accountDao = sqlSession.getMapper(AccountDao.class);
}
@After
public void destroy() throws IOException {
sqlSession.commit();
inputStream.close();
sqlSession.close();
}
@Test
public void testFindAll(){
List<Account> accounts = accountDao.findAll();
}
}
2)、使用注解实现一对多复杂关系映射
package com.bin.domain;
import java.io.Serializable;
import java.util.Date;
import java.util.List;
public class User implements Serializable {
private Integer UserId;
private String userName;
private Date userBirthday;
private String userSex;
private String userAddress;
private List<Account> account;
public List<Account> getAccount() {
return account;
}
public void setAccount(List<Account> account) {
this.account = account;
}
public Integer getUserId() {
return UserId;
}
public void setUserId(Integer userId) {
UserId = userId;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public Date getUserBirthday() {
return userBirthday;
}
public void setUserBirthday(Date userBirthday) {
this.userBirthday = userBirthday;
}
public String getUserSex() {
return userSex;
}
public void setUserSex(String userSex) {
this.userSex = userSex;
}
public String getUserAddress() {
return userAddress;
}
public void setUserAddress(String userAddress) {
this.userAddress = userAddress;
}
@Override
public String toString() {
return "User{" +
"UserId=" + UserId +
", userName='" + userName + '\'' +
", userBirthday=" + userBirthday +
", userSex='" + userSex + '\'' +
", userAddress='" + userAddress + '\'' +
'}';
}
}
package com.bin.dao;
import com.bin.domain.User;
import org.apache.ibatis.annotations.*;
import org.apache.ibatis.mapping.FetchType;
import java.util.List;
public interface UserDao {
@Select("select * from user")
@Results(id="UserMap",value = {
@Result(id = true,column = "id",property = "userId"),
@Result(column = "username",property = "userName"),
@Result(column = "birthday",property = "userBirthday"),
@Result(column = "sex",property = "userSex"),
@Result(column = "address",property = "userAddress"),
@Result(column = "id",property = "account",
many = @Many(select = "com.bin.dao.AccountDao.findAccountByID",
fetchType = FetchType.EAGER))
})
List<User> findAll();
}
package com.bin.dao;
import com.bin.domain.Account;
import org.apache.ibatis.annotations.One;
import org.apache.ibatis.annotations.Result;
import org.apache.ibatis.annotations.Results;
import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.mapping.FetchType;
import java.util.List;
public interface AccountDao {
@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(column = "uid",property = "user",
one = @One(select = "com.bin.dao.UserDao.findByID",
fetchType = FetchType.LAZY))
})
List<Account> findAll();
@Select("select * from account where uid = #{id}")
List<Account> findAccountByID(Integer id);
}
package UserDaoTest;
import com.bin.dao.UserDao;
import com.bin.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.IOException;
import java.io.InputStream;
import java.util.Date;
import java.util.List;
public class test {
InputStream inputStream;
SqlSessionFactory sqlSessionFactory;
SqlSession sqlSession;
UserDao userDao;
@Before
public void init() throws IOException {
inputStream = Resources.getResourceAsStream("SqlMapConfig.xml");
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
sqlSession = sqlSessionFactory.openSession();
userDao = sqlSession.getMapper(UserDao.class);
}
@After
public void destroy() throws IOException {
sqlSession.commit();
inputStream.close();
sqlSession.close();
}
@Test
public void testFindAll(){
List<User> users = userDao.findAll();
for (User user: users) {
System.out.println(user);
System.out.println(user.getAccount());
}
}
}
3、mybatis基于注解的二级缓存
<settings>
<setting name="cacheEnabled" value="true"/>
</settings>
package com.bin.dao;
import com.bin.domain.User;
import org.apache.ibatis.annotations.*;
import org.apache.ibatis.mapping.FetchType;
import java.util.List;
@CacheNamespace(blocking = true)
public interface UserDao {
@Select("select * from user")
@Results(id="UserMap",value = {
@Result(id = true,column = "id",property = "userId"),
@Result(column = "username",property = "userName"),
@Result(column = "birthday",property = "userBirthday"),
@Result(column = "sex",property = "userSex"),
@Result(column = "address",property = "userAddress"),
@Result(column = "id",property = "account",
many = @Many(select = "com.bin.dao.AccountDao.findAccountByID",
fetchType = FetchType.EAGER))
})
List<User> findAll();
@Select("select * from user where id = #{uid}")
@ResultMap("UserMap")
User findByID(Integer id);
}
|