1.框架概述
???????MyBatis是一个优秀的基于java的持久层框架,内部封装了jdbc,开发者只需要关注sql语句本身,而不需要处理加载驱动、创建连接、创建statement、关闭连接、资源等繁杂的过程。 ???????MyBatis通过 xml或注解两种方式将要执行的各种 sql语句配置起来,并通过 java对象和 sql 的动态参数进行映射生成最终执行的 sql 语句,最后由 myBatis 框架执行 sql 并将结果映射为 java 对象并返回。
2.Mybatis快速入门
2.1. 搭建MyBatis开发环境
2.1.1.MyBatis下载
下载Mybatis,github下载地址:https://github.com/mybatis/mybatis-3
2.1.2.创建mysql数据库和表
创建mysql数据库和表 数据库名称:mybatis 表名:student CREATE TABLE student ( id int(11) NOT NULL, name varchar(255) DEFAULT NULL, email varchar(255) DEFAULT NULL, age int(11) DEFAULT NULL, PRIMARY KEY (id ) ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
2.1.3.创建maven工程(IDEA版本:2012.1,JDK版本:12)
2.1.4.在pom.xml中加入maven依赖和资源扫描插件
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.1</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.9</version>
</dependency>
</dependencies>
<build>
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>false</filtering>
</resource>
</resources>
</build>
2.1.5.创建Student实体类
package com.gxust.bean;
public class Student {
private Integer id;
private String name;
private String email;
private Integer age;
}
2.1.6.编写Dao接口StudentDao
package com.gxust.dao;
import com.gxust.bean.Student;
import java.util.List;
public interface StudentDao {
List<Student> selectStudents();
}
2.1.7.编写Dao接口Mapper映射文件StudentDao.xml
映射文件的要求要求: 1、在 dao包中创建文件 StudentDao.xml 2、要 StudentDao.xml的文件名称和接口 StudentDao一样 ,区分大小写的一样。
<?xml version="1.0" encoding="UTF-8" ?>
<!--
mapper 是当前文件的根标签,必须的。
sql映射文件(sql mapper): 写sql语句的, mybatis会执行这些sql
1.指定约束文件
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
mybatis-3-mapper.dtd是约束文件的名称, 扩展名是dtd的。
2.约束文件作用: 限制和检查在当前文件中出现的标签,属性必须符合mybatis的要求。
-->
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--namespace:叫做命名空间,唯一值的, 可以是自定义的字符串。要求你使用dao接口的全限定名称。-->
<mapper namespace="com.gxust.dao.StudentDao">
<!--
select:表示查询操作。
id: 你要执行的sql语法的唯一标识, mybatis会使用这个id的值来找到要执行的sql语句
可以自定义,但是要求你使用接口中的方法名称。
resultType:表示结果类型的, 是sql语句执行后得到ResultSet,遍历这个ResultSet得到java对象的类型。
值写的类型的全限定名称
-->
<select id="selectStudents" resultType="com.gxust.bean.Student" >
select id,name,email,age from student order by id
</select>
<!--
在当前文件中,可以使用特定的标签,表示数据库的特定操作。
<select>:表示执行查询,select语句
<update>:表示更新数据库的操作, 就是在<update>标签中 写的是update sql语句
<insert>:表示插入, 放的是insert语句
<delete>:表示删除, 执行的delete语句
-->
</mapper>
2.1.8.创建Mybatis主配置文件:
??1.项目src/main 下创建 resources目录,设置 resources目录为 resources root ??2.创建主配置文件:名称为mybatis.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>
<!--settings:控制mybatis全局行为-->
<settings>
<!--设置mybatis输出日志-->
<setting name="logImpl" value="STDOUT_LOGGING" />
</settings>
<!--环境配置: 数据库的连接信息
default:必须和某个environment的id值一样。
告诉mybatis使用哪个数据库的连接信息。也就是访问哪个数据库
-->
<environments default="mysql">
<!-- environment : 一个数据库信息的配置, 环境
id:一个唯一值,自定义,表示环境的名称。
-->
<environment id="mysql">
<!--
transactionManager :mybatis的事务类型
type: JDBC(表示使用jdbc中的Connection对象的commit,rollback做事务处理)
-->
<transactionManager type="JDBC"/>
<!--
dataSource:表示数据源,连接数据库的
type:表示数据源的类型, POOLED表示使用连接池
-->
<dataSource type="POOLED">
<!--
name的值:driver, user, username, password 是固定的,不能自定义。
value的值:输入你自己数据库的连接信息
-->
<!--数据库的驱动类名-->
<property name="driver" value="com.mysql.jdbc.Driver"/>
<!--连接数据库的url字符串-->
<property name="url" value="jdbc:mysql://localhost:3306/mybatis?useUnicode=true&characterEncoding=utf-8"/>
<!--访问数据库的用户名-->
<property name="username" value="root"/>
<!--密码-->
<property name="password" value="zz567891"/>
</dataSource>
</environment>
</environments>
<!-- sql mapper(sql映射文件)的位置-->
<mappers>
<!--一个mapper标签指定一个文件的位置。
从类路径开始的路径信息。 target/clasess(类路径)
-->
<mapper resource="com/gxust/dao/StudentDao.xml"/>
</mappers>
</configuration>
<!--
mybatis的主配置文件: 主要定义了数据库的配置信息, sql映射文件的位置
1. 约束文件
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
mybatis-3-config.dtd:约束文件的名称
2. configuration 根标签。
-->
2.1.9.编写测试类测试环境配置是否成功
在src/test/java/com/gxust/下创建MybatisTest.java文件
public class MybatisTest {
@Test
public void testOne() throws IOException {
String config = "mybatis.xml";
InputStream in = Resources.getResourceAsStream(config);
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
SqlSession session = factory.openSession();
List<Student> students = session.selectList("com.gxust.dao.StudentDao.selectStudents");
for (Student student : students){
System.out.println(student);
}
session.close();
}
}
最终我们得到输出结果:
==> Preparing: select id,name,email,age from student order by id
==> Parameters:
<== Columns: id, name, email, age
<== Row: 1, 唐三, tangsan@qq.com, 20
<== Row: 2, 小舞, xiaowu@qq.com, 19
<== Row: 3, 宁荣荣, ningrongrong@163.com, 18
<== Total: 3
Student{id=1, name='唐三', email='tangsan@qq.com', age=20}
Student{id=2, name='小舞', email='xiaowu@qq.com', age=19}
Student{id=3, name='宁荣荣', email='ningrongrong@163.com', age=18}
到此,证明我们的基本环境配置已经成功。接下来,我们可以在配置文件中进行增添和修改内容来满足我们更多的要求。
2.1.10.mybatis.xml文件加入日志配置
加入日志配置语句后可以在控制台输出执行的sql语句和参数信息
<settings> <setting name="logImpl" value="STDOUT_LOGGING" /> </settings>
2.2.使用Mybatis进行基本的CURD操作
CURD是一个数据库技术中的缩写词,一般的项目开发的各种参数的基本功能都是CURD。作用是用于处理数据的基本原子操作。它代表创建(Create)、更新(Update)、读取(Retrieve)和删除(Delete)操作。
2.2.1.insert
1)StudentDao接口中增加方法
int insertStudent(Student student);
2)StudentDao.xml中加入sql语句
<insert id="insertStudent">
insert into student(id,name,email,age) values(#{id},#{name},#{email},#{age})
</insert>
对于代码中参数的传递问题,将在3.2节深入理解参数中进行讲解。
3)增加测试方法
@Test
public void testTwo() throws IOException{
String config = "mybatis.xml";
InputStream in = Resources.getResourceAsStream(config);
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
SqlSession session = factory.openSession();
Student student = new Student();
student.setId(4);
student.setName("比比东");
student.setAge(24);
student.setEmail("bibidong@163.com");
int rows = session.insert("com.gxust.dao.StudentDao.insertStudent", student);
session.commit();
System.out.println("增加的记录数为:"+rows);
session.close();
}
2.2.2.update
1)StudentDao接口中增加方法
int updateStudent(Student student);
2)StudentDao.xml中加入sql语句
<update id="updateStudent">
update student set age = #{age} where name=#{name}
</update>
3)增加测试方法
@Test
public void testThree() throws IOException {
String config = "mybatis.xml";
InputStream in = Resources.getResourceAsStream(config);
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
SqlSession session = factory.openSession();
Student student = new Student();
student.setName("宁荣荣");
student.setAge(19);
session.update("com.gxust.dao.StudentDao.updateStudent",student);
session.commit();
session.close();
}
2.2.3.delete
1)StudentDao接口中增加方法
int deleteStudent(Student student);
2)StudentDao.xml中加入sql语句
<delete id="deleteStudent">
delete from student where name = #{name}
</delete>
3)增加测试方法
@Test
public void testFour() throws IOException {
String config = "mybatis.xml";
InputStream in = Resources.getResourceAsStream(config);
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
SqlSession session = factory.openSession();
Student student = new Student();
student.setName("比比东");
session.delete("com.gxust.dao.StudentDao.deleteStudent",student);
session.commit();
session.close();
}
2.3.Mybatis对象分析
2.3.1对象使用
1)Resources类
Resources类,顾名思义就是资源,用于读取资源文件。其有很多方法通过加载并解析资源文件,返回不同类型的 IO流对象。
2)SqlSessionFactoryBuilder类
SqlSessionFactory的创建,需要使用 SqlSessionFactoryBuilder对象的 build()方法。由于SqlSessionFactoryBuilder对象在创建完工厂对象后,就完成了其历史使命,即可被销毁。所以,一般会将该 SqlSessionFactoryBuilder对象创建为一个方法内的局部对象,方法结束,对象销毁。
3)SqlSessionFactory接口
SqlSessionFactory接口对象是 一个重量级对象(系统开销大的对象),它是线程安全的, 所以一个应用只需要一个该对象即可。 创建 SqlSession需要使用 SqlSessionFactory接口的的 openSession()方法。 ? openSession(true):创建一个有自动提交功能的 SqlSession ? openSession(false):创建一个非自动提交功能的 SqlSession,需手动提交 ? openSession():同 openSession(false)
4)SqlSession接口
SqlSession接口对象用于执行持久化操作。一个SqlSession对应着一次数据库会话,一次会话以SqlSession对象的创建开始,以 SqlSession对象的关闭结束。 SqlSession接口对象是线程不安全的,所以每次数据库会话结束前 ,需要马上调用其 close()方法,将 其关闭。再次需要会话,再次创建。 建议SqlSession在方法内部创建,使用完毕后关闭。
2.3.2.创建工具类
1)创建MyBatisUtil工具类
我们知道,SqlSessionFactory接口是一个重量级对象,所以我们应该控制一个应用只创建就足够了。解决方案是,将该接口的创建放入工具类的静态代码块中,让其在类加载时就创建,且只创建一次。
public class MyBatisUtil {
private static SqlSessionFactory factory = null;
static {
try {
String config = "mybatis.xml";
InputStream in = Resources.getResourceAsStream(config);
factory = new SqlSessionFactoryBuilder().build(in);
} catch (IOException e) {
factory = null;
e.printStackTrace();
}
}
public static SqlSession getSqlSession(){
SqlSession session = null;
if (factory != null){
session = factory.openSession();
}
return session;
}
}
2)使用MyBatisUtil工具类
在测试类中使用工具类进行查询操作
@Test
public void testFive(){
SqlSession session = MyBatisUtil.getSqlSession();
List<Student> students = session.selectList("com.gxust.dao.StudentDao.selectStudents");
for (Student student : students){
System.out.println(student);
}
}
2.4.MyBatis使用传统Dao开发方式
2.4.1.Dao开发
1)创建Dao接口实现类
public class StudentDaoImpl implements StudentDao
2)实现接口中select方法
@Override
public List<Student> selectStudents() {
SqlSession sqlSession = MyBatisUtil.getSqlSession();
List<Student> students = sqlSession.selectList("com.gxust.dao.StudentDao.selectStudents");
sqlSession.close();
return students;
}
测试实现的查询操作:
@Test
public void testSix(){
StudentDao dao = new StudentDaoImpl();
List<Student> students = dao.selectStudents();
for (Student student:students){
System.out.println(student);
}
}
3)实现接口中insert方法
@Override
public int insertStudent(Student student) {
int rows;
SqlSession sqlSession = MyBatisUtil.getSqlSession();
rows = sqlSession.insert("com.gxust.dao.StudentDao.insertStudent",student);
sqlSession.commit();
sqlSession.close();
return rows;
}
测试实现的查询操作:
@Test
public void testSeven(){
Student student = new Student();
student.setId(4);
student.setName("比比东");
student.setAge(19);
student.setEmail("bibidong@163.com");
StudentDao dao = new StudentDaoImpl();
int rows = dao.insertStudent(student);
System.out.println("插入了:"+rows+"条记录");
}
4)实现接口中update方法
@Override
public int updateStudent(Student student) {
int rows;
SqlSession sqlSession = MyBatisUtil.getSqlSession();
rows = sqlSession.update("com.gxust.dao.StudentDao.updateStudent",student);
sqlSession.commit();
sqlSession.close();
return rows;
}
测试实现的查询操作:
@Test
public void testEight(){
Student student = new Student();
student.setName("比比东");
student.setAge(28);
StudentDao dao = new StudentDaoImpl();
int rows = dao.updateStudent(student);
System.out.println("修改了:"+rows+"条记录");
}
5)实现接口中delete方法
@Override
public int deleteStudent(Student student) {
int rows;
SqlSession sqlSession = MyBatisUtil.getSqlSession();
rows = sqlSession.delete("com.gxust.dao.StudentDao.deleteStudent",student);
sqlSession.commit();
sqlSession.close();
return rows;
}
测试实现的查询操作:
@Test
public void testNignt(){
Student student = new Student();
student.setName("比比东");
StudentDao dao = new StudentDaoImpl();
int rows = dao.deleteStudent(student);
System.out.println("删除了:"+rows+"条记录");
}
2.4.2.对传统Dao开发方式的分析
从前面自定义的Dao接口实现类中,我们能够看出一个问题:Dao的实现类其实并没有干什么实质性的工 作,它仅仅就是通过SqlSession的相关API定位到映射文件mapper中相应id的SQL语句,真正对DB进 行操作的工作其实是由框架通过mapper中的SQL语句完成的。
3.Mybatis框架Dao代理
3.1.Dao代理实现CURD
3.1.1实现步骤
1)删除Dao接口实现类
2)使用getMapper()方法获取代理对象
我们只需要调用SqlSession的getMapper()方法,就能够获取到指定接口的实现类对象。该方法的参数为指定Dao接口类的class值。
SqlSession session = factory.openSession();
StudentDao dao = session.getMapper(Student.class);
StudentDao studentDao = MyBatisUtil.getSqlSession().getMapper(StudentDao.class);
3)使用Dao代理对象方法执行sql语句
执行insert方法:
@Test
public void testDao(){
StudentDao dao = MyBatisUtil.getSqlSession().getMapper(StudentDao.class);
List<Student> students = dao.selectStudents();
for (Student student : students){
System.out.println(student);
}
}
执行insert方法: 由于通过Dao代理获取Dao接口的实现类中并没有为我们自动提交事务,这时我们需要到工具类中修改获取SqlSession对象的方法,使其具备自动提交功能。
session = factory.openSession(true);
@Test
public void testInsert(){
Student student = new Student();
student.setId(4);
student.setName("比比东");
student.setAge(22);
student.setEmail("bibidong@163.com");
StudentDao dao = MyBatisUtil.getSqlSession().getMapper(StudentDao.class);
dao.insertStudent(student);
}
执行update方法:
@Test
public void testUpdate(){
Student student = new Student();
student.setName("比比东");
student.setAge(28);
StudentDao dao = MyBatisUtil.getSqlSession().getMapper(StudentDao.class);
dao.updateStudent(student);
}
执行delete方法:
@Test
public void testDelete(){
Student student = new Student();
student.setName("比比东");
StudentDao dao = MyBatisUtil.getSqlSession().getMapper(StudentDao.class);
dao.deleteStudent(student);
}
3.1.2.原理
执行测试代码:
@Test
public void testProxy(){
StudentDao dao = MyBatisUtil.getSqlSession().getMapper(StudentDao.class);
System.out.println("StudentDao="+dao);
}
得到输出结果:
StudentDao=org.apache.ibatis.binding.MapperProxy@1460a8c0
MapperProxy类定义:
invoke()方法 重点方法:
3.2.深入理解参数
3.2.1.parameterType
< select > , < insert > , < update > , < delete >都可以使用parameterType指定传入的参数类型,例如:
<delete id="deleteStudent" parameterType="int">
delete from student where id=#{studentId}
</delete>
等同于:
<delete id="deleteStudent" parameterType="java.lang.Integer">
delete from student where id=#{studentId}
</delete>
parameterType:接口中方法参数的类型,类型的完全限定名或别名。这个属性是可选的,因为MyBatis可以推断出具体传入语句的参数,默认值为未设置(unset)。接口中方法的参数从java代码传入到mapper文件的sql语句。 我们能够从mybatis官方文档的15页得到mybatis默认支持的别名: There are many built-in type aliases for common Java types. They are all case insensitive, note the special handling of primitives due to the overloaded names.
Alias | MappedType |
---|
_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.MyBatis传递参数
从java代码中把参数传递到mapper.xml文件中。
3.2.3.一个简单参数
当Dao接口中方法的参数只有一个简单类型(java基本类型和String),我们使用占位符 #{ 任意字符 },它和方法的参数名无关。 接口方法:
Student selectStudentById(int id);
Mapper文件:
<select id="selectStudentById" resultType="com.gxust.bean.Student">
select id,name,email,age from student where id=#{studentId}
</select>
测试方法:
@Test
public void testParamter(){
SqlSession session = jdbcUtil.getSqlSession();
StudentDao mapper = session.getMapper(StudentDao.class);
Student student = mapper.selectStudentById(1);
System.out.println(student);
session.close();
}
3.2.4.多个参数,使用@Param
当 Dao 接口的方法包含多个参数时,我们需要通过名称使用参数。用法:在方法形参前面加入 @Param(“自定义参数名”),同时Mapper文件使用 #{自定义参数名},这两个参数名必须相同。 示例: 接口方法:
List<Student> selectParameters(@Param("stuName") String name,@Param("stuAge") Integer age);
Mapper文件:
<select id="selectParameters" resultType="com.gxust.bean.Student">
select id,name,age,email from student where name=#{stuName} or age=#{stuAge}
</select>
测试方法:
@Test
public void testParameters(){
SqlSession session = jdbcUtil.getSqlSession();
StudentDao dao = session.getMapper(StudentDao.class);
List<Student> students = dao.selectParameters("唐三", 18);
session.close();
}
3.2.5.多个参数,使用对象
使用 java 对象传递参数,java 的属性值就是sql语句需要的参数值。 每一个属性就是一个参数。 语法格式:
#{property,javaType=java中数据类型名 ,jdbcType=数据类型名称}
示例: 使用Student对象作为参数,接口方法:
List<Student> selectMultiObject(Student student);
Mapper文件:
<select id="selectMultiObject" resultType="com.gxust.bean.Student">
select id,name,age,email from student where name=#{name} or age=#{age}
</select>
或者:
<select id="selectMultiObject" resultType="com.gxust.bean.Student">
select id,name,age,email from student
where name=#{name,javaType=String,jdbcType=VARCHAR}
or age=#{age,javaType=int,jdbcType=INTEGER}
</select>
测试方法:
@Test
public void testObject(){
SqlSession session = jdbcUtil.getSqlSession();
StudentDao dao = session.getMapper(StudentDao.class);
Student student = new Student();
student.setName("宁荣荣");
student.setAge(20);
List<Student> students = dao.selectMultiObject(student);
for (Student student1 : students){
System.out.println(student1);
}
session.close();
}
3.2.6.多个参数,按位置
参数位置从0开始,引用参数语法 #{ arg位置 } 第一个参数是 #{arg0}, 第二个是 #{arg1} 注意: mybatis 3.3版本和之前的版本使用 #{0},#{1}方式, 从 mybatis3.4开始使用 #{arg0}方式。 示例: 接口方法:
List<Student> selectStudentsByLocation(String name,int age);
Mapper文件:
<select id="selectStudentsByLocation" resultType="com.gxust.bean.Student">
select id,name,age,email from student where name=#{arg0} or age=#{arg1}
</select>
测试代码:
@Test
public void testBylocation(){
SqlSession session = jdbcUtil.getSqlSession();
StudentDao dao = session.getMapper(StudentDao.class);
List<Student> students = dao.selectStudentsByLocation("宁荣荣", 20);
for (Student student : students){
System.out.println(student);
}
session.close();
}
3.2.7.多个参数,使用Map
Map集合可以存储多个值, 使用 Map 向 mapper 文件一次传入多个参数。Map 集合使用 String 的 key, Object类型的值存储参数。 mapper文件使用 #{ key } 引用参数值。 示例: 接口方法:
List<Student> selectStudentsByMap(Map<String,Object> map);
Mapper文件:
<select id="selectStudentsByMap" resultType="com.gxust.bean.Student">
select id,name,age,email from student where name=#{myname} or age=#{myage}
</select>
测试代码:
@Test
public void testByMap(){
SqlSession session = jdbcUtil.getSqlSession();
StudentDao dao = session.getMapper(StudentDao.class);
Map<String,Object> map = new HashMap<>();
map.put("myname","唐三");
map.put("myage",20);
List<Student> students = dao.selectStudentsByMap(map);
for (Student student:students){
System.out.println(student);
}
session.close();
}
3.2.8. #和$
#:占位符 ,告诉 mybatis使用实际的参数值代替。并使用 PrepareStatement 对象执行sql语句 , #{…}代替sql语句的 “?”。 这样做更安全,更迅速,通常也是首选做法。 Mapper文件:
mapper文件
<select id="selectStudentById" resultType="com.gxust.bean.Student">
select id,name,email,age from student where id=#{id}
</select>
转为MyBatis的执行语句是: String sql=“select id,name,email,age form student where id = ?”; PreparedStatement prea = conn.prepareStatement(sql); prea.setInt(1,1); 分析: where id = ?,就相当于where id = #{id} prea.setInt(1,1),其中,1会替换掉 #{studentId}
$ :字符串替换 它的作用是告诉 mybatis 使用 $ 包含的“字符串”替换所在位置。使用 Statement把 sql 语句和 ${}的内容连接起来。主要用在替换表名,列名 ,不同列排序等操作。 示例:使用不同的列名作为查询条件 接口方法:
Student selectStudentBy$(@Param("col") String columnName,@Param("value") Object value);
Mapper文件:
<select id="selectStudentBy$" resultType="com.gxust.bean.Student">
select id,name,age,email from student where ${col} = #{value}
</select>
测试方法:
@Test
public void test$(){
SqlSession session = jdbcUtil.getSqlSession();
StudentDao dao = session.getMapper(StudentDao.class);
dao.selectStudentBy$("id",1);
dao.selectStudentBy$("name","宁荣荣");
dao.selectStudentBy$("age",20);
dao.selectStudentBy$("email","ningrongrong@163.com");
session.close();
}
小结: # 和 $ 的区别 1. #使用 ?在 sql语句中作占位符, 使用PreparedStatement执行 sql,效率高 2. #能够避免 sql注入,更安全。 3. $ 不使用占位符,是字符串连接方式,使用 Statement 对象执行 sql,效率低 4. $ 有 sql 注入的风险,缺乏安全性。 5. $ 可以替换表名或者列名
3.3.封装MyBatis输出结果
3.3.1.resultType
resultType: 执行sql得到ResultSet转换的类型 ,使用类型的完全限定名或别名。注意如果返回的是集合,那应该设置为集合包含的类型,而不是集合本身。 resultType 和 resultMap,不能同时使用。 例如以下 Mapper 文件:
<select id="selectStudentById" resultType="com.gxust.bean.Student">
select id,name,age,email from student where id=#{studentId}
</select>
其中 resultType=“com.gxust.bean.Student” 表示Sql语句执行后得到的列的数据将转换为 java 对象Student。 其中和JDBC处理查询结果集的过程相似:
ResultSet res = stmt.executeQuery("select * from student");
while(res.next()){
Student stu = new Student();
stu.setId(res.getInt("id"));
stu.setName(res.getString("name"));
stu.setAge(res.getInt("age"));
stu.setEmail(res.getString("email"));
stuList.add(stu);
}
1)返回结果为简单类型
接口方法:
int countStudent();
Mapper文件:
<select id="countStudent" resultType="int">
select count(1) from student
</select>
测试方法:
@Test
public void testInt(){
StudentDao dao = jdbcUtil.getSqlSession().getMapper(StudentDao.class);
int count = dao.countStudent();
System.out.println(count);
}
2)返回结果为对象类型
接口方法:
Student selectStudentById(int id);
Mapper文件:
<select id="selectStudentById" resultType="com.gxust.bean.Student">
select id,name,age,email from student where id=#{studentId}
</select>
测试方法:
@Test
public void testParamter(){
SqlSession session = jdbcUtil.getSqlSession();
StudentDao mapper = session.getMapper(StudentDao.class);
Student student = mapper.selectStudentById(1);
System.out.println(student);
}
3)返回结果为Map类型
sql的查询结果作为 Map的 key和 value。推荐使用 Map<Object,Object>。 注意: Map 作为接口返回值, sql语句的查询结果最多只能有一条记录。 大于一条记录是错误。 接口方法:
Map<Object,Object> selectStudentReturnMap(int id);
Mapper文件:
<select id="selectStudentReturnMap" resultType="java.util.HashMap">
select name ,email from student where id = #{studentId}
</select>
测试方法:
@Test
public void testReturnMap(){
StudentDao dao = jdbcUtil.getSqlSession().getMapper(StudentDao.class);
Map<Object, Object> map = dao.selectStudentReturnMap(1);
System.out.println("结果是:"+map);
}
若使用 Map作为返回值,若查询结果的列数大于2,则第一列作为 key,其余列的结果作为 value数组
3.3.2.resultMap
resultMap可以自定义sql的结果和java对象属性的映射关系。更灵活的把列值赋值给指定属性。 常用在列名和java对象属性名不一样的情况。 使用方式: 1.先定义 resultMap,指定列名和属性的对应关系。 2.在 < select >中把 resultType 替换为 resultMap。 示例: 创建新的实体类对象OtherStudent
public class OtherStudent {
private Integer stuId;
private String stuName;
private Integer stuAge;
private String stuEmail;
}
接口方法:
OtherStudent selectByresultMap(@Param("name") String name);
Mapper文件:
<resultMap id="myStudent" type="com.gxust.bean.OtherStudent">
<id column="id" property="stuId"/>
<result column="name" property="stuName"/>
<result column="age" property="stuAge"/>
<result column="email" property="stuEmail"/>
</resultMap>
<select id="selectByresultMap" resultMap="myStudent">
select id,name,age,email from student where name=#{name}
</select>
测试方法:
@Test
public void testResultMap(){
StudentDao dao = jdbcUtil.getSqlSession().getMapper(StudentDao.class);
OtherStudent otherStudent = dao.selectByresultMap("宁荣荣");
System.out.println("查询结果是:"+otherStudent);
}
3.3.3.实体类属性名和列名不同的处理方式
1)使用列别名和< resultType >
有如下实体类OtherStudent:
public class OtherStudent {
private Integer stuId;
private String stuName;
private Integer stuAge;
private String stuEmail;
}
我们发现,该实体类的属性与数据库表中的列名不一样,而mybatis是通过实体类中的setter方法进行属性赋值,若表中的列名为name,则调用setName()方法进行赋值,若找不到该set方法,则mybatis将会报错。 解决方案1:sql语句中使用列别名 接口方法:
OtherStudent selectByresultMap(@Param("name") String name);
Mapper文件:
<select id="selectByresultMap" resultType="com.gxust.bean.OtherStudent">
select id as stuId,name as stuName,age as stuAge,email as stuEmail from student
where name = #{name}
</select>
测试方法:
@Test
public void testResultMap(){
StudentDao dao = jdbcUtil.getSqlSession().getMapper(StudentDao.class);
OtherStudent otherStudent = dao.selectByresultMap("宁荣荣");
System.out.println("查询结果是:"+otherStudent);
}
2)使用< resultMap>
接口方法:
OtherStudent selectByresultMap(@Param("name") String name);
Mapper文件:
<resultMap id="myStudent" type="com.gxust.bean.OtherStudent">
<id column="id" property="stuId"/>
<result column="name" property="stuName"/>
<result column="age" property="stuAge"/>
<result column="email" property="stuEmail"/>
</resultMap>
<select id="selectByresultMap" resultMap="myStudent">
select id,name,age,email from student where name=#{name}
</select>
测试方法:
@Test
public void testResultMap(){
StudentDao dao = jdbcUtil.getSqlSession().getMapper(StudentDao.class);
OtherStudent otherStudent = dao.selectByresultMap("宁荣荣");
System.out.println("查询结果是:"+otherStudent);
}
3.4.模糊like查询
模糊查询的实现有两种方式,一是 java 代码中给查询数据加上 “%”;二是在 mapper文件 sql语句的条件位置加上“%"。 现有需求:查询姓名包含“荣”字的学生信息 例一:在 java 代码中提供要查询的 "%荣%" 接口方法:
List<Student> selectLikeOne(String name);
Mapper文件:
<select id="selectLikeOne" resultType="com.gxust.bean.Student">
select id,name,email,age from student
where name like #{name}
</select>
测试方法:
@Test
public void testLikeOne(){
String name= "%荣%";
StudentDao dao = jdbcUtil.getSqlSession().getMapper(StudentDao.class);
List<Student> students = dao.selectLikeOne(name);
for (Student student:students){
System.out.println(student);
}
}
例二:在Mapper文件中使用 like name “%” #{参数} "%" 接口方法:
List<Student> selectLikeTwo(String name);
Mapper文件:
<select id="selectLikeTwo" resultType="com.gxust.bean.Student">
select id,name,email,age from student
where name like "%" #{name} "%"
</select>
测试方法:
@Test
public void testLikeOne(){
String name= "%荣%";
StudentDao dao = jdbcUtil.getSqlSession().getMapper(StudentDao.class);
List<Student> students = dao.selectLikeTwo(name);
for (Student student:students){
System.out.println(student);
}
}
4.MyBatis框架动态SQL
动态SQL,通过 MyBatis提供的各种标签对条件作出判断以实现动态拼接 SQL 语句。 这里的条件判断使用的表达式为 OGNL 表达式。 常用的动态SQL 标签有 < if >、 < where >、 < choose/ > 、< foreach >等。MyBatis的动态 SQL语句,与 JSTL中的语句非常相似。动态SQL,主要用于解决查询条件不确定的情况:在程序运行期间,根据用户提交的查询条件进行查询。提交的查询条件不同,执行的 SQL 语句不同。若将每种可能的情况均逐一列出,对所有条件进行排列组合,将会出现大量的 SQL语句。此时,可使用动态 SQL来解决这样的问题
4.1.环境准备
创建新的 maven项目,加入 mybatis mysql驱动依赖创建实体类 Student,StudentDao接口StudentDao.xml , mybatis.xml , 测试类使用之前的表student。在 mapper的动态 SQL中若出现大于号(>)、小于号 (<)、大于等于号 (>=),小于等于号 (<=)等符号,最好将其转换为实体符号。否则,XML可能会出现解析出错问题。特别是对于小于号(<),在 XML中是绝不能出现的。否则解析 mapper 文件会出错。 实体符号表:
< | 小于 | < |
---|
<= | 小于等于 | <= | > | 大于 | > | >= | 大于等于 | >= |
4.2.动态SQL之< if >
语法:< if test=“条件” > Sql语句部分 < /if > 分析: 对于该标签的执行,当test的值为true时,会将其包含的Sql语句片段拼接到其所在的Sql语句之中。 示例: 接口方法:
List<Student> selectStudentIf(Student student);
Mapper文件:
<select id="selectStudentIf" resultType="com.gxust.bean.Student">
select id,name,email,age from student
where 1=1 /*添加1=1这句话防止if条件判断为false时导致where后无语句,程序报错*/
<if test="name != null and name != ''">
and name = #{name}
</if>
<if test="age > 0">
and age > #{age}
</if>
</select>
测试方法:
@Test
public void testIf(){
Student student = new Student();
student.setName("宁荣荣");
student.setAge(18);
StudentDao dao = jdbcUtil.getSqlSession().getMapper(StudentDao.class);
List<Student> students = dao.selectStudentIf(student);
for (Student stu:students){
System.out.println(stu);
}
}
4.3.动态SQL之< where >
< if/ >标签中存在一个比较麻烦的地方:需要在 where 后手工添加 1=1 的子句。因为,若 where后 的所有 < if/ >条件均为 false,而 where后若又没有 1=1 子句,则 SQL 中就会只剩下一个空的 where 导致SQL查询出错。所以,在 where 后,需要添加永为真子句 1=1,以防止这种情况的发生。但当数据量很大时,会严重影响查询效率。 解决方法: 使用< where/ >标签,在有查询条件时, 可以自动添加上 where子句;没有查询条件时,不会添加 where子句。需要注意的是,第一个 < if/ >标签中的 SQL片段,可以不包含and。不过,写上and也不会报错,因为系统会将多出的 and 去掉。但其它 < if/ >中 SQL片断的 and,必须要求写上。否则 SQL语句将拼接出错。 语法:< where > 其他动态 sql < /where > 示例: 接口方法:
List<Student> selectStudentWhere(Student student);
Mapper文件:
<select id="selectStudentWhere" resultType="com.gxust.bean.Student">
select id,name,age,email from student
<where>
<if test="name != null and name != ''">
name = #{name}
</if>
<if test="age > 0">
and age > #{age}
</if>
</where>
</select>
测试方法:
@Test
public void testWhere(){
Student student = new Student();
student.setName("唐三");
student.setAge(18);
StudentDao dao = jdbcUtil.getSqlSession().getMapper(StudentDao.class);
List<Student> students = dao.selectStudentWhere(student);
for (Student stu:students){
System.out.println(stu);
}
}
4.4.动态SQL之< foreach >
< foreach/ >标签用于实现对于数组与集合的遍历。对其使用,需要注意:
collection表示要遍历的集合类型 , list array等。 open、 close、 separator为对遍历内容的 SQL拼接。
语法:
<foreach collection="集合类型" open="开始的字符" close="结束的字符"
item="集合中的成员" separator="集合成员之间的分隔符">
#{item的值}
</foreach>
4.4.1.遍历List<简单类型>
需求:现需要我们查询学生id是2、4、6的学生信息 分析:对于这个需求,我们在数据库中可以很简单的使用Sql语句:SELECT * FROM student WHERE id IN(2,4,6) 查询出来,那么,我们如何在Java代码中实现呢?
解决方法:将要查询的 id 值放入List集合中,使用foreach标签遍历该集合中的元素,并拼接成 (2,4,6) 的形式,其集合类型为 list ,开始字符为’ ( ',结束字符为 ’ ) ',分隔符为 ’ , '。 代码示例: 接口方法:
List<Student> selectStudentForList(List<Integer> idList);
Mapper文件:表达式中的 List 使用 list 表示,其大小使用 list.size表示。
<select id="selectStudentForList" resultType="com.gxust.bean.Student">
select id,name,age,email from student
<if test="list != null and list.size > 0">
where id in
<foreach collection="list" open="(" item="id" separator="," close=")">
#{id}
</foreach>
</if>
</select>
测试方法:
@Test
public void testForList(){
List<Integer> idList = new ArrayList<>();
idList.add(2);
idList.add(4);
idList.add(6);
StudentDao dao = jdbcUtil.getSqlSession().getMapper(StudentDao.class);
List<Student> students = dao.selectStudentForList(idList);
for (Student stu:students){
System.out.println(stu);
}
}
4.4.2.遍历List<对象类型>
需求:现还是需要我们查询学生id是2、4、6的学生信息,但是,要求接口参数为List<对象类型> 分析:实现这个需求我们只需要弄明白Mapper文件中的Sql语句该如何拿到我们放入List的对象的属性的值即可 解决方法:取属性值的方法为—— 对象.属性名 接口方法:
List<Student> selectStudentForObject(List<Student> stuList);
Mapper文件:表达式中的 List 使用 list 表示,其大小使用 list.size表示。
<select id="selectStudentForObject" resultType="com.gxust.bean.Student">
select id,name,age,email from student
<if test="list != null and list.size > 0">
where id in
<foreach collection="list" open="(" item="stu" separator="," close=")">
#{stu.id}
</foreach>
</if>
</select>
测试方法:
@Test
public void testForObject(){
List<Student> list = new ArrayList<>();
Student student = new Student();
student.setId(2);
list.add(student);
Student student2 = new Student();
student2.setId(4);
list.add(student2);
Student student3 = new Student();
student3.setId(6);
list.add(student3);
StudentDao dao = jdbcUtil.getSqlSession().getMapper(StudentDao.class);
List<Student> students = dao.selectStudentForObject(list);
for (Student stu:students){
System.out.println(stu);
}
}
4.5.动态SQL之代码片段
< sql/ >标签用于定义 SQL 片断,以便其它 SQL 标签复用。而其它标签使用该 SQL片断,需要使用 < include/ >子标签。 该 < sql/ >标签可以定义 SQL语句中的任何部分,所以 < include/ >子标签可以放在动态 SQL的任何位置。 使用示例 接口方法:
List<Student> selectStudentSqlFragment(List<Student> stuList);
Mapper文件:
<sql id="findStudents">
select id,name,age,email from student
</sql>
<select id="selectStudentSqlFragment" resultType="com.gxust.bean.Student">
<include refid="findStudents"/> /*引用自定义sql片段*/
<if test="list != null and list.size > 0">
where id in
<foreach collection="list" open="(" item="stu" separator="," close=")">
#{stu.id}
</foreach>
</if>
</select>
测试方法:
@Test
public void testForObject(){
List<Student> list = new ArrayList<>();
Student student = new Student();
student.setId(2);
list.add(student);
Student student2 = new Student();
student2.setId(4);
list.add(student2);
Student student3 = new Student();
student3.setId(6);
list.add(student3);
StudentDao dao = jdbcUtil.getSqlSession().getMapper(StudentDao.class);
List<Student> students = dao.selectStudentSqlFragment(list);
for (Student stu:students){
System.out.println(stu);
}
}
5.MyBatis主配置文件
5.1.主配置文件
之前项目中使用的mybatis.xml是主配置文件。主配置文件特点是: 1.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">
2.根元素,<configuration> 3.主要包含内容
定义别名 数据源 mapper文件
5.2.dataSource标签
Mybatis中访问数据库,采用连接池技术,但它采用的是自己的连接池技术。在 Mybatis 的 mybatis.xml配置文件中,通过 <dataSource type="pooled"> 来实现 Mybatis中连接池的配置。
5.2.1.dataSource类型
从上图中可以看出,Mybatis将数据源分为三类:
- UNPOOLED 不使用连接池的数据源
- POOLED 使用连接池的数据源
- JNDI 使用 JNDI实现的数据源
其中 UNPOOLED ,POOLED数据源实现了 javax.sq.DataSource接口, JNDI和前面两个实现方式不同。
5.2.2.dataSource配置
在MyBatis.xml主配置文件,配置 dataSource:
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybaits?charset=utf-8"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</dataSource>
MyBatis 在初始化时,根据 <dataSource> 的 type 属性来创建相应类型的的数据源 DataSource,即 POOLED MyBatis会创建 PooledDataSource 实例,UNPOOLED MyBatis 会创建 UnpooledDataSource 实例,JNDI MyBatis会从 JNDI服务上查找 DataSource 实例,然后返回使用。
5.3.事务
5.3.1.默认需要手动提交事务
Mybatis 框架是对 JDBC 的封装,所以 Mybatis 框架的事务控制方式,本身也是用 JDBC的 Connection对象的 commit(), rollback() ,Connection对象的 setAutoCommit()方法来设置事务提交方式的。
<transactionManager type="JDBC"/>
该标签用于指定MyBatis所使用的事务管理器。MyBatis支持两 种事务管理器类型 JDBC与 MANAGED。
JDBC:使用 JDBC的事务管理机制。即,通过 Connection的 commit()方法提交,通过 rollback()方法 回滚。但默认情况下, MyBatis将自动提交功能关闭了,改为了手动提交 。即程序中需要显式的对 事务进行提交或回滚。从日志的输出信息中可以看到。
Setting autocommit to false on JDBC Connection [com.mysql…
MANAGED:由容器来管理事务的整个生命周期(如 Spring容器)。
5.3.2.自动提交事务
设置自动提交的方式,factory 的openSession() 分为有参数和无参数的。
SqlSession openSession();
SqlSession openSession(boolean autoCommit);
若参数为true,使用自动提交,可以修改 mybatisUtil 的 getSqlSession() 方法。 session = factory.openSession(true); 再执行insert操作,无需执行session.commit() ,事务是自动提交的。
5.4.使用数据库属性配置文件
为了方便对数据库连接的管理, DB连接四要素数据一般都是存放在一个专门的属性文件中的。 MyBatis主配置文件需要从这个属性文件中读取这些数据。 使用步骤:
1.在classpath路径下,创建properties文件
在resources目录创建 jdbc.properties文件,文件名称自定义。 文件内容: jdbc.driver=com.mysql.jdbc.Driver jdbc.url=jdbc:mysql://localhost:3306/mybatis?charset=utf-8 jdbc.username=root jdbc.password=123456
2.使用properties标签
修改mybatis.xml主配置文件,文件开始位置加入: <properties resource="jdbc.properties"/>
<?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="jdbc.properties"/>
3.使用key指定值
<dataSource type="POOLED">
<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>
5.5.typeAliases(类型别名)
Mybatis支持默认别名,我们也可以采用自定义别名方式来开发 ,主要使用在 <select resultType="别名" 在mybatis.xml主配置文件定义别名如下:
<typeAliases>
<typeAlias type="com.gxust.bean.Student" alias="firststudent"/>
<package name="com.gxust.bean"/>
<package name="...其他包"/>
</typeAliases>
在mapper.xml文件中,使用别名表示类型:
<select id="selectStudents" resultType="firststudent">
select id,name,email,age from student
</select>
5.6.mappers(映射器)
1. <mapper resource=" " />
使用相对于类路径的资源,从 classpath路径查找文件 例如:<mapper resource="com/gxust/dao/StudentDao.xml" />
2. <package name=""/>
指定包下的所有Dao接口 例如:<package name="com.gxust.dao"/> 注意:此种方法要求Dao接口名称和 mapper 映射文件名称相同,且在同一个目录中。`
6.扩展
6.1.PageHelper
6.1.1.MyBatis通用分页插件
官方地址:https://github.com/pagehelper/Mybatis–PageHelper PageHelper 支持多种数据库:
- Oracle
- Mysql
- MariaDB
- SQLite
- Hsqldb
- PostgreSQL
- DB2
- SqlServer(2005,2008)
- Informix
- H2
- SqlServer2012
- Derby 13. Phoenix
6.1.2.基于PageHelper分页
实现步骤:
1)添加maven坐标
在pom.xml中加入下面的依赖:
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>5.1.10</version>
</dependency>
2)加入plugin配置
在mybaits主配置文件mybatis.xml的<environments> 之前加入
<plugins>
<plugin interceptor="com.github.pagehelper.PageInterceptor"/>
</plugins>
3)PageHelper对象
在查询语句之前调用PageHelper.startPage 静态方法 。 除了PageHelper.startPage 方法外,还提供了类似用法的 PageHelper.offsetPage 方法。 在你需要进行分页的 MyBatis 查询方法前调用 PageHelper.startPage 静态方法即可,紧跟在这个方法后的第一个MyBatis 查询方法会被进行分页。
示例: 接口方法:
List<Student> selectAll();
Mapper文件:
<select id="selectAll" resultType="com.gxust.bean.Student">
select * from student order by id
</select>
测试方法:
@Test
public void testPageHelper(){
StudentDao dao = jdbcUtil.getSqlSession().getMapper(StudentDao.class);
PageHelper.startPage(1,2);
List<Student> students = dao.selectAll();
}
结果展示:
== > Preparing: SELECT count(0) FROM student == > Parameters: <== Columns: count(0) <== Row: 7 <== Total: 1 == > Preparing: select * from student order by id LIMIT ? == > Parameters: 2(Integer) <== Columns: id, name, email, age <== Row: 1, 唐三, tangsan@qq.com, 20 <== Row: 2, 小舞, xiaowu@qq.com, 19 <== Total: 2
|