🍅笔记来源于B站动力节点视频
🍅老师讲的很不错,通俗易懂,入门级学习。点击跳转
文章目录
MyBatis 框架
第一章 框架的概述
1.三层架构
mvc:web开发中,使用mvc架构模式。 m:数据, v:视图, c:控制器。
c控制器: 接收请求,调用service对象,显示请求的处理结果。 当前使用servlet作为控制器
v视图: 现在使用jsp, html,css,js。 显示请求的处理结果,把m中数据显示出来。
m数据: 来自数据库mysql, 来自文件,来自网络
mvc作用:
1)实现解耦合。
2)让mvc 各负其职。
3)使的系统扩展更好。更容易维护。
三层架构:
1.界面层(视图层):接收用户的请求,调用service, 显示请求的处理结果的。 包含了jsp,html,servlet等对象。 对应的包controller,
2.业务逻辑层:处理业务逻辑, 使用算法处理数据的。 把数据返回给界面层。 对应的是service包,和包中的很多的XXXService类。 例如: StudentService , OrderService, ShopService
3.持久层(数据库访问层):访问数据库,或者读取文件,访问网络。获取数据。 对应的包是dao。 dao包中很多的StudentDao, OrderDao, ShopDao等等。
2. 三层架构请求的处理流程
用户发起请求---->界面层----->业务逻辑层---->持久层---->数据库(mysql)
3. 为什么要使用三层?
1,结构清晰、耦合度低, 各层分工明确
2,可维护性高,可扩展性高
3,有利于标准化
4,开发人员可以只关注整个结构中的其中某一层的功能实现
5,有利于各层逻辑的复用
4. 三层架构模式和框架
每一层对应着一个框架
1)界面层—SpringMVC框架
2)业务层—Spring框架
3)持久层—MyBatis框架
5 .框架
- 什么是框架(framework)
框架:就是一个软件, 完成了部分的功能。 软件中的类和类之间的方法调用都已经规定好了。 通过这些可以完成某些功能。 框架看做是模版。
框架是可以升级的,改造的。 框架是安全的。
框架是对某一个方面有用的,不是全能的。
6. 框架解决的问题
1)框架能实现技术的整合。
2)提供开发的效率。 降低难度。
7. jdbc访问数据库的优缺点
传统的JDBC编程方式
public void findStudent() {
Connection conn = null;
Statement stmt = null;
ResultSet rs = null;
try {
//注册 mysql 驱动
Class.forName("com.mysql.jdbc.Driver");
//连接数据的基本信息 url ,username,password
String url = "jdbc:mysql://localhost:3306/springdb";
String username = "root";
String password = "123456";
//创建连接对象
conn = DriverManager.getConnection(url, username,
password);
//保存查询结果
List<Student> stuList = new ArrayList<>();
//创建 Statement, 用来执行 sql 语句
stmt = conn.createStatement();
//执行查询,创建记录集,
rs = stmt.executeQuery("select * from student");
while (rs.next()) {
Student stu = new Student();
stu.setId(rs.getInt("id"));
stu.setName(rs.getString("name"));
stu.setAge(rs.getInt("age"));
//从数据库取出数据转为 Student 对象,封装到 List 集合
stuList.add(stu);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
//关闭资源
if (rs != null) ;
{
rs.close();
}
if (stmt != null) {
stmt.close();
}
if (conn != null) {
conn.close();
}
} catch (Exception e) {
e.printStackTrace();
}
} }
优点:
- 直观,好理解
缺点:
- 创建很多对象 Connection ,Statement, ResultSet
- 注册驱动
- 执行sql语句
- 把ResultSet转为 Student , List集合。
- 关闭资源
- sql语句和业务逻辑代码混在一起
8 MyBatis框架
什么 mybatis: 是一个持久层框架, 原名是ibatis, 2013改名为 MyBatis. MyBatis可以操作数据库,对数据执行增删改查。 看做是高级的jdbc。 解决jdbc的缺点。
mybatis能做什么?
1) 注册驱动 。
2) 创建jdbc中使用的Connection, Statement,ResultSet
-
执行sql语句, 得到ResultSet
-
处理ResultSet, 把记录集中的数据转为java对象, 同时还能把java对象放入到List集合。
5)关闭资源
6)实现sql语句和java代码的解耦合。
mybatis的文档: https://mybatis.org/mybatis-3/zh/index.html
第二章 MyBatis入门
2.1 第一个例子
实现步骤:
0.创建student表(id,name,email,age)
1.新建maven项目
2.修改pom.xml
1)加入依赖 mybatis依赖, mysql驱动, junit
2)在加入资源插件
<dependencies>
<!--mybatis-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.1</version>
</dependency>
<!--mysql驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.22</version>
</dependency>
<!--单元测试-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.1</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<!--资源插件:处理src/main/java目录中的xml-->
<resources>
<resource>
<directory>src/main/java</directory><!--所在的目录-->
<includes><!--包括目录下的.properties,.xml 文件都会扫描到-->
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>false</filtering>
</resource>
</resources>
</build>
3.创建实体类Student。定义属性, 属性名和列名保持一致
4.创建Dao接口, 定义操作数据库的方法。
public interface StudentDao {
//查询一个学生
Student selectStudentById(Integer id);
}
5.创建xml文件(mapper文件), 写sql语句。
mybatis框架推荐是把sql语句和java代码分开
mapper文件:定义和dao接口在同一目录, 一个表一个mapper文件。
1.约束文件http://mybatis.org/dtd/mybatis-3-mapper.dtd
约束文件的作用:定义和限制当前文件可以使用的标签和属性,以及标签出现的顺序2.mapper是根标签,namespace:命名空间,不能为空,唯一值
推荐使用Dao接口的权限定名称
作用:参与识别sql语句的作用3.在mapper中可以写< insert>,< delete>,< update>,< select>等标签
分别是增删改查操作< insert>,< delete>,< update>,< select>
id:要执行的sql语句的唯一标识,是自定义的字符串,推荐使用dao接口中的方法名称
(如果你使用了Dao接口层,那么是要一致的,因为,mybaits是通过 mapping里面的namespace+select id来映射到你的Dao接口的。如果你不使用Dao接口层,而是直接使用SqlSessionFactory来自己处理数据访问逻辑,那么你可以自定义select id,但是保证 namespace+select id 的唯一性。) 这里使用的是后者
resultType:告诉mybatis,执行sql语句,把数据赋值给那一个类型的java对象
resultType的值现在使用的java对象的全限定名称
<?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="pdsu.edu.mybatis.dao.StudentDao">
<!-- 查询所有学生student的方法
<!--如果你使用了Dao接口层,那么是要一致的,因为,
mybaits是通过 mapping里面的namespace+select id来映射到你的Dao接口的。
如果你不使用Dao接口层,而是直接使用SqlSessionFactory来自己处理数据访问逻辑,
那么你可以自定义select id,但是要保证 namespace+select id 的唯一性。-->
<select id="selectStudentById" resultType="pdsu.edu.mybatis.domain.Student">
select *
from student
</select>
</mapper>
6.创建mybatis的主配置文件(xml文件):有一个, 放在resources目录下
1)定义创建连接实例的数据源(DataSource)对象
- 指定其他mapper文件的位置
使用mapper的resource属性指定mapper文件的路径这个路径是从target/classes路径开启的
使用注意:
resource = “mapper文件的路径,使用 / 分割 , 一个mapper resource指定一个mapper文件”
<?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>
<!--设置日志-->
<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
<!--配置 mybatis 环境-->
<environments default="development">
<!--id:数据源的名称-->
<environment id="development">
<!--配置事务类型:使用 JDBC 事务(使用 Connection 的提交和回滚)-->
<transactionManager type="JDBC"/>
<!--配置数据源:创建Connection对象-->
<dataSource type="POOLED">
<!--driver:驱动的内容-->
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<!--连接数据库的内容-->
<property name="url"
value="jdbc:mysql://localhost:3306/springdb?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone = GMT"/>
<!--用户名-->
<property name="username" value="root"/>
<!--密码-->
<property name="password" value="root"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="pdsu/edu/mybatis/dao/StudentDao.xml"/>
</mappers>
</configuration>
7.创建测试的内容。
使用main方法,测试mybatis访问数据库
也可以使用junit 访问数据库
junit测试方法:
查询操作
@Test
public void testSelectStudent() throws IOException {
//调用mybatis某个对象的方法,执行mapper文件中的是sql语句
//mybatis核心类:SQLSessionFactory
//1.定义mybatis主文件的配置文件的位置,从类路径的相对路径
String config = "mybatis.xml";
//2.读取主配置文件,使用mybatis框架中的Resource类
InputStream inputStream = Resources.getResourceAsStream(config);
//3.创建SQLSessionFactory对象,使用SQLSessionFactoryBuilder类
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(inputStream);
//4.获取SQLSession对象
SqlSession session = factory.openSession();
//5.指定要执行的sql语句的id
//sql的id = namespace+"."+select|update|insert|delete标签的id的值
String sqlId = "pdsu.edu.mybatis.dao.StudentDao"+"."+"selectStudentById";
//6.通过SqlSession的方法,执行sql语句
Student student = session.selectOne(sqlId);
System.out.println("使用mybatis查询一个学生:"+student);
//7.关闭SqlSession对象
session.close();
}
另:
<!--可以传入参数,查询具体的那一条数据-->
Student student = session.selectOne(sqlId,2);
<!--#{studentId} :占位符,表示从java程序中传入过来的数据-->
<select id="selectStudentById" resultType="pdsu.edu.mybatis.domain.Student">
select *
from student where id = #{studentId}
</select>
添加操作
传入java对象 ,使用#{属性名} 获取此属性的值
属性值放到#{}占位符的位置,mybatis执行的此属性,对应的getXXX()
第一种不带参数:
int rows = session.insert(sqlId);
<insert id="insertStudent">
insert into student values (4,"王二","wanger@qq.com",20)
</insert>
第二种带参数:
Student student = new Student();
student.setId(5);
student.setName("麻子");
student.setEmail("mazi@qq.com");
student.setAge(30);
int rows = session.insert(sqlId,student);
<insert id="insertStudent">
insert into student
values (#{id}, #{name}, #{email}, #{age})
</insert>
2.2 概念
1.自动提交:当你的 sql语句执行完毕后, 提交事务。 数据库更新操作之间保存到数据
2.手动(手工)提交事务:在你需要提交事务的位置, 执行方法,提交事务或者回顾事务。
mybatis默认提交sql语句的时候 手工提交事务模式,在insert,update,delete中添加事务
2.3 MyBatis的一些重要对象
1) Resources : mybatis框架中的对象, 一个作用读取 主配置信息。
InputStream inputStream = Resources.getResourceAsStream("mybatis.xml");
2)SqlSessionFactoryBuilder:负责创建SqlSessionFactory对象
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(inputStream);
3)SqlSessionFactory: 重要对象
SqlSessionFactory是重量级对象:创建此对象需要使用更多的资源和时间。 在项目中有一个就可以了。
SqlSessionFactory接口:作用是SqlSession的工厂, 就是创建SqlSession对象。
DefaultSqlSessionFactory实现类
public class DefaultSqlSessionFactory implements SqlSessionFactory { }
SqlSessionFactory接口中的方法
openSession(): 获取一个默认的SqlSession对象, 默认是需要手工提交事务的。
openSession(boolean): boolean参数表示是否自动提交事务。
true: 创建一个自动提交事务的SqlSession
false: 等同于没有参数的openSession
- SqlSession对象
SqlSession对象是通过SqlSessionFactory获取的。 SqlSession本身是接口
DefaultSqlSession: 实现类
public class DefaultSqlSession implements SqlSession { }
SqlSession作用是提供了大量的执行sql语句的方法:
selectOne:执行sql语句,最多得到一行记录,多余1行是错误。
selectList:执行sql语句,返回多行数据
selectMap:执行sql语句的,得到一个Map结果
insert:执行insert语句
update:执行update语句
delete:执行delete语句
commit:提交事务
rollback:回顾事务
注意SqlSession对象不是线程安全的, 使用的步骤:
①:在方法的内部,执行sql语句之前,先获取SqlSession对象
②:调用SqlSession的方法,执行sql语句
③:关闭SqlSession对象,执行SqlSession.close()
2.4 使用模版和工具类
2.4.1 创建模板
1)创建模版,mapper文件模版和mybatis主配置文件模版
创建模版的步骤:
创建模版文件:
创建文件选择使用的模版:
2.4.2 创建 MyBatisUtil 类
package pdsu.edu.mybatis.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;
/*
* 工具类:创建SqlSession对象
* */
public class MybatisUtil {
private static SqlSessionFactory factory = null;
static {
String config = "mybatis.xml";
try{
InputStream inputStream = Resources.getResourceAsStream(config);
factory = new SqlSessionFactoryBuilder().build(inputStream);
}catch (IOException e){
e.printStackTrace();
}
}
//创建方法:SqlSession对象
public static SqlSession getSqlSession(){
SqlSession session = null;
if (factory!=null){
session = factory.openSession();//
}
return session;
}
}
使用 MyBatisUtil 类
/*查询某个学生*/
@Test
public void testSelectById() {
//1.获取SqlSession对象
SqlSession session = MybatisUtil.getSqlSession();
//2.指定sqlId
String sqlId = "pdsu.edu.mybatis.dao.StudentDao.selectById";
//3.执行SqlSession的方法,表示执行sql对象
Student student = session.selectOne(sqlId, 1);
//4.关闭SqlSession对象
session.close();
}
2.5 MyBatis 使用传统 Dao 开发方式
使用 Dao 的实现类(StudentDaoImpl),操作数据库
2.5.1 Dao开发
(1)Dao接口
public interface StudentDao {
//查询一个学生
Student selectById(Integer id);
}
(2)创建Dao接口实现类并实现接口中的方法
public class StudentDaoImpl implements StudentDao {
//查询某个学生
@Override
public Student selectById(Integer id) {
SqlSession session = MybatisUtil.getSqlSession();
String sqlId = "pdsu.edu.mybatis.dao.StudentDao.selectById";
Student student = session.selectOne(sqlId, 3);
session.close();
return student;
}
}
(3)测试查询操作:
/*查询某个学生*/
@Test
public void testSelectOne() {
//使用dao的方法
//接口类型 变量= new 接口的实现类();
StudentDao dao = new StudentDaoImpl();
Student student = dao.selectById(5);
System.out.println("通过dao执行方法,得到的对象==学生实体" + student);
}
2.5.2 传统 Dao 开发方式的分析
在前面例子中自定义 Dao 接口实现类时发现一个问题:Dao 的实现类其实并没有干什么实质性的工作,它仅仅就是通过 SqlSession 的相关 API 定位到映射文件 mapper 中相应 id 的 SQL 语句,真正对 DB 进行操作的工作其实是由框架通过 mapper 中的 SQL 完成的。
所以,MyBatis 框架就抛开了 Dao 的实现类,直接定位到映射文件mapper 中的相应 SQL 语句,对 DB 进行操作。这种对 Dao 的实现方式称为Mapper 的动态代理方式。
Mapper 动态代理方式无需程序员实现 Dao 接口。接口是由 MyBatis 结合映射文件自动生成的动态代理实现的。
第三章 MyBatis的Dao代理
3.1 dao代理
dao代理技术:由mybatis创建StudentDao接口实现Proxy(StudentDaoImpl),实现框架创建的studentDaoImpl代替
你自己手工实现的StudentDaoImpl类的功能,不用开发人员写dao接口的实现类
去掉 Dao 接口实现类
3.1.1 mybatis提供代理:
mybatis创建Dao接口的实现类对象, 完成对sql语句的执行。 mybatis创建一个对象代替你的 dao实现类功能。
3.1.2 使用mybatis代理要求
1)mapper文件中的namespace 一定dao接口的全限定名称
2)mapper文件中 标签的id是dao接口方法名称
3.1.3 mybatis代理实现方式
使用SqlSession对象的方法 getMapper(dao.class)
例如: 现在有 StudentDao接口。
/*查询单个学生*/
@Test
public void testSelectById() {
//1.获取SqlSession对象
SqlSession session = MybatisUtil.getSqlSession();
//2.获取dao代理
StudentDao dao = session.getMapper(StudentDao.class);
Student student1 = dao.selectById(1);
System.out.println(student1);
//3.关闭SqlSession对象
session.close();
}
//上面代码中,省去了dao包的实现类
StudentDao dao = session.getMapper(StudentDao.class);
等同于
StudentDao dao = new StudentDaoImpl();
3.2 理解参数
理解参数是: 通过java程序把数据传入到mapper文件中的sql语句。 参数主要是指dao接口方法的形参
3.2.1 parameterType
parameterType:表示参数的类型, 指定dao方法的形参数据类型。 这个形参的数据类型是给mybatis使用。 mybatis在给sql语句的参数赋值时使用。 PreparedStatement.setXXX( 位置, 值)
mybatis执行的sql语句:select * from student where id = ?
?是占位符,使用jdbc中preparedStatement执行这样的sql语句
PreparedStatement pst = conn.prepareStatement(“select * from student where id = ?”)给 ? 位置赋值
参数Integer , 执行pst.setInt(位置,值);
参数String , 执行pst.setString(位置,“值”)
第一个用法: java类型的全限定类型名称 parameterType="java.lang.Integer"
第二个用法: mybatis定义的java类型的别名 parameterType="int"
parameterType:mybatis通过反射机制可以获取 dao接口方法参数的类型, 可以不写
<select id="selectById" parameterType="integer"
resultType="com.bjpowernode.domain.Student">
select id,name,email,age from student where id=#{studentId}
</select>
下面是一些为常见的 Java 类型内建的类型别名。它们都是不区分大小写的,注意,为了应对原始类型的命名重复,采取了特殊的命名风格。
别名 | 映射的类型 |
---|---|
_byte | byte |
_long | long |
_short | short |
_int | int |
_integer | int |
_double | double |
_float | float |
_boolean | boolean |
string | String |
byte | Byte |
long | Long |
short | Short |
int | Integer |
integer | Integer |
double | Double |
float | Float |
boolean | Boolean |
date | Date |
decimal | BigDecimal |
bigdecimal | BigDecimal |
object | Object |
map | Map |
hashmap | HashMap |
list | List |
arraylist | ArrayList |
collection | Collection |
iterator | Iterator |
3.2.2 dao接口方法是一个简单类型的参数
//dao接口的方法形参是一个简单类型的
//简单类型: java基本数据类型和String
Student selectByEmail(String email);
<!--
dao接口是一个简单类型的参数
mapper文件,获取这个参数值,使用#{任意字符}
-->
<select id="selectByEmail" resultType="com.bjpowernode.domain.Student">
select id,name,email,age from student where email=#{studentEmail}
</select>
3.2.3 dao接口方法有多个简单类型的参数
@Param: 命名参数, 在方法的形参前面使用的, 定义参数名。 这个名称可以用在mapper文件中。
dao接口,方法的定义
/*
多个简单类型的参数
使用@Param命名参数, 注解是mybatis提供的
位置:在形参定义的前面
属性:value 自定义的参数名称
*/
List<Student> selectByNameOrAge(@Param("myname") String name,
@Param("myage") Integer age);
mapper文件
<!--
多个简单类型的参数.
当使用了@Param命名后,例如@Param("myname").
在mapper中,使用#{命名的参数}, 例如 #{myname}
-->
<select id="selectByNameOrAge" resultType="com.bjpowernode.domain.Student">
select id,name,email,age from student where name=#{myname} or age=#{myage}
</select>
3.2.4 dao接口方法使用一个对象作为参数
方法的形参是一个java对象。这个java对象表示多个参数。使用对象的属性值作为参数使用
java对象
public class Student {
private Integer id;
private String name;
private String email;
private Integer age;
//set|get方法
}
public class QueryParam {
private Object p1;
private Object p2;
//set|get方法
}
dao接口中的方法定义
/*
* 一个java对象作为参数( 对象由属性, 每个属性有set,get方法)
*/
List<Student> selectByObject(Student student);
List<Student> selectByQueryParam(QueryParam param);
mapper文件
<!--
一个java对象作为方法的参数,使用对象的属性作为参数值使用
简单的语法: #{属性名} , mybatis调用此属性的getXXX()方法获取属性值
-->
<select id="selectByObject" resultType="com.bjpowernode.domain.Student">
select id,name,email,age from student where name=#{name} or age=#{age}
</select>
<select id="selectByQueryParam" resultType="com.bjpowernode.domain.Student">
select id,name,email,age from student where name=#{p1} or age=#{p2}
</select>
<!--负责的语法格式: #{属性名,javaType=java类型的全限定名称,jdbcType=mybatis中定义列的数据类型}-->
<select id="selectByObject" resultType="com.bjpowernode.domain.Student">
select id,name,email,age from student where
name=#{name,javaType=java.lang.String,jdbcType=VARCHAR}
or
age=#{age,javaType=java.lang.Integer,jdbcType=INTEGER}
</select>
3.2.5 dao接口中多个简单类型的参数,使用位置
参数位置: dao接口中方法的形参列表,从左往右,参数位置是 0 , 1, 2…
语法格式:#{arg0} ,#{arg1}
dao接口的方法
/*
使用位置,获取参数
*/
List<Student> selectByPosition(String name,Integer age);
<!--
mybatis版本是 3.5.1
使用位置获取参数值, dao接口方法是多个简单类型的参数
语法: #{arg0}, #{arg1}....
-->
<select id="selectByPosition" resultType="com.bjpowernode.domain.Student">
select id,name,email,age from student where name=#{arg0} or age=#{arg1}
</select>
3.2.6 dao接口参数是一个Map
map作为dao接口的参数, 使用 key 获取参数值,mapper文件中,语法格式 #{key}
/*
使用Map作为参数
*/
List<Student> selectStudentByMap(Map<String,Object> map);
mapper文件
<!--
使用Map传递参数,
在mapper文件中,获取map的值,是通过key获取的,语法:#{key}
-->
<select id="selectStudentByMap" resultType="com.bjpowernode.domain.Student">
select id,name,email,age from student where name=#{myname} or age=#{myage}
</select>
测试,调用方法的位置
@Test
public void testSelectByMap(){
SqlSession sqlSession = MyBatisUtil.getSqlSession();
StudentDao dao = sqlSession.getMapper(StudentDao.class);
//使用map传递参数
Map<String,Object> data = new HashMap<>();
data.put("myname", "李思思");
data.put("myage", 20);
List<Student> students = dao.selectStudentByMap(data);
students.forEach( stu-> System.out.println("stu="+stu));
sqlSession.close();
}
3.3 #和$的区别
3.3.1 # 占位符
语法: #{字符}
mybatis处理#{} 使用jdbc对象是 PrepareStatment对象
<select id="selectById" parameterType="integer"
resultType="com.bjpowernode.domain.Student">
select id,name,email,age from student where id=#{studentId}
</select>
mybatis出创建PrepareStatement对象,执行sql语句
String sql=" select id,name,email,age from student where id=?";
PrepareStatement pst = conn.prepareStatement(sql);
pst.setInt(1,1001); //传递参数
ResultSet rs = pst.executeQuery(); //执行sql语句
#{}特点:
1)使用的PrepareStatement对象,执行sql语句,效率高。
2)使用的PrepareStatement对象,能避免sql语句, sql语句执行更安全。
3) #{} 常常作为 列值使用的, 位于等号的右侧, #{}位置的值和数据类型有关的。
3.3.2 $ 占位符
语法 : ${字符}
mybatis执行${}占位符的sql语句
<select id="selectById" parameterType="integer"
resultType="com.bjpowernode.domain.Student">
select id,name,email,age from student where id=${studentId}
</select>
此时会报错,查询的是id列,或给传入的值加个'',如("'id'")
<!--使用name列排序-->
<select id="queryStudentOrderByColName" resultType="pdsu.edu.mybatis.domain.Student">
select *
from student
order by ${myname}
</select>
${} 表示字符串连接, 把sql语句的其他内容和 ${}内容使用 字符串(+) 连接的方式连在一起
String sql="select id,name,email,age from student where id=" + "1001";
mybatis创建Statement对象, 执行sql语句。
Statement stmt = conn.createStatement(sql);
ResultSet rs = stmt.executeQuery();
${} 的特点
1)使用Statement对象,执行sql语句,效率低
2)${}占位符的值,使用的字符串连接方式, 有sql注入的风险。 有代码安全的问题
3)${} 数据是原样使用的, 不会区分数据类型。
4)${}常用作 表名或者列名,
在能保证数据安全的情况下使用 ${}
3.4 封装MyBatis输出结果
封装输出结果: MyBatis执行sql语句,得到ResultSet, 转为java对象。
讲两个 resultType, resultMap
3.4.1 resultType
resultType属性: 在执行select时使用, 作为标签的属性出现的。
resultType:表示结果类型 , mysql执行sql语句,得到java对象的类型。 它的值有两种
1) java类型的全限定名称 。 2)使用别名
1) resultType:表示java自定义对象
Student selectById(Integer id);
<select id="selectById" parameterType="integer"
resultType="com.bjpowernode.domain.Student">
select id,name,email,age from student where id=#{studentId}
</select>
resultType:现在使用java类型的全限定名称。 表示的意思 mybatis执行sql,把ResultSet中的数据转为Student类型的对象。 mybatis会做以下操作:
1. 调用com.bjpowernode.domain.Student的无参数构造方法,创建对象。
Student student = new Student(); //使用反射创建对象
2. 同名的列赋值给同名的属性。
student.setId( rs.getInt("id"));
student.setName(rs.getString("name"));
3. 得到java对象, 如果dao接口返回值是List集合, mybatis把student对象放入到List集合。
所以执行 Student mystudent = dao.selectById(1001); 得到 数据库中 id=1001这行数据,
这行数据的列值, 付给了mystudent对象的属性。 你能得到mystudent对象。 就相当于是 id=1001这行数据。
2)resultType表示简单类型
dao方法
long countStudent();
mapper文件
<!--
执行sql语句,得到是一个值(一行一列)
-->
<select id="countStudent" resultType="java.lang.Long">
select count(*) from student
</select>
3) resultType:表示一个map结构
//查询结果返回是一个Map
Map<Object,Object> selectMap(@Param("stuid") Integer id);
<!--
执行sql得到一个Map结构数据, mybatis执行sql,把ResultSet转为map
sql执行结果, 列名做map的key , 列值作为value
sql执行得到是一行记录,转为map结构是正确的。
dao接口返回是一个map, sql语句最多能获取一行记录,多余一行是错误
-->
<select id="selectMap" resultType="java.util.HashMap">
select id,name,email from student where id = #{stuid}
</select>
练习题:
输入一个省份id ,得到 省份id ,省份name, 城市id,城市名称
例如输入 省份id=1
1 河北 1 石家庄
1 河北 2 秦皇岛
3.4.2 resultMap
resultMap: 结果映射。 自定义列名和java对象属性的对应关系。 常用在列名和属性名不同的情况。
用法:
1.先定义 resultMap标签, 指定列名和属性名称对应关系
2.在select标签使用resultMap属性,指定上面定义的resultMap的id值
<!--使用resultMap定义列和属性的关系-->
<!--定义resultMap
id:给resultMap的映射关系起个名称,唯一值
type:java类型的全限定名称
-->
<resultMap id="customMap" type="com.bjpowernode.vo.CustomObject">
<!--定义列名和属性名的对应-->
<!--主键类型使用id标签-->
<id column="id" property="cid" />
<!--非主键类型使用result标签-->
<result column="name" property="cname" />
<!--列名和属性名相同不用定义-->
<result column="email" property="email" />
<result column="age" property="age" />
</resultMap>
<!--使用resultMap属性,指定映射关系的id
resultMap和resultType 不能同时使用, 二选一。
-->
<select id="selectById2" resultMap="customMap">
select id,name,email,age from student where id=#{stuid}
</select>
3.5 列名和java对象属性名称不一样解决方式
1) 使用resultMap: 自定义列名和属性名称对应关系
2)使用resultType: 使用列别名,让别名和java对象属性名称一样
3.6 like
第一种方式: 在java程序中,把like的内容组装好。 把这个内容传入到sql语句
//like第一种方式
List<Student> selectLikeOne(@Param("name") String name);
mapper
<!--like第一种方式-->
<select id="selectLikeOne" resultType="com.bjpowernode.domain.Student">
select * from student where name like #{name}
</select>
执行like
@Test
public void testLikeOne(){
SqlSession sqlSession = MyBatisUtil.getSqlSession();
StudentDao dao = sqlSession.getMapper(StudentDao.class);
String name="%李%";
List<Student> students = dao.selectLikeOne(name);
sqlSession.close();
students.forEach( stu-> System.out.println(stu));
}
第二种方式: 在sql语句,组织like的内容。
sql语句like的格式: where name like “%“空格#{name}空格”%”
//like第二种方式
List<Student> selectLikeTwo(@Param("name") String name);
<!--like第二种方式-->
<select id="selectLikeTwo" resultType="com.bjpowernode.domain.Student">
select * from student where name like "%" #{name} "%"
</select>
@Test
public void testLikeTwo(){
SqlSession sqlSession = MyBatisUtil.getSqlSession();
StudentDao dao = sqlSession.getMapper(StudentDao.class);
String name="李";
List<Student> students = dao.selectLikeTwo(name);
sqlSession.close();
students.forEach( stu-> System.out.println(stu));
}
第四章 动态sql
什么是动态sql: 同一个dao的方法, 根据不同的条件可以表示不同的sql语句, 主要是where部分有变化
使用mybatis提供的标签,实现动态sql的能力, 主要讲 if ,where ,foreach, sql。
使用动态sql的时候, dao方法的形参使用java对象。
什么时候使用动态sql:
在 mapper 的动态 SQL 中若出现大于号(>)、小于号(<)、大于等于
号(>=),小于等于号(<=)等符号,最好将其转换为实体符号。否则,
XML 可能会出现解析出错问题。
特别是对于小于号(<),在 XML 中是绝不能出现的。否则解析 mapper
文件会出错。
实体符号表:
4.1 if 标签
语法:
<if test="boolean判断结果">
sql 代码
</if>
在mapper文件中
<select id="selectStudent" resultType="com.bjpwoernode.domain.Student">
select *from student
<if test="条件">
sql语句
</if>
<if test="条件">
sql语句
</if>
</select>
例子:
List<Student> selectIf(Student student);
<!--if
test: 使用对象的属性值作为条件
-->
<select id="selectIf" resultType="com.bjpowernode.domain.Student">
select * from student
where id=-1
<if test="name !=null and name!=''">
or name = #{name}
</if>
<if test="age >0">
or age < #{age}
</if>
</select>
4.2 where 标签
使用if标签时,容易引起sql语句语法错误。 使用where标签解决if产生的语法问题。
使用时 where ,里面是一个或多个if 标签, 当有一个if标签 判断条件为true, where标签会转为 WHERE 关键字附加到sql语句的后面。 如果 if 没有一个条件为true , 忽略where和里面的if。
需要注意的是,第一个< if/>标签中的SQL 片断,可以不包含 and。不过,写上 and 也不错,系统会将多出的 and去掉。但其它< if />中 SQL 片断的 and,必须要求写上。否则 SQL 语句将拼接出错.
where标签删除 和他最近的一个or 或者一个 and。
语法:
<where>
<if test="条件1">sql语句1</if>
<if test="条件2">sql语句2</if>
</where>
例子:
//where
List<Student> selectWhere(Student student);
<!--where-->
<select id="selectWhere" resultType="com.bjpowernode.domain.Student">
select * from student
<where>
<if test="name !=null and name!=''">
or name = #{name}
</if>
<if test="age >0">
or age < #{age}
</if>
</where>
</select>
4.3 foreach 循环
使用foreach可以循环数组,list集合, 一般使用在in语句中。
语法:
< foreach collection="集合类型" open="开始的字符" close="结束的字符"
item="集合中的成员" separator="集合成员之间的分隔符">
#{item 的值}
</ foreach>
标签属性:
collection: 表示,循环的对象是 数组, 还是list集合。 如果dao接口方法的形参是 数组,
collection="array" ,如果dao接口形参是List, collection="list"
open:循环开始时的字符。 sql.append("(");
close:循环结束时字符。 sql.append(")");
item:集合成员, 自定义的变量。 Integer item = idlist.get(i);// item是集合成员
separator:集合成员之间的分隔符。 sql.append(","); //集合成员之间的分隔符
#{item 的值}:获取集合成员的值。
第一种方式:遍历 List<简单类型>
//foreach-1
List<Student> selectForeachOne(List<Integer> idlist);
<!--foreach第一种方式, 循环简单类型的List-->
<select id="selectForeachOne" resultType="com.bjpowernode.domain.Student">
select * from student
<if test="list !=null and list.size>0">
where id in
<foreach collection="list" open="(" close=")" separator="," item="myid">
#{myid}
</foreach>
</if>
</select>
@Test
public void testSelectForeachOne(){
//1.获取SqlSession
SqlSession session = MyBatisUtil.getSqlSession();
//2.获取dao的代理
StudentDao dao = session.getMapper(StudentDao.class);
List<Integer> idlist = new ArrayList<>();
idlist.add(1001);
idlist.add(1002);
idlist.add(1003);
List<Student> students = dao.selectForeachOne(idlist);
students.forEach( stu-> System.out.println("stu=="+stu));
//3.关闭SqlSession对象
session.close();
}
第二种方式:遍历 List<对象类型>
//foreach-2
List<Student> selectForeachTwo(List<Student> studentList);
<!--foreach第二种方式, 循环的List<Student>-->
<select id="selectForeachTwo" resultType="com.bjpowernode.domain.Student">
select * from student
<if test="list != null and list.size>0">
where id in
<foreach collection="list" open="(" close=")" separator="," item="stu">
#{stu.id}
</foreach>
</if>
</select>
@Test
public void testSelectForeachTwo(){
//1.获取SqlSession
SqlSession session = MyBatisUtil.getSqlSession();
//2.获取dao的代理
StudentDao dao = session.getMapper(StudentDao.class);
List<Student> list = new ArrayList<>();
Student s1 = new Student();
s1.setId(1001);
Student s2 = new Student();
s2.setId(1002);
list.add(s1);
list.add(s2);
List<Student> students = dao.selectForeachTwo(list);
students.forEach( stu-> System.out.println("stu=="+stu));
//3.关闭SqlSession对象
session.close();
}
4.4 sql标签
sql标签标示 一段sql代码, 可以是表名,几个字段, where条件都可以, 可以在其他地方复用sql标签的内容。
使用方式:
1) 在mapper文件中定义 sql代码片段 <sql id="唯一字符串"> 部分sql语句 </sql>
2)在其他的位置,使用include标签引用某个代码片段
例如:
<!--定义代码片段-->
<sql id="selectStudent">
select * from student
</sql>
<sql id="studentFieldList">
id,name,email
</sql>
<select id="selectIf" resultType="com.bjpowernode.domain.Student">
<include refid="selectStudent" />
where id=-1
<if test="name !=null and name!=''">
or name = #{name}
</if>
<if test="age >0">
or age < #{age}
</if>
</select>
<!--where-->
<select id="selectWhere" resultType="com.bjpowernode.domain.Student">
select <include refid="studentFieldList"/> from student
<where>
<if test="name !=null and name!=''">
or name = #{name}
</if>
<if test="age >0">
or age < #{age}
</if>
</where>
</select>
第五章 MyBatis配置文件
mybatis配置文件两大类: 1 mybatis主配置文件; 2 mybatis的mapper文件
- mybatis主配置文件,提供mybatis全局设置的。包含的内容 日志, 数据源,mapper文件位置
- mapper文件: 写sql语句的。 一个表一个mapper文件
5.1 settings部分
settings是mybatis的全局设置,影响整个mybatis的运行。 这个设置一般使用默认值就可以了。
<settings>
<setting name="cacheEnabled" value="true"/>
<setting name="lazyLoadingEnabled" value="true"/>
<setting name="multipleResultSetsEnabled" value="true"/>
<setting name="useColumnLabel" value="true"/>
<setting name="useGeneratedKeys" value="false"/>
<setting name="autoMappingBehavior" value="PARTIAL"/>
<setting name="autoMappingUnknownColumnBehavior" value="WARNING"/>
<setting name="defaultExecutorType" value="SIMPLE"/>
<setting name="defaultStatementTimeout" value="25"/>
<setting name="defaultFetchSize" value="100"/>
<setting name="safeRowBoundsEnabled" value="false"/>
<setting name="mapUnderscoreToCamelCase" value="false"/>
<setting name="localCacheScope" value="SESSION"/>
<setting name="jdbcTypeForNull" value="OTHER"/>
<setting name="lazyLoadTriggerMethods" value="equals,clone,hashCode,toString"/>
</settings>
5.2 typeAliase别名(建议使用全限定类名)
mybatis提供的对java类型定义简短,好记名称。
自定义别名的步骤:
1)在mybatis主配置文件,使用 typeAliases标签声明别名
2)在mapper文件中, resultType=“别名”
声明别名(mybatis主配置文件)
在日志的下面写
<typeAliases>
<!--第一种语法格式
type:java类型的全限定名称(自定义类型)
alias:自定义别名
-->
<typeAlias type="com.bjpowernode.domain.Student" alias="stu" />
<!--
第二种方式:
name:包名 mybatis会把这个包中所有类名作为别名(不区分大小写)
优点:使用方便,一次给多个类定义别名
缺点:别名不可以自定义,必须是类名
-->
<package name="pdsu.edu.mybatis.domain"/>
</typeAliases>
mapper文件中使用
resultType="别名"
第一种方式:
<select id="selectById" parameterType="integer" resultType="stu">
select id,name,email,age from student where id=#{studentId}
</select>
第二种方式:
<select id="selectById" parameterType="integer" resultType="student">
select id,name,email,age from student where id=#{studentId}
</select>
5.3 配置环境
environments: 环境标签, 在他里面可以配置多个environment
属性: default ,必须是某个environment的id属性值。 表示mybatis默认连接的数据库
environment: 表示一个数据库的连接信息。
属性: id 自定义的环境的标识。 唯一值。
transactionManager:事务管理器
属性: type 表示事务管理器的类型。
属性值:1)JDBC: 使用Connection对象, 由mybatis自己完成事务的处理。
2) MANAGED: 管理,表示把事务的处理交给容器实现(由其他软件完成事务的提交,回滚)
dataSource: 数据源,创建的Connection对象,连接数据库。
属性: type 数据源的类型
属性值:1) POOLED, mybatis会在内存中创建PooledDataSource类,管理多个Connection连接对象,使 用的连接池
2) UNPOOLED ,不使用连接池, mybatis创建一个UnPooledDataSource这个类, 每次执行sql 语句先创建Connection对象,再执行sql语句,最后关闭Connection
3) JNDI : java的命名和目录服务。
<environments default="online">
<environment id="development">
<transactionManager type="JDBC"/>
<!--配置数据源: 创建Connection对象。-->
<dataSource type="POOLED">
<!--driver:驱动的内容-->
<property name="driver" value="com.mysql.jdbc.Driver"/>
<!--连接数据库的url-->
<property name="url"
value="jdbc:mysql://localhost:3306/springdb"/>
<!--用户名-->
<property name="username" value="root"/>
<!--密码-->
<property name="password" value="123"/>
</dataSource>
</environment>
<!-- 项目上线后使用的数据库 -->
<environment id="online">
<transactionManager type="JDBC"/>
<!--配置数据源: 创建Connection对象。-->
<dataSource type="POOLED">
<!--driver:驱动的内容-->
<property name="driver" value="com.mysql.jdbc.Driver"/>
<!--连接数据库的url-->
<property name="url"
value="jdbc:mysql://localhost:3306/springdb"/>
<!--用户名-->
<property name="username" value="admin"/>
<!--密码-->
<property name="password" value="123456"/>
</dataSource>
</environment>
</environments>
5.4 使用数据库属性配置文件(*)
需要把数据库的配置信息放到一个单独文件中, 独立管理。 这个文件扩展名是 properties. 在这个文件中,使用自定义的key=value的格式表示数据
使用步骤:
1.在resources目录中,创建xxxx.properties
2.在文件中,使用 key=value的格式定义数据。
例如 jdbc.url=jdbc:mysq://localhost:3306/springdb
3.在mybatis主配置文件, 使用properties标签引用外部的属性配置文件
4.在使用值的位置, 使用${key}获取key对应的value(等号右侧的值)
例子:
jdbc.properties
jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/springdb?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone = GMT
jdbc.username=root
jdbc.password=root
mybatis主配置文件
<!--使用外部属性配置文件
resource:指定类路径下的某个属性配置文件
-->
<properties resource="jdbc.properties" />
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<!--配置数据源: 创建Connection对象。-->
<dataSource type="POOLED">
<!--driver:驱动的内容-->
<property name="driver" value="${jdbc.driver}"/>
<!--连接数据库的url-->
<property name="url" value="${jdbc.url}"/>
<!--用户名-->
<property name="username" value="${jdbc.username}"/>
<!--密码-->
<property name="password" value="${jdbc.password}"/>
</dataSource>
</environment>
</environments>
5.5 mapper 标签(*)
使用mapper指定其他mapper文件的位置,
mapper标签使用的格式有两个常用的方式:
<mappers>
<!--第一种方式, resources="mapper文件的路径"
优点:文件清晰。 加载的文件是明确的。
文件的位置比较灵活。
缺点:文件比较多, 代码量会比较大, 管理难度大
-->
<mapper resource="com/bjpowernode/dao/StudentDao.xml"/>
<mapper resource="com/bjpowernode/dao/OrderDao.xml"/>
<!--
第二种方式,使用<package>
name:包名, mapper文件所在的包名。
特点: 把这个包中的所有mapper文件,一次加载。
使用要求:
1. mapper文件和dao接口在同一目录
2. mapper文件和dao接口名称完全一样。
-->
<package name="com.bjpowernode.dao" />
<package name="com.bjpowernode.dao1" />
</mappers>
第六章 PageHelper数据分页
PageHelper做数据分页。 在你的select语句后面加入 分页的 sql 内容, 如果你使用的mysql数据库, 它就是在select * from student 后面加入 limit 语句。
使用步骤:
1.加入依赖pagehelper依赖
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>5.1.10</version>
</dependency>
2.在mybatis主配置文件, 加入plugin声明
在<environments> 之前加入
<plugins>
<plugin interceptor ="com.github.pagehelper.PageInterceptor" />
</plugins>
sql语句:
select * from 表名 limit 起始记录数=((当前页-1)*每页的条数),每页取几条
PageInfo< ProductInfo > pageInfo = new PageInfo<>(list);
4.pageInfo参数详解
//当前页
private int pageNum;
//每页的数量
private int pageSize;
//当前页的数量
private int size;
//由于startRow和endRow不常用,这里说个具体的用法
//可以在页面中"显示startRow到endRow 共size条数据"
//当前页面第一个元素在数据库中的行号
private int startRow;
//当前页面最后一个元素在数据库中的行号
private int endRow;
//总记录数
private long total;
//总页数
private int pages;
//本页的结果集
private List<T> list;
//前一页
private int prePage;
//下一页
private int nextPage;
//是否为第一页
private boolean isFirstPage = false;
//是否为最后一页
private boolean isLastPage = false;
//是否有前一页
private boolean hasPreviousPage = false;
//是否有下一页
private boolean hasNextPage = false;
//导航页码数
private int navigatePages;
//所有导航页号
private int[] navigatepageNums;
//导航条上的第一页
private int navigateFirstPage;
//导航条上的最后一页
private int navigateLastPage;
例:
3.在select语句之前,调用PageHelper.startPage(页码, 每页大小)
List<Student> selectAll();
<select id="selectAll" resultType="pdsu.edu.mybatis.domain.Student">
select *
from student order by id
</select>
@Test
public void testPageHelper() {
//1.获取SqlSession对象
SqlSession session = MybatisUtil.getSqlSession();
//2.获取dao代理
StudentDao dao = session.getMapper(StudentDao.class);
//调用pageHelper对象
PageHelper.startPage(2,3);
List<Student> students = dao.selectAll();
students.forEach(stu-> System.out.println(stu));
//3.关闭SqlSession对象
session.close();
}
对比:
没有使用PageHelper
select * from student order by id
使用PageHelper
SELECT count(0) FROM student
select * from student order by id LIMIT ?