mybatis采用 ORM 思想解决了实体和数据库映射的问题,对 jdbc 进行了封装,屏蔽了 jdbc api 底层访问细节,使我们不用与 jdbc api 打交道,就可以完成对数据库的持久化操作。
ORM:对象关系映射(Object Relational Mapping),以面向对象的思想来操作数据库
- 对象就是javabean对象
- 关系就是数据表
- javabean属性与表字段一一对应
mybatis的两种开发方式
- 使用原生接口
- Mapper代理实现自定义接口
Mybatis入门程序
1. pom.xml
<dependencies>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.5</version>
</dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.21</version>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13</version>
<scope>test</scope>
<!--lombok简化实体类编写-->
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.20</version>
</dependencies>
<build>
<resources>
<!-- 表示编译java源码时,包含src/main/java和src/main/resources目录下的xml、properties一起 -->
<!--如果mapper.xml在src/main/java目录下,就必须做这个配置,不然编译后会丢弃mapper.xml文件-->
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.xml</include>
<include>**/*.properties</include>
</includes>
</resource>
<directory>src/main/resources</directory>
</resources>
</build>
2. 创建Account表
create table t_account(
id int primary key auto_increment,
username varchar(11),
password varchar(11),
age int
);
3. 创建实体类Account
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Account {
private long id;
private String username;
private String password;
private int age;
}
4. 创建IAccountDao接口
接口中每一个方法都代表一个SQL语句,SQL语句写在mapper文件中
public interface IAccountDao {
//查询一个用户
Account selectAccountById(Integer id);
5. 创建SQL映射文件
建议不同表的操作放在不同的mapper文件中
在接口同一目录下,创建与接口同名的文件
IAccountDao.xml
,来编写SQL语句
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--namespace命名空间:推荐使用dao接口的全限定名称-->
<mapper namespace="dao.IAccountDao">
<!--id是要执行SQL语句的唯一标识,推荐使用dao接口中的方法名称
resultType:告诉mybatis,将查询到的数据赋值给哪个类的对象,使用实体类的全限定名称
#{accountId}:占位符,表示从java程序中传来的数据
-->
<select id="selectAccountById" resultType="domain.Account">
select * from t_account where id=#{accountId}
</select>
</mapper>
6. 创建主配置文件
这里连接数据库的url的参数的大小写敏感 &characterEncoding=utf-8
mybatis.xml包含了对 MyBatis 系统的核心设置,包括获取数据库连接实例的数据源(DataSource)以及决定事务作用域和控制方式的事务管理器(TransactionManager)
<!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">
<environment id="development">
<!--事务管理-->
<transactionManager type="JDBC"/>
<!--配置数据源:创建Connection对象-->
<dataSource type="POOLED">
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/eesy?useUnicode=true&characterEncoding=utf-8"/>
<property name="username" value="ggbond"/>
<property name="password" value="xxxx"/>
</dataSource>
</environment>
</environments>
<!--指定mapper文件的位置,进而找到SQL语句-->
<mappers>
<!--resource属性从类路径target/classes开始-->
<mapper resource="dao/IAccountDao.xml"></mapper>
</mappers>
</configuration>
7. 测试执行
public class MyTest {
@Test
public void testSelectAccountById() throws IOException {
//1.定义mybatis主配置文件的位置,从类路径开始的相对路径
String config="mybatis.xml";
//2.使用mybatis框架的工具类Resources来读取主配置文件为流对象
InputStream is = Resources.getResourceAsStream(config);
//3.使用SqlSessionFactoryBuilder创建SqlSessionFactory对象
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(is);
//4.获取sqlSession对象
SqlSession sqlSession = factory.openSession();
//5.指定要执行的SQL语句 = namespace + "." + id
String sqlId="dao.IAccountDao"+"."+"selectAccountById";
//6.通过sqlSession的方法,执行SQL语句
Account account=sqlSession.selectOne(sqlId,2);
System.out.println(account);
//7.关闭sqlSession对象
sqlSession.close();
}
注意
1. 占位符 #{}
如果传给mybatis的是一个对象,那么使用
#{属性名}
占位符赋值,mybatis通过get方法来获取值
2. 事务
mybatis执行SQL语句默认是手动提交事务模式:在做insert、update、delete后需要手动提交事务
sqlSession.commit();
mybatis相关对象
1. Resources
作用:读取类路径中的主配置文件为流对象
//1.定义mybatis主配置文件的位置,从类路径开始的相对路径
String config="mybatis.xml";
//2.使用mybatis框架的工具类Resources来读取主配置文件为流对象
InputStream is = Resources.getResourceAsStream(config);
2. SqlSessionFactoryBuilder
作用:创建SqlSessionFactory对象
//3.使用SqlSessionFactoryBuilder创建SqlSessionFactory对象
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(is);
3. SqlSessionFactory
SqlSessionFactory是重量级对象:创建此对象需要使用更多的资源和时间。在项目中有一个即可(在工具类中放在静态代码块内)
作用:是SqlSession的工厂,就是创建SqlSession对象。
方法:
-
:获取一个默认的SqlSession对象, 默认是需要手工提交事务的。openSession()
-
: boolean参数表示是否自动提交事务。openSession(boolean)
true: 创建一个自动提交事务的SqlSession
false: 等同于没有参数的openSession
4. 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()
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<!--设置日志-->
<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
<!--配置mybatis的运行环境。可以配置多个环境,选择其中一个环境-->
<environments default="development">
<environment id="development">
<!--事务管理-->
<transactionManager type="JDBC"/>
<!--配置数据源:创建Connection对象-->
<dataSource type="POOLED">
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/eesy?useUnicode=true&characterEncoding=utf-8"/>
<property name="username" value="ggbond"/>
<property name="password" value="xxxx"/>
</dataSource>
</environment>
</environments>
<!--指定mapper文件的位置,进而找到SQL语句-->
<mappers>
<!--resource属性从类路径target/classes开始-->
<mapper resource="dao/IAccountDao.xml"></mapper>
</mappers>
使用模板和工具类简化开发
模板
mybatis-mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="dao接口的全限定名称">
<!--使用insert、update、delete、select标签写SQL -->
mybatis-config
工具类
/**
* 工具类,创建SqlSession对象
*/
public class MyBatisUtil {
private static SqlSessionFactory factory =null;
static{
//1.定义mybatis主配置文件的位置,文件的类路径
try {
//2.使用mybatis框架的工具类Resources来读取主配置文件为流对象
InputStream is = Resources.getResourceAsStream(config);
//3.使用SqlSessionFactoryBuilder创建SqlSessionFactory对象
factory = new SqlSessionFactoryBuilder().build(is);
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 获取SqlSession对象的方法
* @return
*/
public static SqlSession getSqlSession(){
SqlSession sqlSession=null;
if (factory!=null){
sqlSession=factory.openSession();
return sqlSession;
使用:
@Test
public void testUtil() throws IOException {
//1.获取SqlSession对象
SqlSession sqlSession = MyBatisUtil.getSqlSession();
//2.指定要执行的SQL语句
String sqlId="dao.IAccountDao"+"."+"insertAccount";
//3.通过sqlSession的方法,执行SQL语句
Account account = new Account(7,"琪琪","qiqi",20);
int i = sqlSession.insert(sqlId,account);
System.out.println(i);
//4.提交事务
sqlSession.commit();
//5.关闭sqlSession对象
sqlSession.close();
Mybatis的mapper代理
使用传统开发方式
需要创建接口实现类,实现接口方法
public class AccountDaoImpl implements IAccountDao {
@Override
public Account selectAccountById(Integer id) {
String sqlId="dao.IAccountDao"+"."+"selectAccountById";
Account account = sqlSession.selectOne(sqlId, id);
//4.关闭sqlSession对象
return account;
public int insertAccount(Account account) {
return i;
public List<Account> selectAccounts() {
String sqlId="dao.IAccountDao"+"."+"selectAccounts";
List<Account> accounts = sqlSession.selectList(sqlId);
return accounts;
传统开发方式的分析
在前面例子中自定义 Dao 接口实现类时发现一个问题:Dao 的实现类其实并没有干什么实质性的工作,它仅仅就是通过 SqlSession 的相关 API 定位到映射文件 mapper 中相应 id 的 SQL 语句,真正对 DB 进行操作的工作其实是由框架通过 mapper 中的 SQL 完成的。所以,MyBatis 框架就抛开了 Dao 的实现类,直接定位到映射文件mapper 中的相应 SQL 语句,对 DB 进行操作。这种对 Dao 的实现方式称为Mapper 的动态代理方式。
Mapper 代理实现数据库操作(重点)
Mapper 动态代理方式无需程序员实现 Dao 接口。接口是由 MyBatis 结合映射文件自动生成的动态代理实现的。
- 需要dao接口和对应的mapper文件,不需要接口的实现类mapper文件的要求:
-
- mapper文件中的namespace是dao接口的全限定名称
- mapper文件中 标签的id是dao接口方法名称
- 以上操作绑定了mapper文件是接口的实现类
- 只需调用 SqlSession 的
方法,即可获取指定接口的实现类对象。该方法的参数为指定 Dao 接口类的 class 值。getMapper()
//2.获取接口的代理对象(将接口和配置文件绑定)
IAccountDao dao = sqlSession.getMapper(IAccountDao.class);
Account account = dao.selectAccountById(1);
System.out.println(account);
映射文件
mapper.xml
1. SQL语句中参数的传递
通过java代码将参数传入到mapper文件中的sql语句。 参数主要是指dao接口方法的形参
parameterType(可选)
- parameterType是标签属性,表示接口中方法参数的类型, 属性值是类型的全限定名或别名。
- 这个属性是可选的,因为 MyBatis 可以推断出具体传入语句的参数,默认值为未设置(unset)。
- 接口中方法的参数从 java 代码传入到 mapper 文件的 sql 语句。
第一个用法: 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>
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}
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}
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;
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 id="selectByQueryParam" resultType="com.bjpowernode.domain.Student">
select id,name,email,age from student where name=#{p1} or age=#{p2}
<!--负责的语法格式: #{属性名,javaType=java类型的全限定名称,jdbcType=mybatis中定义列的数据类型}
javaType, jdbcType 的类型 MyBatis 可以检测出来,一般不需要设置。常用格式 #{ property }-->
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}
2. #和$的区别
# 占位符
语法: #{字符}
mybatis处理#{} 使用jdbc对象是 PrepareStatment对象
resultType="com.bjpowernode.domain.Student">
select id,name,email,age from student where id=#{studentId}
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) #{} 常常作为 列值使用的, 位于等号的右侧, #{}位置的值和数据类型有关的。
$ 占位符
语法 : ${字符}
mybatis执行${}占位符的sql语句,使用Statement对象
select id,name,email,age from student where id=${studentId}
</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注入的风险。 有代码安全的问题
- ${} 数据是原样使用的(传什么就用什么), 不会区分数据类型。
4)${} 常用作 表名或者列名, 在能保证数据安全的情况下使用 ${}
3. 输出结果的封装
输出结果的封装:mybatis执行SQL语句,将结果集转为java对象
1. resultType
- 在执行select时使用, 作为<select>标签的属性出现的。
- 表示查询结果的类型,mysql执行sql语句,得到java对象的类型。值有两种写法:
-
- java类的全限定名称
- 使用类的别名
对象类型
resultType属性现在使用java类型的全限定名称,表示mybatis执行sql,把ResultSet中的数据转为Student类型的对象
# dao接口方法
Student selectById(Integer id);
# mapper文件
<select id="selectById" resultType="com.bjpowernode.domain.Student">
mybatis会执行以下操作:
- 调用com.bjpowernode.domain.Student的无参数构造方法,创建对象
Student student = new Student(); //使用反射创建对象
- 同名的列赋值给同名的属性
student.setId( rs.getInt("id"));
student.setName(rs.getString("name"));
- 得到java对象, 如果dao接口返回值是List集合, mybatis把student对象放入到List集合
简单类型
基本类型和String
long countStudent();
# mapper文件,执行sql语句,得到是一个值(一行一列)
<select id="countStudent" resultType="java.lang.Long">
select count(*) from student
Map
mybatis执行sql,把ResultSet转为map:列名为key,列值为value
注意:如果sql执行得到一行,是可以转为map的;但多于一行会报错,无法转换为map
//查询结果返回是一个Map
Map<Object,Object> selectMap(@Param("stuid") Integer id);
<select id="selectMap" resultType="java.util.HashMap">
select id,name,email from student where id != #{stuid}
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}
3. 列名和java对象属性名称不一样的解决方式
- 使用resultMap: 自定义列名和属性名称对应关系
- 使用resultType: 使用列别名(as),让别名和java对象属性名称一样
4. 模糊查询 like
类似于一个基本参数吧
dao接口方法
List<Student> selectLikeOne(@Param("name") String name);
mapper
<select id="selectLikeOne" resultType="com.bjpowernode.domain.Student">
select * from student where name like #{name}
执行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));
5. 自定义类的别名
mybatis提供的对java类的全限定名称进行自定义别名
自定义别名的步骤:
1)在mybatis主配置文件,使用 typeAliases标签声明别名
<typeAliases>
<!--第一种语法格式
type:java类型的全限定名称(自定义类型)
alias:自定义别名
-->
<typeAlias type="com.bjpowernode.domain.Student" alias="stu" />
</typeAliases>
2)在mapper文件中, resultType="别名"
resultType="别名"
<select id="selectById" parameterType="integer" resultType="stu">
全局配置文件
config.xml
mybatis配置文件两大类:
- mybatis主配置文件,提供mybatis全局设置的。包含的内容 日志, 数据源,mapper文件位置
- mapper文件: 写sql语句的。 一个表一个mapper文件
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>
2. typeAliases 别名标签
设置别名
<typeAliases>
<!--第一种语法格式
type:java类型的全限定名称(自定义类型)
alias:自定义别名
优点: 别名可以自定义
缺点: 每个类型必须单独定义
-->
<typeAlias type="com.bjpowernode.domain.Student" alias="stu" />
<typeAlias type="com.bjpowernode.vo.QueryParam" alias="qp" />
<!--第二种方式
name:包名, mybatis会把这个包中所有类名作为别名(不用区分大小写)
优点:使用方便,一次给多个类定义别名
缺点: 别名不能自定义,必须是类名。
<package name="com.bjpowernode.domain" />
<package name="com.bjpowernode.vo" />
</typeAliases>
3. environments 环境标签
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">
<!--配置数据源: 创建Connection对象。-->
<!--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"/>
<!-- 项目上线后使用的数据库 -->
<environment id="online">
<property name="username" value="admin"/>
<property name="password" value="123456"/>
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.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/springdb?useUnicode=true&characterEncoding=utf-8
jdbc.username=root
jdbc.password=123
mybatis主配置文件
<!--使用外部属性配置文件
resource:指定类路径下的某个属性配置文件
<properties resource="jdbc.properties" />
<environments default="development">
<property name="driver" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</environment>
</environments>
5. 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>
动态SQL
动态 SQL,通过 MyBatis 提供的各种标签对条件作出判断以实现动态拼接SQL 语句。这里的条件判断使用的表达式为 OGNL 表达式。常用的动态 SQL标签有<if>、<where>、<choose/>、<foreach>等。MyBatis 的动态 SQL 语句,与 JSTL 中的语句非常相似。
动态 SQL,主要用于解决查询条件不确定的情况:在程序运行期间,根据用户提交的查询条件进行查询。提交的查询条件不同,执行的 SQL 语句不同。若将每种可能的情况均逐一列出,对所有条件进行排列组合,将会出现大量的SQL 语句。此时,可使用动态 SQL 来解决这样的问题。动态SQL的dao接口方法参数是对象
比较符号转换为实体符
在 mapper 的动态 SQL 中若出现大于号(>)、小于号(<)、大于等于号(>=),小于等于号(<=)等符号,最好将其转换为实体符号。否则,XML 可能会出现解析出错问题。
&:
&
1. if 标签
语法:
-
<if test="条件">sql语句</if>
- test使用对象的属性值作为条件
<!--在mapper文件中-->
<select id="selectStudent" resultType="com.bjpwoernode.domain.Student">
select *from student
<if test="条件">
sql语句
</if>
示例:
List<Student> selectIf(Student student);
<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}
2. where 标签
<if/>标签的中存在一个比较麻烦的地方:需要在 where 后手工添加 1=1的子句。因为,若 where 后的所有<if/>条件均为 false,而 where 后若又没有 1=1 子句,则 SQL 中就会只剩下一个空的 where,SQL 出错。所以,在where 后,需要添加永为真子句 1=1,以防止这种情况的发生。但当数据量很大时,会严重影响查询效率。
使用<where/>标签,在有查询条件为true时,可以自动添加上 where 子句;没有查询条件为true时,不会添加 where 子句。需要注意的是,第一个<if/>标签中的SQL 片断,可以不包含 and。不过,写上 and 也不错,系统会将多出的 and去掉。但其它<if/>中 SQL 片断的 and,必须要求写上。否则 SQL 语句将拼接出错
<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">
<where>
<if test="name !=null and name!=''">
or name = #{name}
</if>
<if test="age >0">
or age < #{age}
</where>
3. foreach 循环标签
使用foreach可以循环数组,list集合, 一般使用在in语句中
标签属性:
-
collection:表示循环的对象是数组,还是list集合。
如果dao接口方法的形参是数组,collection="array"
如果dao接口形参是List,collection="list"
- open:循环开始时的字符。 sql.append("(");
- close:循环结束时字符。 sql.append(")");
- item:集合成员, 自定义的变量。 Integer item = idlist.get(i);
- separator:集合成员之间的分隔符。 sql.append(",");
- #{item 的值}:获取集合成员的值。
< foreach collection="集合类型" open="开始字符" close="结束字符" item="集合成员" separator="集合成员之间的分隔符">
#{item 的值}
</ foreach>
循环简单类型的List
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>
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">
<foreach collection="list" open="(" close=")" separator="," item="stu">
#{stu.id}
@Test
public void testSelectForeachTwo(){
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);
4. 代码片段
<sql/>标签用于定义 SQL 片断,以便其它 SQL 标签复用。而其它标签使用该 SQL 片断,需要使用<include/>子标签。该<sql/>标签可以定义 SQL 语句中的任何部分,所以<include/>子标签可以放在动态 SQL 的任何位置。
<!--定义代码片段-->
<sql id="selectStudent">
</sql>
<sql id="studentFieldList">
id,name,email
<include refid="selectStudent" />
where id=-1
or name = #{name}
</select>
<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}
</where>
分页助手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>
3.在select语句之前,调用
PageHelper.startPage(页码,每页大小)
。紧跟在这个方法后的第一个 MyBatis 查询方法会被进行分页
public void testSelect() throws IOException {
//获取第 1 页,3 条内容(页码从1开始)
PageHelper.startPage(1,3);
List<Student> studentList = studentDao.selectStudents();
studentList.forEach( stu -> System.out.println(stu));
对比:
- 没有使用PageHelper
select * from student order by id
- 使用PageHelper
SELECT count(0) FROM student
select * from student order by id LIMIT ?