框架介绍
- 框架概念
框架,是软件的半成品,完成了软件开发过程中的通用性功能,从而简化了软件的开发步骤、提高开发效率
- Java web开发常用框架
MyBatis
简化了数据持久化操作的步骤,ORM框架 (Hibernate、EJB3、Spring JDBC)Spring
提供了对象创建、初始化及对象管理的功能,容器框架SpringMVC
完成前端和代码的交互,MVC框架
MyBatis介绍
MyBatis 是一个ORM框架,用于实现数据的持久化操作
2.1 ORM
ORM (Object Relational Mapping) 对象关系映射 就是将一个Java对象映射到数据表中的一行记录
2.2 JDBC操作的缺点
存在大量的冗余代码、步骤繁琐
数据库连接、Statement对象等都需要手动创建
查询的结构需要手动进行数据封装
SQL没有经过优化直接执行,效率较低
2.3 了解MyBatis
MyBatis是apache的一个开源项目,前身是iBatis
- 2010年托管到google code,更名为MyBatis;2013年托管到GitHub
- MyBatis是一个优秀的Java持久层框架,支持SQL自定义
- MyBatis底层就是对JDBC的封装,几乎消除了所有的JDBC步骤,我们只需要关注SQL本身
MyBatis框架部署
框架部署,就是将框架引入到我们的项目中
3.1 创建Maven项目
可以是Java应用,也可以是web应用
3.2 添加依赖
因为MyBatis是基于JDBC的封装,因此操作数据库也需要数据库驱动依赖
<!-- MySQL数据库驱动jar -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
<!-- MyBatis依赖 -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.6</version>
</dependency>
3.3 创建MyBatis配置文件
-
在resources目录创建mybatis-config.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"> <configuration> <environments default="mysql"> <environment id="mysql"> <transactionManager type="JDBC"></transactionManager> <dataSource type="org.apache.ibatis.datasource.pooled.PooledDataSourceFactory"> <property name="driver" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/myshop?characterEncoding=utf-8"/> <property name="username" value="root"/> <property name="password" value="admin123"/> </dataSource> </environment> </environments> </configuration>
MyBatis开发步骤
4.1 建表
create table books(
book_id int primary key auto_increment,
book_name varchar(100) not null,
book_author varchar(20) not null,
book_price decimal(6,2) not null
);
4.2 创建实体类
添加lombok依赖
<!--lombok-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.12</version>
<scope>provided</scope>
</dependency>
创建实体类
@Data
@AllArgsConstructor
@NoArgsConstructor
@ToString
public class Book {
private int bookId;
private String bookName;
private String bookAuthor;
private double bookPrice;
}
4.3 定义DAO接口
在DAO接口中定义操作的方法
public interface BookDAO {
public int insertBook(Book book);
}
4.4 映射配置
- 在resources目录下创建一个mappers目录
- 在mappers目录下创建一个.xml文件(MyBatis映射文件)
- 完成Mapper文件的配置
<?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">
<!--namespace = 所需实现的接口全限定名-->
<mapper namespace="com.qfedu.dao.BookDAO">
<insert id="insertBook">
insert into books(book_name,book_author,book_price)
values(#{bookName},#{bookAuthor},#{bookPrice})
</insert>
</mapper>
4.5 注册Mapper
将mapper文件配置到主配置文件(mybatis-config.xml)
在mybatis-config.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">
<configuration>
<!--<environments default="mysql">...</environments>-->
<mappers>
<mapper resource="mappers/BookMapper.xml"/>
</mappers>
</configuration>
单元测试
5.1 添加单元测试依赖
<!-- https://mvnrepository.com/artifact/junit/junit -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
5.2 创建测试类
- 在test目录中创建与被测试类所在包同名的包
- 在创建的包中,创建测试类(测试类名 = 被测试类名+Test)
测试类如图: |
---|
5.3 创建测试方法
在测试类中创建测试方法:
- 测试方法无参数无返回值
- 测试方法名与被测方法名同名(也可以在被测试方法名前加上test)
- 添加@Test注解
测试方法 |
---|
5.4 测试规则
package com.qfedu.dao;
public class BookDAOTest {
@Test
public void testInsertBook() throws IOException {
//1.创建被测试类的对象
//a.获取配置文件的输入流对象
InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
//b.通过SqlSessionFactoryBuilder加载配置文件,构建SqlSession工厂
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(is);
//c.从SqlSession工厂获取SqlSession(sqlSession就相当于JDBC中的Connection)
SqlSession sqlSession = factory.openSession();
//d.从数据库连接(会话:SqlSession)中获取DAO对象
BookDAO bookDAO = sqlSession.getMapper(BookDAO.class);
//2.准备被测试方法所需的参数
Book book = new Book(0, "Java", "二狗", 222.22);
//3.使用被测试类的对象调用被测试方法,并获取返回值
int i = bookDAO.insertBook(book);
sqlSession.commit();
//4.结果断言
//assertEquals(1,i);
System.out.println(i);
}
}
MyBatis工具类封装
package com.qfedu.utils;
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 java.io.IOException;
import java.io.InputStream;
public class MyBatisUtil {
private static SqlSessionFactory factory;
private static final ThreadLocal<SqlSession> local = new ThreadLocal<SqlSession>();
static{
try {
InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
factory = new SqlSessionFactoryBuilder().build(is);
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 获取sqlSession对象
* @return
*/
public static SqlSession openSession(){
SqlSession sqlSession = local.get();
if(sqlSession == null){
//默认sqlSession不自动提交
sqlSession = factory.openSession();
//设置sqlSession自动提交
//sqlSession = factory.openSession(true);
local.set(sqlSession);
}
return sqlSession;
}
/**
* 提交
*/
public static void commit(){
SqlSession sqlSession = openSession();
sqlSession.commit();
closeSession();
}
/**
* 回滚
*/
public static void rollback(){
SqlSession sqlSession = openSession();
sqlSession.rollback();
closeSession();
}
/**
* 关闭sqlSession
*/
public static void closeSession(){
SqlSession sqlSession = openSession();
sqlSession.close();
local.remove();
}
/**
* 获取Mapper对象
* @param clazz
* @param <T>
* @return
*/
public static <T extends Object> T getMapper(Class<T> clazz){
T dao = openSession().getMapper(clazz);
return dao;
}
}
七、MyBatis的CRUD操作
-
DAO接口
public interface BookDAO { public int insertBook(Book book); public int deleteBook(int bookId); public int updateBook(Book book); public Book getBook(int bookId); public List<Book> listBooks(); }
-
Mapper配置
<?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"> <!--namespace = 所需实现的接口全限定名--> <mapper namespace="com.qfedu.dao.BookDAO"> <!-- public int insertBook(Book book);--> <insert id="insertBook"> insert into books(book_name,book_author,book_price) values(#{bookName},#{bookAuthor},#{bookPrice}) </insert> <!-- public int deleteBook(int bookId);--> <delete id="deleteBook"> delete from books where book_id=#{bookId} </delete> <!-- public int updateBook(Book book);--> <update id="updateBook"> update books set book_name=#{bookName}, book_author=#{bookAuthor}, book_price=#{bookPrice} where book_id=#{bookId} </update> <!-- public Book getBook(int bookId);--> <select id="getBook" resultType="com.qfedu.pojo.Book"> select book_id as bookId,book_name as bookName, book_author as bookAuthor,book_price as bookPrice from books where book_id=#{bookId} </select> <!-- public List<Book> listBooks();--> <select id="listBooks" resultSets="java.util.List" resultType="com.qfedu.pojo.Book"> select book_id as bookId,book_name as bookName, book_author as bookAuthor,book_price as bookPrice from books </select> </mapper>
八、MyBatis配置文件
8.1 MyBatis核心配置文件
mybatis-config.xml 主要用于配置MyBatis的连接信息
properties 用于键值对定义
-
在resources中创建mysql.properties,配置如下
jdbc.driver=com.mysql.jdbc.Driver jdbc.url=jdbc:mysql://localhost:3306/db_bims?characterEncoding=utf-8 jdbc.username=root jdbc.password=admin123
-
在mybatis-config.xml 通过properties标签加载mysql.properties文件
<properties resource="mysql.properties"></properties>
-
在datasource配置中直接通过key进行引用
<dataSource type="org.apache.ibatis.datasource.pooled.PooledDataSourceFactory"> <property name="driver" value="${jdbc.driver}"/> <property name="url" value="${jdbc.url}"/> <property name="username" value="${jdbc.username}"/> <property name="password" value="${jdbc.password}"/> </dataSource>
typeAliases 用于配置实体类的别名
<typeAliases>
<typeAlias type="com.qfedu.pojo.Book" alias="Book"/>
</typeAliases>
plugins 用于配置MyBatis插件:分页插件
<!--用于配置MyBatis插件:分页插件-->
<plugins>
<plugin interceptor=""></plugin>
</plugins>
environments 用于配置数据源
<!--在environments标签中可以通过environment配置多个数据源,default属性指定生效的数据库源-->
<environments default="mysql">
<environment id="mysql">
<!--指定mybatis的事务管理方式 JDBC MANAGED
JDBC 表示基于会话的事务管理 ==> SqlSession
MANAGED 表示MyBatis依靠容器完成事务管理
-->
<transactionManager type="JDBC"></transactionManager>
<!--dataSource指定数据源,type用于指定DataSourceFactory,可以配置第三方连接池-->
<dataSource type="org.apache.ibatis.datasource.pooled.PooledDataSourceFactory">
<property name="driver" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</dataSource>
</environment>
</environments>
mappers 用于加载映射文件
<mappers>
<mapper resource="mappers/BookMapper.xml"/>
</mappers>
8.2 MyBatis映射文件
映射文件,就是对DAO接口中数据库操作方法的实现
mapper根标签的namespace属性
- namespace属性用于指定一个DAO接口的全限定名,用于声明当前mapper文件是对哪个接口的实现
catch标签 用于进行缓存配置
insert 标签
用于声明对添加操作的实现
delete标签
用于声明对删除操作的实现
update标签
用于声明对修改操作的实现
select标签
用于声明对查询操作的实现
sql 标签
用于定义sql片段
-
定义sql片段
<sql id="selectFields"> book_id as bookId,book_name as bookName,book_author as bookAuthor,book_price as bookPrice </sql>
-
引用sql片段
<select id="getBook" resultType="Book"> select <include refid="selectFields"/> from books where book_id=#{bookId} </select>
8.3 添加操作配置
8.3.1 DAO方法传递多个参数
-
解决方案一:
public int insertBook2(String bookName,String bookAuthor,double bookPrice);
<insert id="insertBook2"> insert into books(book_name,book_author,book_price) values(#{arg0},#{arg1},#{arg2}) </insert>
-
解决方案二:
public int insertBook2(@Param("name")String bookName, @Param("author")String bookAuthor, @Param("price")double bookPrice);
<insert id="insertBook2"> insert into books(book_name,book_author,book_price) values(#{name},#{author},#{price}) </insert>
8.3.2 主键回填
当执行添加操作时,如果主键是自动生成的,而我们完成添加操作之后想获得主键,可以通过主键回填实现
- 解决方案一:
<insert id="insertBook" useGeneratedKeys="true" keyProperty="bookId"> insert into books(book_name,book_author,book_price) values(#{bookName},#{bookAuthor},#{bookPrice}) </insert>
- 解决方案二:
<insert id="insertBook" > <selectKey keyProperty="bookId"> select uuid() </selectKey> insert into books(book_name,book_author,book_price) values(#{bookName},#{bookAuthor},#{bookPrice}) </insert>
8.4 查询操作配置
8.4.1 指定查询操作返回类型
-
如果查询操作返回的是一条记录,则通过resultType执行封装结果的实体类即可(别名)
<select id="getBook" resultType="Book"> select <include refid="selectFields"/> from books where book_id=#{bookId} </select>
-
如果查询操作返回的是多条记录,
- 通过resultSets指定封装数据的集合类型(java.util.List)
此步骤可以省略
- 需要通过resultType指定一条记录封装的实体对象类型
<select id="listBooks" resultSets="java.util.List" resultType="Book"> select <include refid="selectFields"/> from books </select>
- 通过resultSets指定封装数据的集合类型(java.util.List)
8.4.2 ORM映射
ORM映射 , 从数据表中查询的结果需要封装到实体类对象,我们需要指定一条记录中的字段值和实体类对象的属性的对应关系
-
第一种情况:如果
实体类属性名
和数据表字段名
一一对应,则无需指定映射关系,MyBatis会自定将数据表字段的值封装到实体对象对应的属性中 -
第二种情况:实体类属性名 与 数据表字段名不一致,则需指定映射关系:
-
方案1:通过查询语句给字段设置别名,让别名与实体对象属性名一致即可
<select id="getBook" resultType="Book"> select book_id as bookId,book_name as bookName,book_author as bookAuthor,book_price as bookPrice from books where book_id=#{bookId} </select>
-
方案2:通过结果集映射指定映射关系
<resultMap id="bookMap" type="Book"> <id column="book_id" property="bookId"/> <result column="book_name" property="bookName"/> <result column="book_author" property="bookAuthor"/> <result column="book_price" property="bookPrice"/> </resultMap> <!-- public Book getBook(int bookId);--> <select id="getBook" resultMap="bookMap"> select book_id,book_name,book_author,book_price from books where book_id=#{bookId} </select> <!-- public List<Book> listBooks();--> <select id="listBooks" resultMap="bookMap"> select book_id,book_name,book_author,book_price from books </select>
-
8.5 MyBatis日志配置
日志,程序运行过程的记录
-
添加日志框架依赖——log4j
<dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.17</version> </dependency>
-
在resources目录中创建log4j.properties文件
# Global logging configuration # DEBUG \ INFO \ WARM \ ERROR log4j.rootLogger=DEBUG, stdout # MyBatis logging configuration... log4j.logger.org.mybatis.example.BlogMapper=TRACE # Console output... log4j.appender.stdout=org.apache.log4j.ConsoleAppender log4j.appender.stdout.layout=org.apache.log4j.PatternLayout log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %n%m
级别 描述 ALL LEVEL 打开所有日志记录开关;是最低等级的,用于打开所有日志记录。 DEBUG 输出调试信息;指出细粒度信息事件对调试应用程序是非常有帮助的。 INFO 输出提示信息;消息在粗粒度级别上突出强调应用程序的运行过程。 WARN 输出警告信息;表明会出现潜在错误的情形。 ERROR 输出错误信息;指出虽然发生错误事件,但仍然不影响系统的继续运行。 FATAL 输出致命错误;指出每个严重的错误事件将会导致应用程序的退出。 OFF LEVEL 关闭所有日志记录开关;是最高等级的,用于关闭所有日志记录。
九、关联映射
因为实体与实体之间存在关系 --> 数据表与数据表之间会存在关联关系
9.1 实体与实体的关系
一对一 (用户信息,用户详情)
一对多 (班级 , 学生)
多对一 (学生,班级)
多对多 (学生,课程)
9.2 一对一
用户信息 — 用户详情
-
建库建表
-- 用户信息表 create table users( user_id int primary key auto_increment, user_name varchar(20) not null unique, user_pwd varchar(16) not null ); -- 用户详情表 create table details( detail_id int primary key auto_increment, user_realname varchar(20) not null, user_gender char(2) not null, user_id int not null unique, constraint FK_USER foreign key(user_id) REFERENCES users(user_id) );
-
创建实体类
@Data public class User { private int userId; private String userName; private String userPwd; //一个用户信息包含一个详情信息 private Detail detail; }
@Data public class Detail { private int detailId; private String userRealname; private String userGender; private int userId; }
-
创建DAO接口
public interface UserDAO { public int insertUser(User user); public User getUser(int userId); }
public interface DetailDAO { public int insertDetail(Detail detail); }
添加操作—事务管理
-
创建Mapper映射
<!--UserMapper--> <insert id="insertUser" useGeneratedKeys="true" keyProperty="userId"> insert into users(user_name,user_pwd) values(#{userName},#{userPwd}); </insert>
<!--DetailMapper--> <insert id="insertDetail" useGeneratedKeys="true" keyProperty="detailId"> insert into details(user_realname,user_gender,user_id) values(#{userRealname},#{userGender},#{userId}); </insert>
-
测试添加操作
@Test public void insertUser() { Detail detail = new Detail(0, "王三胖", "真的不详", 0); User user = new User(0,"hehehe","123456",detail); try{ UserDAO userDAO = MyBatisUtil.getMapper(UserDAO.class); int i = userDAO.insertUser(user); System.out.println(user.getUserId()); detail.setUserId(user.getUserId()); DetailDAO detailDAO = MyBatisUtil.getMapper(DetailDAO.class); int j = detailDAO.insertDetail(detail); MyBatisUtil.commit(); System.out.println(i+"---"+j); }catch (Exception e){ MyBatisUtil.rollback(); } }
查询操作—联合查询
-
连接查询
<resultMap id="userMap" type="User"> <id column="user_id" property="userId"/> <result column="user_name" property="userName"/> <result column="user_pwd" property="userPwd"/> <result column="detail_id" property="detail.detailId"/> <result column="user_realname" property="detail.userRealname"/> <result column="user_gender" property="detail.userGender"/> <result column="user_id" property="detail.userId"/> </resultMap> <!-- public User getUser(int userId);--> <select id="getUser" resultMap="userMap"> select u.*,d.detail_id,d.user_realname,d.user_gender from users u INNER JOIN details d on u.user_id=d.user_id where u.user_id=#{userId}; </select>
<resultMap id="userMap2" type="User"> <id column="user_id" property="userId"/> <result column="user_name" property="userName"/> <result column="user_pwd" property="userPwd"/> <association property="detail" javaType="Detail"> <result column="detail_id" property="detailId"/> <result column="user_realname" property="userRealname"/> <result column="user_gender" property="userGender"/> <result column="user_id" property="userId"/> </association> </resultMap> <!-- public User getUser(int userId);--> <select id="getUser" resultMap="userMap2"> select u.*,d.detail_id,d.user_realname,d.user_gender from users u INNER JOIN details d on u.user_id=d.user_id where u.user_id=#{userId}; </select>
-
嵌套查询
- DetailMapper
<resultMap id="detailMap" type="Detail"> <id column="detail_id" property="detailId"/> <result column="user_realname" property="userRealname"/> <result column="user_gender" property="userGender"/> <result column="user_id" property="userId"/> </resultMap> <!-- public Detail getDetailByUserId(int userId);--> <select id="getDetailByUserId" resultMap="detailMap"> select * from details where user_id=#{userId} </select>
- UserMapper
<resultMap id="userMap3" type="User"> <id column="user_id" property="userId"/> <result column="user_name" property="userName"/> <result column="user_pwd" property="userPwd"/> <association property="detail" column="user_id" select="com.qfedu.dao.DetailDAO.getDetailByUserId"/> </resultMap> <!-- public User getUser(int userId);--> <select id="getUser" resultMap="userMap3"> select * from users where user_id=#{userId}; </select>
9.3 一对多、多对一
班级 — 学生 : 在多的一端添加外键与一的一端主键进行关联
9.3.1 建表
create table classes(
cid int primary key auto_increment,
cname varchar(30) not null unique,
cdesc varchar(100)
);
create table students(
sid char(5) primary key,
sname varchar(20) not null,
sage int not null,
cid int not null,
constraint FK_CLASSES foreign key(cid) references classes(cid)
);
9.3.2 创建实体类
从一的一端体现关系(一对多):
-
班级(一):
public class Clazz{ private int classId; private String className; private String classDesc; private List<Student> stus; }
-
学生(多):
public class Student{ private String stuId; private String stuName; private int stuAge; }
从多的一端体现关系(多对一):
-
班级(一):
public class Clazz{ private int classId; private String className; private String classDesc; }
-
学生(多):
public class Student{ private String stuId; private String stuName; private int stuAge; private Clazz clazz; }
9.3.3 添加操作
@Test
public void insertClass() {
List<Student> stus = new ArrayList<Student>();
stus.add(new Student("10001","张三",20,0));
stus.add(new Student("10002","李四",19,0));
stus.add(new Student("10003","二狗",18,0));
Clazz cla = new Clazz(0,"Java2001","777",stus);
try {
ClazzDAO clazzDAO = MyBatisUtil.getMapper(ClazzDAO.class);
StudentDAO studentDAO = MyBatisUtil.getMapper(StudentDAO.class);
int i = clazzDAO.insertClass(cla);
for (Student s : cla.getStus()) {
s.setCid(cla.getClassId());
studentDAO.insertStudent(s);
}
MyBatisUtil.commit();
}catch (Exception e){
MyBatisUtil.rollback();
}
}
9.3.4 查询操作
一对多查询 查询班级同时级联查询到所有的学生
-
连接查询
public interface ClazzDAO { public Clazz getClazz(int cid); }
<resultMap id="classMap" type="Clazz"> <id column="cid" property="classId"/> <result column="cname" property="className"/> <result column="cdesc" property="classDesc"/> <collection property="stus" ofType="Student"> <result column="sid" property="stuId"/> <result column="sname" property="stuName"/> <result column="sage" property="stuAge"/> <result column="cid" property="cid"/> </collection> </resultMap> <!-- public Clazz getClazz(int cid);--> <select id="getClazz" resultMap="classMap"> select c.*, s.sid, s.sname, s.sage from classes c INNER JOIN students s on c.cid = s.cid where c.cid = #{cid} </select>
-
嵌套查询
<resultMap id="studentMap" type="Student"> <id column="sid" property="stuId"/> <result column="sname" property="stuName"/> <result column="sage" property="stuAge"/> <result column="cid" property="cid"/> </resultMap> <!-- public List<Student> listStudentsByCid(int cid);--> <select id="listStudentsByCid" resultMap="studentMap"> select * from students where cid=#{cid} </select>
<resultMap id="classMap" type="Clazz"> <id column="cid" property="classId"/> <result column="cname" property="className"/> <result column="cdesc" property="classDesc"/> <collection property="stus" column="cid" select="com.qfedu.dao.StudentDAO.listStudentsByCid"/> </resultMap> <!-- public Clazz getClazz(int cid);--> <select id="getClazz" resultMap="classMap"> select * from classes where cid=#{cid} </select>
多对一查询 查询学生的时候级联查询此学生对应的班级
-
连接查询
<resultMap id="studentMap" type="Student"> <id column="sid" property="stuId"/> <result column="sname" property="stuName"/> <result column="sage" property="stuAge"/> <association property="clazz" javaType="Clazz"> <result column="cid" property="classId"/> <result column="cname" property="className"/> <result column="cdesc" property="classDesc"/> </association> </resultMap> <!-- public Student getStudent(String sid);--> <select id="getStudent" resultMap="studentMap"> select s.*,c.cname,c.cdesc from students s inner join classes c on s.cid = c.cid where sid=#{sid} </select>
-
嵌套查询(Java)
<resultMap id="classMap" type="Clazz"> <id column="cid" property="classId"/> <result column="cname" property="className"/> <result column="cdesc" property="classDesc"/> </resultMap> <!-- public Clazz getClassByCid(int cid);--> <select id="getClassByCid" resultMap="classMap"> select * from classes where cid=#{cid} </select>
<resultMap id="studentMap" type="Student"> <id column="sid" property="stuId"/> <result column="sname" property="stuName"/> <result column="sage" property="stuAge"/> <association property="clazz" column="cid" select="com.qfedu.dao.ClazzDAO.getClassByCid"/> </resultMap> <!-- public Student getStudent(String sid);--> <select id="getStudent" resultMap="studentMap"> select * from students where sid=#{sid} </select>
9.4 多对多
学生 — 课程 : 建立第三张表(关系表)
9.4.1 建表
create table students(
sid char(5) primary key,
sname varchar(20) not null
);
create table courses(
course_id int primary key auto_increment,
course_name varchar(100) not null
);
create table sc(
sid char(5),
cid int,
constraint FK_STUDENTS foreign key(sid) references students(sid),
constraint FK_COURSES foreign key(cid) references courses(course_id)
)
9.4.2 实体类
- 关系表仅仅维护 学生 和 课程 的 多对多关系,此时关系表就没有对应的实体类
- 关系表不仅维护 学生 和 课程 的 多对多关系,还提供了其他数据的存储 ==> 使用成绩表维护学生和课程的 多对多关系;在这种情况下,关系表需要对应实体类
十、动态SQL
根据条件动态完成SQL的拼接
10.1 sql片段
<sql id="fieldNames">
id,nick,gender,imgurl,age,height, city,mdesc
</sql>
<select id="searchMembers" resultType="Member">
select <include refid="fieldNames"/> from members
</select>
10.2 if
<select id="searchMembers" resultType="Member">
select <include refid="fieldNames"/> from members where 1=1
<if test="gender != null">
and gender=#{gender}
</if>
<if test="city != null">
and city=#{city}
</if>
</select>
10.3 where
<select id="searchMembers" resultType="Member">
select <include refid="fieldNames"/> from members
<where>
<if test="gender != null">
and gender=#{gender}
</if>
<if test="minAge != null">
and age >= #{minAge}
</if>
<if test="maxAge != null">
and age <= #{maxAge}
</if>
<if test="city != null">
and city=#{city}
</if>
</where>
</select>
10.4 trim
<select id="searchMembers" resultType="Member">
select <include refid="fieldNames"/> from members
<trim prefix="where" prefixOverrides="AND|OR">
<if test="gender != null">
and gender=#{gender}
</if>
<if test="minAge != null">
and age >= #{minAge}
</if>
<if test="maxAge != null">
and age <= #{maxAge}
</if>
<if test="city != null">
and city=#{city}
</if>
</trim>
</select>
10.5 foreach
<select id="">
select * from members where id in
<foreach collection="list" item="id" open="(" separator="," close=")">
#{id}
</foreach>
</select>
十一、缓存
缓存,就是内存中的一块存储空间,作用是将频繁操作的数据进行缓存,便于提升查询速度
11.1 一级缓存
一级缓存也称之为SqlSession级缓存,同一个SqlSession进行多次查询相同数据,会将数据保存在一级缓存中
11.2 二级缓存
二级缓存,也称为SqlSessionFactory缓存,需要通过配置开启;开启以后当前SqlSessionFactory创建的SqlSession可以共享这个二级缓存
-
在mybatis-config.xml配置开启二级缓存
<settings> <setting name="cacheEnabled" value="true"/> </settings>
-
在需要缓存的Mapper文件中配置缓存
<cache/>
-
存储在二级缓存的对象需要实现序列化接口
public class Member implements Serializable { //... }
十二、整合druid连接池
MyBatis也提供了对第三方连接池的整合
-
添加druid依赖
<!-- https://mvnrepository.com/artifact/com.alibaba/druid --> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.1.6</version> </dependency>
-
创建配置类
public class DruidDataSourceFactory extends PooledDataSourceFactory { public DruidDataSourceFactory() { this.dataSource = new DruidDataSource(); } }
-
在mybatis-config.xml进行配置(dataSource中使用的是DruidDataSource,属性名要使用DruidDataSource的属性名)
<environment id="mysql"> <transactionManager type="JDBC"></transactionManager> <dataSource type="com.qfedu.utils.DruidDataSourceFactory"> <!--dataSource ==> DruidDataSource --> <property name="driverClass" value="${jdbc.driver}"/> <property name="jdbcUrl" value="${jdbc.url}"/> <property name="username" value="${jdbc.username}"/> <property name="password" value="${jdbc.password}"/> </dataSource> </environment>
十三、分页插件
PageHelper是一款适用于MyBatis的分页插件
13.1 添加插件依赖
<!-- https://mvnrepository.com/artifact/com.github.pagehelper/pagehelper -->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>5.1.10</version>
</dependency>
13.2 配置分页拦截器
<plugins>
<plugin interceptor="com.github.pagehelper.PageInterceptor"></plugin>
</plugins>
十四、MyBatis的注解使用
1. 建表
略
2.创建实体类
@Data
@AllArgsConstructor
@NoArgsConstructor
@ToString
public class Book {
private int bookId;
private String bookName;
private String bookAuthor;
private double bookPrice;
}
3.创建DAO接口
public interface BookDAO {
public int insertBook(Book book);
public int deleteBook(int bookId);
public int updateBook(Book book);
public Book getBook(int bookId);
}
4.在DAO方法上添加注解
public interface BookDAO {
@Insert("insert into books(book_name,book_author,book_price) values(#{bookName},#{bookAuthor},#{bookPrice})")
public int insertBook(Book book);
@Delete("delete from books where book_id=#{bookId}")
public int deleteBook(int bookId);
//@Update("sql")
// public int updateBook(Book book);
@Select("select book_id as bookId,book_name as bookName,book_author as bookAuthor," +
"book_price as bookPrice from books where book_id=#{bookId}")
public Book getBook(int bookId);
}
5.将DAO接口配置到mybatis-config.xml
<mappers>
<mapper class="com.qfedu.dao.BookDAO"/>
</mappers>
十五、延迟加载
延迟加载 —— 如果开启了延迟加载,在嵌套关联查询中,默认先执行最上层的查询不执行关联查询,当我们需要使用关联数据时,才发送查询请求执行查询。
如何启用延迟加载:
<settings>
<setting name="lazyLoadingEnabled" value="true"/>
</settings>
十六、$和#
${} 和 #{} 都可以用来获取DAO方法传递的参数
${} 表示将获取的参数拼接到SQL语句,可能导致SQL注入
#{}表示SQL采用预编译,然后在进行参数的赋值,可以避免SQL的注入问题
2020.07.27 练习任务
1. 使用MyBatis完成单表的CRUD
- 添加操作(主键回填)
- 根据主键删除
- 根据主键修改其他字段
- 根据主键查询一条记录
- 查询所有记录
- 分页
- 统计总记录数
- 分页查询
2. 一对一关联练习
- 完成课堂案例(自己列举一个一对一案例,然后完成)
2020年7月28日 练习任务
1. 一对多和多对多关联练习
- 课堂案例
2. 动态sql案例
- 珍爱网会员搜索
- 使用Druid连接池
- 使用PageHelper插件分页
blic Book getBook(int bookId);
}
#### 4.在DAO方法上添加注解
```java
public interface BookDAO {
@Insert("insert into books(book_name,book_author,book_price) values(#{bookName},#{bookAuthor},#{bookPrice})")
public int insertBook(Book book);
@Delete("delete from books where book_id=#{bookId}")
public int deleteBook(int bookId);
//@Update("sql")
// public int updateBook(Book book);
@Select("select book_id as bookId,book_name as bookName,book_author as bookAuthor," +
"book_price as bookPrice from books where book_id=#{bookId}")
public Book getBook(int bookId);
}
5.将DAO接口配置到mybatis-config.xml
<mappers>
<mapper class="com.qfedu.dao.BookDAO"/>
</mappers>
十五、延迟加载
延迟加载 —— 如果开启了延迟加载,在嵌套关联查询中,默认先执行最上层的查询不执行关联查询,当我们需要使用关联数据时,才发送查询请求执行查询。
如何启用延迟加载:
<settings>
<setting name="lazyLoadingEnabled" value="true"/>
</settings>
[外链图片转存中…(img-mcSe57CX-1596019169831)]
十六、$和#
${} 和 #{} 都可以用来获取DAO方法传递的参数
${} 表示将获取的参数拼接到SQL语句,可能导致SQL注入
#{}表示SQL采用预编译,然后在进行参数的赋值,可以避免SQL的注入问题