目录
1、简介
什么是MyBatis?
- MyBatis是一款优秀的持久层框架
- 它支持定制化SQL、存储过程以及高级映射。
- MyBatis避免了几乎所有的JDBCI代码和手动设置参数以及获取结果集。
- MyBatis 可以使用简单的XML或注解来配置和映射原生类型、接口和Java的POJO(Plain old JavaObjects,普通老式Java对象)为数据库中的记录。
MyBatis本是 apache的一个开源项目 iBatis, 2010年这个项目由 apache software foundation迁移到了google code,并且改名为MyBatis 。2013年11月迁移到Github。
持久层
数据持久化:jdbc、数据库、io文件保存等
持久层
Dao层、Servci层、Controller层
- 完成持久化工作的代码块
- 层与层之间界限明显
为啥要用Mybatis?
-
帮程序将数据存入数据库中
-
方便
-
传统的JDBC代码太复杂了。简化。框架。自动化。
2、第一个Mybatis程序
步骤:
- 添加MyBatis的坐标
- 创建user数据表
- 编写User实体类
- 编写映射文件UserMapper.xml
- 编写核心配置文件SqIMapConfig.xml
- 测试代码
1、在pom.xml添加依赖
<!--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>5.1.9</version>
</dependency>
2、创建实体类
public class User {
private String phone;
private String password;
private int id;
@Override
public String toString() {
return "User{" +
"phone='" + phone + '\'' +
", password='" + password + '\'' +
", id=" + id +
'}';
}
public String getPhone() {
return phone;
}
public void setPhone(String phone) {
this.phone = phone;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
}
3、编写映射文件Mapper
<?xml version="1.0" encoding="UTF-8" ?>
<!--指定约束文件
作用:限制,检查在当前文件中出现的标签,属性必须符合mabatis的要求-->
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="userMapper">
<!--sql语句的编写-->
<select id="findall" resultType="com.wei.domain.User">
select * from users;
</select>
</mapper>
4、编写核心配置文件
<?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="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/数据库名?useSSL=true&useUnicode=true&characterEncoding=UTF-8"/>
<property name="username" value="用户名"/>
<property name="password" value="密码"/>
</dataSource>
</environment>
</environments>
<mappers>
<!--
一个mapper标签指定一个配置文件的位置
编译之后从classes开始下的xml文件
-->
<mapper resource="com/wei/domain/UserMapper.xml"/>
</mappers>
</configuration>
5、测试代码
public class MyTest01 {
@Test
public void test01() throws IOException {
String config = "sqlMapConfig.xml";
//获得核心配置文件
InputStream is = Resources.getResourceAsStream(config);
//获得工厂对象
SqlSessionFactory build = new SqlSessionFactoryBuilder().build(is);
// 获得session会话对象,这个就是数据库对象了
SqlSession sqlSession = build.openSession();
//执行操作
//selectList 参数:mapper文件中的namespace + id
List<User> userList = sqlSession.selectList("userMapper.findall");
//打印数据
for(User item : userList){
System.out.println(item);
}
sqlSession.close();
}
}
输出结果:
User{id=1, phone=‘120’, password=‘123456’}
User{id=2, phone=‘987654321’, password=‘123456’}
User{id=3, phone=‘110’, password=‘123456’}
- 注意:IDEA默认不扫描 .properties、.xml 文件,所以必须要在pom.xml文件的<build>标签中加入一下这一行
<resources>
<resource>
<directory>src/main/java</directory> <!--所在的目录-->
<includes><!--包括目录下的.properties、.xml 文件都会扫描到-->
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>false</filtering>
</resource>
</resources>
3、CRUD
Insert
向数据库表中插入数据
标签属性:
- id
- parameType:参数的类型
1、编写Mapper.xml文件
<?xml version="1.0" encoding="UTF-8" ?>
<!--指定约束文件
作用:限制,检查在当前文件中出现的标签,属性必须符合mabatis的要求-->
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="userMapper">
<!--sql语句的编写-->
<select id="findall" resultType="com.wei.domain.User">
select * from users;
</select>
<insert id="insertOne" parameterType="com.wei.domain.User">
insert into users value(#{id},#{phone},#{password})
</insert>
</mapper>
2、继续测试
@Test
public void test02() throws IOException {
//创建User对象,用来插入数据库
User user = new User();
user.setId(4);
user.setPhone("147258369");
user.setPassword("123456");
String config = "sqlMapConfig.xml";
//获得核心配置文件
InputStream is = Resources.getResourceAsStream(config);
//获得工厂对象
SqlSessionFactory build = new SqlSessionFactoryBuilder().build(is);
// 获得session会话对象,这个就是数据库对象了
SqlSession sqlSession = build.openSession();
//执行插入操作,参数:第一个还是namespace + id,第二个是要插入的对象
sqlSession.insert("userMapper.insertOne",user);
sqlSession.commit();
sqlSession.close();
}
这时需要我们手动提交事务,因为Mybatis默认不自动提交事务。
为什么sqlSession中有事务提交?因为它就是个数据库对象啊。
刷新数据库,可以看到新的数据被插入
注意事项
- 插入语句使用 insert 标签
- 在映射文件中使用 parameterType 属性指定要插入的数据类型
- Sql语句中使用 #{实体类属性名称} 方式引用实体中的属性值
- 插入操作使用的API是 sqlsesion.insert(“命名空间.id”,实体对象);
- 插入、更新、删除操作都涉及到数据库中数据变化,所以要手动提交事务,即
sqlSession.commit()
update
修改数据库中数据
1、编写Mapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!--指定约束文件
作用:限制,检查在当前文件中出现的标签,属性必须符合mabatis的要求-->
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="userMapper">
<!--sql语句的编写-->
<select id="findall" resultType="com.wei.domain.User">
select * from users;
</select>
<insert id="insertOne" parameterType="com.wei.domain.User">
insert into users value(#{id},#{phone},#{password})
</insert>
<update id="update" parameterType="com.wei.domain.User">
update users set phone=#{phone},password=#{password} where id=#{id}
</update>
</mapper>
2、测试插入操作
@Test
public void test03() throws IOException {
//创建User对象,修改id为4的数据
User user = new User();
user.setId(4);
user.setPhone("123456789");
user.setPassword("654321");
String config = "sqlMapConfig.xml";
//获得核心配置文件
InputStream is = Resources.getResourceAsStream(config);
//获得工厂对象
SqlSessionFactory build = new SqlSessionFactoryBuilder().build(is);
// 获得session会话对象,这个就是数据库对象了
SqlSession sqlSession = build.openSession();
//修改id为4的数据
sqlSession.update("userMapper.update",user);
sqlSession.commit();
sqlSession.close();
}
注意事项
- 修改语句使用update标签
- 修改操作使用的API是sqlSession.update(“命名空间.id”,实体对象);
delete
删除数据库中的数据
1、编写Mapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!--指定约束文件
作用:限制,检查在当前文件中出现的标签,属性必须符合mabatis的要求-->
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="userMapper">
<!--sql语句的编写-->
<select id="findall" resultType="com.wei.domain.User">
select * from users;
</select>
<insert id="insertOne" parameterType="com.wei.domain.User">
insert into users value(#{id},#{phone},#{password})
</insert>
<update id="update" parameterType="com.wei.domain.User">
update users set phone=#{phone},password=#{password} where id=#{id}
</update>
<delete id="deleteById" parameterType="int">
delete from users where id = #{id}
</delete>
</mapper>
这里的删除操作是根据id来删除表中的数据段,只有一个参数,#{}中的名字随便起,由于id在数据库是int,所以这里参数类型是int
2、测试代码
@Test
public void test04() throws IOException {
String config = "sqlMapConfig.xml";
//获得核心配置文件
InputStream is = Resources.getResourceAsStream(config);
//获得工厂对象
SqlSessionFactory build = new SqlSessionFactoryBuilder().build(is);
// 获得session会话对象,这个就是数据库对象了
SqlSession sqlSession = build.openSession();
//第二个参数:删除id为4的一行数据
sqlSession.update("userMapper.deleteById",4);
sqlSession.commit();
sqlSession.close();
}
注意事项
- 删除语句使用delete标签
- Sql语句中使用#{任意字符串}方式引用传递的单个参数
- 删除操作使用的API是sqlSession.delete(“命名空间.id”,Object);
4、IDEA自定义模板
5、生命周期和作用域
生命周期,和作用域,是至关重要的,因为错误的使用会导致非常严重的并发问题
1、SqlSessionFactoryBuilder对象
- 主要用来生产SqlSessionFactory,之后就用不到了
- 放在局部变量
2、SqlSessionFactory对象
-
可以看成数据库连接池
-
SqlSessionFactory一旦被创建就应该在应用的运行期间一直存在,没有任何理由丢弃它或重新创建另一个实例。
-
因此 SqlSessionFactory的最佳作用域是应用作用域
-
一般都放在静态代码块中
3、SqlSession:
- 获取连接池中连接的请求
- 用完之后立即关闭
- 不是线程安全的,不能被共享,最佳作用域是请求或者方法的作用域
6、Mybatis的Dao层实现
传统方式
步骤:
-
编写实体类
-
编写UserMapper接口
-
写实现类去实现Mapper接口
-
编写Mapper文件
-
编写核心配置文件
-
测试
1、实体类
package com.wei.domain;
public class User {
private int id;
private String phone;
private String password;
@Override
public String toString() {
return "User{" +
"id=" + id +
", phone='" + phone + '\'' +
", password='" + password + '\'' +
'}';
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getPhone() {
return phone;
}
public void setPhone(String phone) {
this.phone = phone;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
2、UserMapper接口
package com.wei.dao;
import com.wei.domain.User;
import java.io.IOException;
import java.util.List;
public interface UserMapper {
// 查找所有用户信息
public List<User> findAll() throws IOException;
}
3、实现Mapper接口
package com.wei.dao.impl;
import com.wei.dao.UserMapper;
import com.wei.domain.User;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
public class UserMapperImpl implements UserMapper {
@Override
public List<User> findAll() throws IOException {
InputStream is = Resources.getResourceAsStream("sqlMapConfig.xml");
SqlSessionFactory build = new SqlSessionFactoryBuilder().build(is);
SqlSession sqlSession = build.openSession();
List<User> list = sqlSession.selectList("userMapper.findAll");
return list;
}
}
4、编写Mapper文件
<?xml version="1.0" encoding="UTF-8" ?>
<!--指定约束文件
作用:限制,检查在当前文件中出现的标签,属性必须符合mabatis的要求-->
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="userMapper">
<select id="findAll" resultType="com.wei.domain.User">
select * from users;
</select>
</mapper>
5、编写核心配置文件
<?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>
<!--通过propertiees标签加载外部的properties文件-->
<properties resource="jdbc.properties"></properties>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<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>
</environment>
</environments>
<mappers>
<mapper resource="com/wei/mapper/UserMapper.xml"/>
</mappers>
</configuration>
6、测试
@Test
public void test01() throws IOException {
UserMapper user = new UserMapperImpl();
List<User> all = user.findAll();
for(User item : all){
System.out.println(item);
}
}
输出结果:
User{id=1, phone=‘120’, password=‘123456’}
User{id=2, phone=‘987654321’, password=‘123456’}
User{id=3, phone=‘110’, password=‘123456’}
缺点:
总是要是对象的接口实现,太麻烦了
使用代理方式
采用Mybatis的代理开发方式实现DAO层的开发,这种方式是进入企业的主流。
Mapper接口开发方法只需要程序员编写Mapper接口(相当于Dao接口),由Mybatis框架根据接口定义创建接口的动态代理对象,代理对象的方法体同上边Dao接口实现类方法。
Mapper接口开发需要遵循以下规范:
- 1、Mapper.xml文件中的namespace与mapper接口的全限定名相同
- 2、Mapper接口方法名和Mapper.xml中定义的每个statement的id相同
- 3、Mapper接口方法的输入参数类型和mapper.xml中定义的每个sql的parameterType的类型相同
- 4、Mapper接口方法的输出参数类型和mapper.xml中定义的每个sql的resultType的类型相同
测试
@Test
public void test01() throws IOException {
InputStream is = Resources.getResourceAsStream("sqlMapConfig.xml");
SqlSessionFactory build = new SqlSessionFactoryBuilder().build(is);
SqlSession sqlSession = build.openSession();
//使用代理
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
//直接调用方法
List<User> all = mapper.findAll();
for(User item : all){
System.out.println(item);
}
sqlSession.close();
}
7、动态SQL
sql语句是动态变化的,可以根据条件获取不同的slq语句
主要是where的部分发生变化
动态sql的实现使用的是mybatis提供的标签
- <if>
- <where>
- <foreach>
if 标签
对于该标签的执行,当test的值为true时,会将其包含的SQL片断拼接到其所在的SQL语句中。
语法:
<if test=“条件”>
sql语句的部分
</if>
<!--
<if:test="使用参数java对象的属性值作为判断条件,语法属性=xxx值" >
-->
<mapper namespace="com.wei.dao.UserMapper">
<select id="findByCondition"
parameterType="com.wei.domain.User"
resultType="com.wei.domain.User">
select * from users where 1=1
<if test="id!=0">
and id = #{id}
</if>
<if test="phone!=null">
and phone = #{phone}
</if>
</select>
</mapper>
id和phone不传入情况下为了防止出错,设置where 1=1
where 标签
用来包含多个<if>的,当多个if中有一个成立的,<where>会自动增加一个where关键字,并去掉 if 中多余的and , or等
<select id="findByCondition02" resultType="com.wei.domain.User">
select * from student
<where>
<if test="id!=0">
and id = #{id}
</if>
<if test="phone!=null">
and phone = #{phone}
</if>
</where>
</select>
foreach 标签
用来循环java中的数组,list集合的。主要用在sql的in语句中。
<foreach collection="" item="" open="" close="" separator="">
</foreach>属性:
- collection:表示接口中的方法参数的类型,如果是数组使用array,如果是list集合使用list
- item:自定义的,表示数组和集合成员的变量(随便起名)
- open:循环开始是的字符
- close:循环结束时的字符
- separator:集合成员之间的分隔符
java代码
sqlsession sqlsession = MyBatisutils.getsqlsession();
UserMapper mapper = sqlsession.getMapper(UserMapper.class);
List<Integer> list = new ArrayList<>();
list.add(1);
list.add(2);
List<User> users = mapper.findByIds(list);
for(User item: users){
system.out.println(item);
}
mapper文件
<select id="findByIds" parameteType="list"
resultType="com.wei.domain.User">
select * from users
<where>
<foreach collection="list" item="myid" open="id in (" close=")" separator=",">
#{myid}
</foreach>
</where>
</select>
mysql预编译后结果:Preparing: select * from users where id in ( ?, ? )
SQL片段抽取
<sql/>标签 用于定义sql 片断,以便其它SQL标签复用。而其它标签使用该SQL片断,需要使用
<include/>子标签。该<sql/>标签可以定义sQL语句中的任何部分,所以<include/>子标签可以放在动态sQL的任何位置。步骤:
- 1):先定义<sql id=“代码片段名称” > sql语句 / 表名 / 字段等 </sql>
- 2):使用<include refid=“代码片段名”/> 引用
<sql id="stusql"> select * from student /sql>
<select id="..." resultType="...">
<include refid="stusql"/> <!--相当于select * from student -->
</select>
8、解决属性名和字段名不一致问题
使用resultMap
结果集映射
数据库中名字:id name pwd
实体类中 : id name password
例如:
<!--结果集映射-->
<resultMap id="UserMap" type="User">
<!-- coLumn数据库中的字段名,property实体类中的属性 -->
<result column="id" property="id" />
<result column="name" property="name" />
<result column="pwd" property="password" />
</resultMap>
<select id="getUserById" resultMap="UserMap">
select * from user where id = #{id}
</select>
9、开启日志
STDOUT_LOGGING
如果一个数据库操作,出现了异常,我们需要排错。日志就是最好的助手!
SLF4J
LOG4J【掌握】
LOG4j2
JDK_LOGGING
COMMONS_LOGGING
STDOUT_LOGGING【掌握】
NO_LOGGING
在Mybatis核心配置文件中添加下面三行即可开启日志
<settings> <setting name="logImpl" value="STDOUT_LOGGING"/></settings>
LOG4J
1、添加依赖
2、在resources目录下创建log4j.properties文件,写入一下内容
#将等级为DEBUG的日志信息输出到console和file这两个目的地,console和file的定义在下面的代码
log4j.rootLogger=DEBUG,console,file
#控制台输出的相关设置
log4j.appender.console = org.apache.log4j.ConsoleAppender
log4j.appender.console.Target = System.out
log4j.appender.console.Threshold=DEBUG
log4j.appender.console.layout = org.apache.log4j.PatternLayout
log4j.appender.console.layout.ConversionPattern=[%c]-%m%n
#文件输出的相关设置
log4j.appender.file = org.apache.log4j.RollingFileAppender
log4j.appender.file.File=./log/wei.log //将日志输出到当前文件下log/wei.log文件中
log4j.appender.file.MaxFileSize=10mb
log4j.appender.file.Threshold=DEBUG
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=[%p][%d{yy-MM-dd}][%c]%m%n
#日志输出级别
log4j.logger.org.mybatis=DEBUG
log4j.logger.java.sql=DEBUG
log4j.logger.java.sql.Statement=DEBUG
log4j.logger.java.sql.ResultSet=DEBUG
log4j.logger.java.sql.PreparedStatement=DEBUG
3、 配置log4j为日志的实现
Log4j的简单使用
-
1、再要使用log4j中的类中,导入包
-
2、获取日志对象,参数为当前类的class
-
static Logger logger = Logger.getLogger(UserDaoTest.class);
-
-
3、使用info、debug、error方法
logger.info("info:进入了方法"); logger.debug("debug:进入了方法"); logger.error("error:进入了方法");
10、使用注解开发
面向接口编程
大家之前都学过面向对象编程,也学习过接口,但在真正的开发中,很多时候我们会选择面向接口编程
根本原因︰解耦,可拓展,提高复用,分层开发中,上层不用管具体的实现,大家都遵守共同的标准,使得开发变得容易,规范性更好
在一个面向对象的系统中,系统的各种功能是由许许多多的不同对象协作完成的。在这种情况下,各个对象内部是如何实现自己的,对系统设计人员来讲就不那么重要了;
而各个对象之间的协作关系则成为系统设计的关键。小到不同类之间的通信,大到各模块之间的交互,在系统设计之初都是要着重考虑的,这也是系统设计的主要工作内容。面向接口编程就是指按照这种思想来编程。
关于接口的理解
-接口从更深层次的理解,应是定义(规范,约束)与实现(名实分离的原则)的分离。-接口的本身反映了系统设计人员对系统的抽象理解。
-接口应有两类:
-第一类是对一个个体的抽象,它可对应为一个抽象体(abstract class);-第二类是对一个个体某一方面的抽象,即形成一个抽象面(interface) ;-一个体有可能有多个抽象面。抽象体与抽象面是有区别的。
三个面向区别
-面向对象是指,我们考虑问题时,以对象为单位,考虑它的属性及方法.
-面向过程是指,我们考虑问题时,以一个具体的流程(事务过程)为单位,考虑它的实现.
-接口设计与非接口设计是针对复用技术而言的,与面向对象(过程)不是一个问题.更多的体现就是对系统整体的架构
使用注解开发
1 注解在接口方法上实现
// 查询所有用户
@Select("select * from users")
List<User> getList();
2 核心配置文件中绑定接口
<mappers>
<mapper class="com.dao.UserMapper.class"/>
</mappers>
本质:反射机制实现
底层:动态代理
接口的方法中,如果参数多于1个,每个基本类型参数前必须加上@pamar
// 查询所有用户
@Select("select * from users where id = #{id},pwd = #{pwd}")
List<User> getList(@Param("id") int id,@Param("pwd") String password);
如果是引用类型,不用写@Param注解,直接写其中的属性名即可
11、参数
多个参数 使用@Param方式
当Dao接口方法多个参数,需要通过名称使用参数。在方法形参前面加入@Param(“自定义参数名")mapper文件使用#{自定义参数名}。
例如定义: List<Student> selectStudent(@Param("personName") String name ) { ...}
mapper文件: select * from student where name = #{ personName}
接口方法:
List<Student> selectMultiParam(@Param( "personName") String name, @Param ( "personAge") int age);
mapper 文件:
<select id="selectMultiParam" resultType="com.wei.domain.Users">
select * from users
where phone=#{personName} or password=#{password}
</select>
多个参数 使用对象方式传
使用java对象传递参数,java的属性值就是sql需要的参数值。每一个属性就是一个参数。
语法格式:#{ 对象中的属性名,javaType = java中数据类型名, jdbcType = 数据类型名称 }
javaType, jdbcType 的类型MyBatis可以检测出来,一般不需要设置。常用格式 #{ 对象中的属性名 }
多个参数 按位置
参数位置从0开始,引用参数语法 # arg位置},第一个参数是#{arg0},第二个是#{arg1}
注意: mybatis-3.3版本和之前的版本使用#{0},#{1}方式,从mybatis3.4开始使用#{arg0}方式。
接口方法:
List<User> selectByPhoneAndPwd(String phone,String pwd);
mapper文件
<select id="selectByPhoneAndPwd" resultType="com.wei.domain.Users">
select * from users where phone=#{arg0} or password= #{arg1}
</select>
多个参数 使用map
Map集合可以存储多个值,使用Map向mapper文件一次传入多个参数。Map集合使用String的 key,object类型的值存储参数。mapper文件使用# { key }引用参数值。
例如:`
Map<String,String> data = new HashMap<String,String>();
data.put("myphone","111222333");
data.put("mypwd","111222111");
接口方法:
List<User> selectMultiMap(Map<string,String>hap) ;
mapper文件:
<select id="selectNultiMap" resultType="com.wei.domain.User">
select id,name , email,age from student
where phone=#{myphone} or password=#(mypwd}
</select>
# 和 $
# 占位符
,告诉mybatis使用实际的参数值代替。并使用PrepareStatement对象执行sql 语句, #{…}代替sql语句的“?”。这样做更安全,更迅速,通常也是首选做法
mapper文件
<select id="selectById" resultType="com.wei.domain.User">
select * from users where id = #{uid}
</select>
转为MyBatis 的执行是:
String sql = ” select * from users where id=? ";
PreparedStatement ps = conn.prepareStatement(sql);
uid的值如假设为1005
ps.setInt(1,1005);
解释:
where id = ? 就是 where id = #{uid}
ps.setInt(1,1005) , 1005会替换掉 #{uId}
$ 字符串替换
,告诉mybatis使用$包含的“字符串”替换所在位置。使用Statement把 sql语句和${}的内容连接起来。主要用在替换表名,列名,不同列排序等操作。
使用# select * from users where id=#{uid}
#的结果:select* from users where id=?
使用$ select * from users where id=${uid}
$的结果:select * from users where id=1001
string sql="select * from users where id= " + “1001”;
总之:使用$的statement对象执行sql,效率比Preparedstatement低
$ :可以替换表名或者列名,你能确定数据是安全的。可以使用$
# 优点:
- #使用?在sql语句中做站位的,使用Preparedstatement执行sql,效窣高
- #能够避免sql注入,更安全
$ 缺点:
- $ 不使用占位符,是字符串连接方式,使用statement对象执行sql,效率低
- $ 有sql注入的风险,缺乏安全性
- $ :可以替换表名或者列名
12、封装MyBatis的输出结果
使用resultType
resultType结果类型,指sql语句执行完毕后,数据转为的java对象。
处理方式:
- 1、mybatis执行sql语句,然后mybatis调用类的无参数构造方法,创建对象。
- 2、mybatis把Resultset指定列值付给同名的属性。
注意:resultType 和 resultMap,不能同时使用。
<select id=“selectMultiPosition” resultType=“com.wei.domain.Users”>
select * from users
</select>
对等的jdbc
Resultset rs = executeQuery (" select* from users" );
while (rs.next(){
User user = new User();
User .setId(rs.getInt(“id”));
User.setPhone (rs.getstring ( “phone” ) );
User.setPassword (rs.getstring ( “password” ) );
}//需要有set方法
简单类型
接口方法:
int countUser();
mapper 文件:
<select id="countStudent" resultType="int">
select count(*) from users
</select>
resultType结果类型:1、类型的全限定名 2、类型的别名
自定义类型的别名
在mybatis的主配置文件中配置,使用<typeAlias>标签来定义或者使用<package>来定义
<typeAliases>
<!-- 指定一个类型一个自定义别名
type:自定义的类型全限定名
alias:别名-->
<typeAlias type="" alias=""/>
</typeAliases>
<typeAliases>
<!--
name:包名 这个包中的所有类的类名,就是别名-->
<package name="com.wei.dao"/>
</typeAliases>
返回Map
sql的查询结果作为Map 的 key 和 value。推荐使用Map<Object,Object>。
注意:Map作为接口返回值,sql语句的查询结果最多只能有一条记录。大于一条记录是错误。
resultMap
结果映射,指定列名h额java对象的属性对应关系
可以自定义列值赋值给那个java属性
当列名和属性名不一样时,一定要使用resultMap来指定
接口方法
List<Student> selectMap();
mapper.xml
<select id="selectMap" resultMap="userMap">
select * from users
</select>
<!--
1、先定义resultMap
2、在select标签中,使用resultMap来引用
id:自定义名称(用来表示这个resultMap)
type:java类型全限定名-->
<resultMap id="userMap" type="com.wei.dao.User">
<!--
id:表示这一列是数据库中的主键列
column:列名
property:java类型属性名-->
<id column="id" property="id"/>
<!--result:不是主键列-->
<result column="phone" property="phone">
<result column="password" property="password">
</resultMap>
属性名和列名不同的处理方式
1、使用列别名+resultType
2、使用resultMap
模糊查询 like
1、在java中指定号like的内容,如 %韦%
2、在mapper中拼接like的内容,注意:拼接不要用+
13、Mybatis核心配置文件
层级关系
- configuration(配置)
- properties(属性)
- settings(设置)
- typeAliases(类型别名)
- typeHandlers(类型处理器)
- objectFactory (对象工厂)
- plugins(插件)
- environments (环境配置)
- environment(环境变量)
- transactionManager(事务管理器)
- datasource(数据源)
- databaseIdProvider(数据库厂商标识)
- mappers(映射器)
environments (环境配置)
其中实物管理的类型有两种
- JDBC:这个配置就是直接使用了JDBC的提交和回滚设置,它依赖于从数据源得到的连接来管理事务作用域。
- MANAGED:这个配置几乎没做什么。它从来不提交或回滚一个连接,而是让容器来管理事务的整个生命周期(比如JEE应用服务器的上下文)。默认情况下它会关闭连接,然而一些容器并不希望这样,因此需要将closeConnection属性设置为false来阻止它默认的关闭行为。
其中数据源的类型有三种
- UNPOOLED:这个数据源的实现只是每次被请求时打开和关闭连接。
- POOLED:这种数据源的实现利用“池”的概念将JDBC连接对象组织起来。
- JNDI:这个数据源的实现是为了能在如EJB或应用服务器这类容器中使用,容器可以集中或在外部配置数据源,然后放置一个JNDI上下文的引用。
properties(属性)
可以通过properties属性来实现引用配置文件
这些属性都是可外部配置且可动态替换的,既可以在典型的Java属性文件中配置,亦可通过properties元素的子元素来传递。
<properties resource="db.properties">
<!--添加下面两行,如果外部配置文件中没有,则引用下面的字段-->
<property name="jdbc.username" value="abc"/>
<property name="jdbc.password" value="abc123"/>
</properties>
typeAliases(类型别名)
类型别名是为Java类型设置一个短的名字,它只和XML配置有关,存在的意义仅在于用来减少类完全限定名的冗余,例如:
在核心配置文件中为User类起一个别名
<!--给实体类其别名-->
<typeAliases>
<typeAlias type="com.wei.domain.User" alias="User"/>
</typeAliases>
此时就可以在Mapper文件中使用了,之前的com.wei.domain.User就可以写成user
<select id="findall" resultType="user">
select * from users;
</select>
也可以指定一个包名,MyBatis 会在包名下面搜索需要的Java Bean,比如:
扫描实体类的包,它的默认别名就为这个类的类名,首字母小写(其实大写也行)!
<!--在核心配置文件中-->
<!--给实体类其别名-->
<typeAliases>
<package name="com.wei.domain"/>
</typeAliases>
此时在Mapper文件中com.wei.domain.User也可以直接写成user
<!--sql语句的编写-->
<select id="getList" resultType="user">
select * from users
</select>
如果非要再改别名,需要在实体类上写注解
@Alias("myuser")
public class User(){...}
settings(设置)
这是MyBatis 中极为重要的调整设置,它们会改变MyBatis 的运行时行为。
日志的加载:
<!--在主配置文件中-->
<settings>
<setting name="logImpl" value="STDOUT_LOGGING">
</settings>
mappers(映射器)
该标签的作用是加载映射的,加载方式有如下几种:
方式一:使用resource绑定注册
相对于类路径的资源引用
<mappers>
<mapper resource="com/dao/UserMapper.xml"/>
</mappers>
方式二:使用class文件绑定注册
<mappers>
<mapper class="com.dao.UserMapper.xml"/>
</mappers>
使用classs注意:
- 接口和它的Mapper配置文件必须同名
- 必须在同一个包下
方式三:使用扫描包绑定注册
<mappers>
<mapper package="com.dao"/>
</mappers>
使用package注意:
- 接口和它的Mapper配置文件必须同名
- 必须在同一个包下
方式四:使用URL绑定注册
完全限定资源定位符(URL)
<mappers>
<mapper url:"磁盘中的文件路径"/>
</mappers>
14、多表操作
一对一
Mapper文件
<resultMap id="orderMap" type="com.wei.domain.Order">
<!--column:数据库表的字段名 property:实体类属性名-->
<id column="oid" property="id"></id>
<result column="ordertime" property="ordertime"></result>
<result column="total" property="total"></result>
<!-- <result column="uid" property="user.id"></result> -->
<!-- <result column="phone" property="user.phone"></result> -->
<!-- <result column="password" property="user.password"></result> --> <!--或者 property:当前实体类中的属性名 javaType:表示这个属性的类型 -->
<association property="user" javaType="com.wei.domain.User">
<id column="uid" property="id"></id>
<result column="phone" property="phone"></result>
<result column="passwrod" property="password"></result>
</association>
</resultMap>
<select id="findByCondition" parameterType="com.wei.domain.User"
resultMap="orderMap">
select * ,o.id oid from orders o, users u where o.uid = u.id
</select>
属性是一个对象就用association,如果是集合用constructor
一对多
查看用户有哪些订单order
<resultMap id="userMap" type="com.wei.domain.User">
<!--column:数据库表的字段名 property:实体类属性名-->
<id column="oid" property="id"></id>
<result column="phone" property="phone"></result>
<result column="password" property="password"></result>
<!--配置集合信息
property:集合名称
ofType:当前集合中的类型
-->
<constructor property="orderList" ofType="com.wei.domain.Order">
<!--封装的数据-->
<id column="oid" property="id"></id>
<result column="ordertime" property="ordertime"></result>
<result column="total" property="total"></result>
</constructor>
</resultMap>
<select id="findByCondition" parameterType="com.wei.domain.User" resultMap="userMap">
select * ,o.id oid from orders o, users u where o.uid = u.id
</select>
15、缓存
查询数据库耗费资源,一次查询结果暂存在一个可以直接取到的地方,再次查询相同数据时,直接从缓存中取
什么是缓存?
-
存在内存中的临时数据。
-
将用户经常查询的数据放在缓存(内存)中,用户去查询数据就不用从磁盘上(关系型数据库数据文件)查询,从缓存中查询,从而提高查询效率,解决了高并发系统的性能问题。
为什么使用缓存?
- 减少和数据库的交互次数,减少系统开销,提高系统效率。
什么样的数据能使用缓存?
-
经常查询并且不经常改变的数据。
-
MyBatis包含一个非常强大的查询缓存特性,它可以非常方便地定制和配置缓存。缓存可以极大的提升查询效率。
-
MyBatis系统中默认定义了两级缓存:一级缓存和二级缓存
- 默认情况下,只有一级缓存开启。(SqlSession级别的缓存,也称为本地缓存)
- 二级缓存需要手动开启和配置,他是基于namespace级别的缓存、
- 为了提高扩展性,MyBatis定义了缓存接口Cache。我们可以通过实现Cache接口来自定义二级缓存
一级缓存
也叫本地缓存(SqlSession),与数据库同一次会话期间查询到的数据会放在本地缓存中。以后如果需要获取相同的数据,直接从缓存中拿,没必须再去查询数据库;
步骤:
- 1、开启日志
- 2、测试在一个Session中查询两次相同记录
- 3、查看日志输出
缓存失效的情况
- 映射语句文件中的所有select语句的结果将会被缓存
- 映射语句文件中的所有insert、update和delete语句会刷新缓存
- 缓存会使用最近最少使用算法 (LRU,Least Recently Used)算法来清除不需要的缓存
- 缓存不会定时进行刷新(也就是说,没有刷新间隔)
- 缓存会保存列表或对象(无论查询方法返回哪种)的1024个引用
- 缓存会被视为读/写缓存,这意味着获取到的对象并不是共享的,可以安全地被调用者修改,而不干扰其他调用者或线程所做的潜在修改
二级缓存
- 二级缓存也叫全局缓存,一级缓存作用域太低了,所以诞生了二级缓存
- 基于namespace级别的缓存,一个名称空间,对应一个二级缓存;
- 工作机制
- 一个会话查询一条数据,这个数据就会被放在当前会话的一级缓存中;
- 如果当前会话关闭了,这个会话对应的一级缓存就没了;但是我们想要的是,会话关闭了,一级缓存中的数据被保存到二级缓存中;
- 新的会话查询信息,就可以从二级缓存中获取内容;
- 不同的mapper查出的数据会放在自己对应的缓存(map)中;
步骤:
- 1、在核心配置文件中开启全局缓存
<setting name="cacheEnable" value="true">
- 2、在指定宁的Mapper文件中使用二级缓存
<cache
eviction="FIFO"
flushInterval="60000"
size="512"
readonly="true"/>
<select id="getList" resultType="user" useCache="true"> <!--默认是false-->
select * from users
</select>
这个更高级的配置创建了一个 FIFO 缓存,每隔 60 秒刷新,最多可以存储结果对象或列表的 512 个引用,而且返回的对象被认为是只读的,因此对它们进行修改可能会在不同线程中的调用者产生冲突。
可用的清除策略有:
LRU
– 最近最少使用:移除最长时间不被使用的对象。FIFO
– 先进先出:按对象进入缓存的顺序来移除它们。SOFT
– 软引用:基于垃圾回收器状态和软引用规则移除对象。WEAK
– 弱引用:更积极地基于垃圾收集器状态和弱引用规则移除对象。默认的清除策略是 LRU。