1、简介
2、入门
3、核心文件
4、映射文件
5、动态SQL
6、注解SQL
7、缓存
8、拓展
MyBatis框架的前身是iBatis,是Apache软件基金会的一个开源项目。2010年,这个项目有Apache Software Foundation迁移到了Google Code,并更名位MyBatis,2013年,项目迁移到了GitHub上。
如果你只是想要进行简单操作,那么按照以下步骤来,即可掌握mybatis的基本用法!
要使用 MyBatis, 只需将 mybatis-x.x.x.jar 文件置于类路径(classpath)中即可。
如果使用 Maven 来构建项目,则需将下面的依赖代码置于 pom.xml 文件中:
<dependency>
<groupId>org.mybatisgroupId>
<artifactId>mybatisartifactId>
<version>3.4.6version>
dependency>
导入依赖之后,需要编写一个核心配置文件(mybatis-config.xml),这个文件可以获取数据库连接,并且还可以控制mybatis在程序中的一些特性。核心配置文件基本结构如下:
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="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
dataSource>
environment>
environments>
<mappers>
<mapper resource="org/mybatis/example/BlogMapper.xml"/>
mappers>
configuration>
在核心配置文件中,将四个property标签的value值替换成对应驱动、地址、账号、密码即可,这一步操作可以连接上数据库!
mappers标签则是注册编写的接口对应的mapper.xml文件!
你需要编写一个工具类(MybatisUtils)用来获取连接
public class MybatisUtils {
private static SqlSessionFactory sqlSessionFactory;
static {
try {
String resource="mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
} catch (IOException e) {
e.printStackTrace();
}
}
public static SqlSession getSqlSession(){
//你可以在这里给openSession()传一个参数true,这样就无需手动提交了!
return sqlSessionFactory.openSession();
}
}
这个工具类可以方便的获取到数据库的连接,并且充分的利用到了一些属性的作用域,使其不会占用大量资源,让系统性能降低!
其中==String resource=“mybatis-config.xml”;==这里的"mybatis-config.xml"对应的是你的核心配置文件,如果你的核心配置文件并不是这个名称,请写你自己的核心配置文件名称!
getSqlSession()方法则是获取数据库的连接,它是一个静态的方法,所以可以使用类名去调用!
请按照你的实体类,去编写你的接口。如:
public interface StudentMapper {
int update(Map map);
List<Student> findAll(Map map);
List<Student> findAll2(Map map);
List<Student> findAll3(Map map);
}
这是一个学生类的接口,按照规范,它的后缀是Maaper,并且它有一个映射文件,这个文件的名称应该是StudentMapper.xml,请尽量保持接口和xml文件的名称一致,这样即便使用不同方法注册mapper.xml配置文件时,也不会出现错误,并且请将两者放到同一个包下
写好接口之后,你需要去创建接口的mapper.xml文件,结构如下:
DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.th.dao.StudentMapper">
<select id="findAll" resultType="com.th.pojo.Student">
select * from student
select>
mapper>
StudentMapper
,你必须使用完全限定名!使用单元测试:
@Test
public void t5() {
SqlSession sqlSession = MybatisUtils.getSqlSession(); //通过工具类获取连接
//获取接口的映射,相当于是在实例化对象
StudentMapper studentMapper = sqlSession.getMapper(StudentMapper.class);
Map map=new HashMap();
map.put("tid",1);
List<Student> list = studentMapper.findAll2(map);
for (Student student : list) {
System.out.println(student);
}
sqlSession.close();//关闭连接
}
如果是增删改查,在关闭连接前还需要进行提交事务
sqlSession.commit(); //提交事务
configuration(配置)
常用配置仅需掌握这几个就可以,如有其他需求,请到官方文档查看其余配置!
请注意:配置文件中的这些标签是有一个顺序的,请按照顺序来,否则会报错!
你可以将核心xml文件的四个连接数据库的字段放到外部配置文件中。
<properties resource="db.properties"/>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
dataSource>
environment>
environments>
//这是数据库配置文件,如果mysql版本在8.0以上,还需要加上时区
driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/mybatis?useSSL=true&useUnicode=true&character=UTF-8
username=root
password=123
当然,你也可以将账号密码放入核心xml文件中,如下:
<properties resource="org/mybatis/example/config.properties">
<property name="username" value="root"/>
<property name="password" value="123"/>
properties>
以上例子便是将账号密码放入xml核心配置文件中,请注意如果你在外部配置文件中也写了账号密码的属性,那么外部的配置文件会覆盖掉核心xml文件的配置!优先级:外部配置文件>核心配置文件
常用的设置如下:
设置名 | 描述 | 有效值 | 默认值 |
---|---|---|---|
cacheEnabled | 全局性地开启或关闭所有映射器配置文件中已配置的任何缓存。 | true | false | true |
logImpl | 指定 MyBatis 所用日志的具体实现,未指定时将自动查找。 | SLF4J | LOG4J(deprecated since 3.5.9) | LOG4J2 | JDK_LOGGING | COMMONS_LOGGING | STDOUT_LOGGING | NO_LOGGING | 未设置 |
mapUnderscoreToCamelCase | 是否开启驼峰命名自动映射,即从经典数据库列名 A_COLUMN 映射到经典 Java 属性名 aColumn。 | true | false | False |
autoMappingBehavior | 指定 MyBatis 应如何自动映射列到字段或属性。 NONE 表示关闭自动映射;PARTIAL 只会自动映射没有定义嵌套结果映射的字段。 FULL 会自动映射任何复杂的结果集(无论是否嵌套)。 | NONE, PARTIAL, FULL | PARTIAL |
一个配置完整的 settings 元素的示例如下:
<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>
在编写mapper.xml文件时,你会发现resultType返回值写上全限定名是一件很麻烦的事情,这里,你可以给它取一个类型别名!
类型别名可为 Java 类型设置一个缩写名字。 它仅用于 XML 配置,意在降低冗余的全限定类名书写。例如:
<typeAliases>
<typeAlias type="com.th.pojo.Student" alias="student"/>
<typeAlias type="com.th.pojo.Teacher" alias="teacher"/>
typeAliases>
这样配置,你就可以把全限定名改成你自己定义的类别名称了!
也可以指定一个包名,MyBatis 会在包名下面搜索需要的 Java Bean,比如:
<typeAliases>
<package name="com.th.pojo"/>
typeAliases>
每一个在包 com.th.pojo
中的 Java Bean,在没有注解的情况下,会使用 Bean 的首字母小写的非限定类名来作为它的别名。
比如 com.th.pojo.Teacher
的别名为 teacher
;若有注解,则别名为其注解值。
注解别名是一个很好用的东西,你也许可以尝试使用它!
@Alias("author")
public class Author {
...
}
还有一些为常见的 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 |
MyBatis 可以配置成适应多种环境,这种机制有助于将 SQL 映射应用于多种数据库之中, 现实情况下有多种理由需要这么做。例如,开发、测试和生产环境需要有不同的配置;或者想在具有相同 Schema 的多个生产数据库中使用相同的 SQL 映射。还有许多类似的使用场景。
不过要记住:尽管可以配置多个环境,但每个 SqlSessionFactory 实例只能选择一种环境!
你可以使用多种方法来注册MyBatis映射器!
推荐使用!
<mappers>
<mapper resource="org/mybatis/builder/AuthorMapper.xml"/>
<mapper resource="org/mybatis/builder/BlogMapper.xml"/>
<mapper resource="org/mybatis/builder/PostMapper.xml"/>
mappers>
使用注解增删改查时,需要使用以下方法,但这种注册方法需要保证接口和mapper.xml文件名保持一致,否则便找不到!
<mappers>
<mapper class="org.mybatis.builder.AuthorMapper"/>
<mapper class="org.mybatis.builder.BlogMapper"/>
<mapper class="org.mybatis.builder.PostMapper"/>
mappers>
这种方法可以将包内的映射器全部注册!
<mappers>
<package name="org.mybatis.builder"/>
mappers>
以下方法并不推荐使用!
<mappers>
<mapper url="file:///var/mappers/AuthorMapper.xml"/>
<mapper url="file:///var/mappers/BlogMapper.xml"/>
<mapper url="file:///var/mappers/PostMapper.xml"/>
mappers>
SQL 映射文件只有很少的几个顶级元素(按照应被定义的顺序列出):
cache
– 该命名空间的缓存配置。cache-ref
– 引用其它命名空间的缓存配置。resultMap
– 描述查询结果集的字段和实体类属性的对应关系,是最复杂也是最强大的元素。sql
– 可被其它语句引用的可重用语句块。insert
– 映射插入语句。update
– 映射更新语句。delete
– 映射删除语句。select
– 映射查询语句。如果仅传入一个基本类型或其他包装类型等,可以忽略掉parameterType属性,并且参数名也可以随意定义,如下:
接口:
public interface ThFinanceMapper {
ThFinance findId(String id);
}
映射文件:
DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.th.mapper.ThFinanceMapper">
<select id="findId" resultType="ThFinance">
select * from th_finance where finance_id=#{aabb}
select>
mapper>
单元测试:
@Test
public void t1(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
ThFinanceMapper mapper = sqlSession.getMapper(ThFinanceMapper.class);
ThFinance finance = mapper.findId("2022/2/24");
System.out.println(finance.toString());
}
结果:
通常我们会使用==@Param()==注解,绑定参数,这样符合规范,关于@Param()将在下文讲解。
可以将java的对象作为参数传入,但是必须加上属性parameterType,并且参数名也必须与对象中的属性一致,如下:
实体类属性:
private String financeId; //这是实体类的属性名
private String userId;
private java.sql.Timestamp financeCreateDate;
private String financePayName;
private double financePayPrice;
接口:
public interface ThFinanceMapper {
ThFinance findThFinance(ThFinance thFinance);
}
映射文件:
DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.th.mapper.ThFinanceMapper">
<select id="findThFinance" resultType="ThFinance" parameterType="ThFinance">
select * from th_finance
where finance_id=#{financeId} and finance_payName=#{financePayName}
select>
mapper>
单元测试:
@Test
public void t2(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
ThFinanceMapper mapper = sqlSession.getMapper(ThFinanceMapper.class);
//对象作为参数
ThFinance thFinance = new ThFinance();
thFinance.setFinanceId("2022/2/24");
thFinance.setFinancePayName("早餐");
ThFinance finance = mapper.findThFinance(thFinance);
System.out.println(finance.toString());
}
结果:
如果传入的参数很零散,并不是一个对象,那么我们可以将它们放到map集合中,把map作为参数,同样的parameterType也是必不可少的,但是需要注意的是,参数名称是map的key值:
接口:
public interface ThFinanceMapper {
ThFinance mapThFinance(Map map);
}
映射文件:
DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.th.mapper.ThFinanceMapper">
<select id="mapThFinance" resultType="ThFinance" parameterType="map">
select * from th_finance where finance_id=#{id} and finance_payName=#{name}
select>
mapper>
单元测试:
@Test
public void t3(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
ThFinanceMapper mapper = sqlSession.getMapper(ThFinanceMapper.class);
//参数名称是map的key
Map map = new HashMap();
map.put("id","2022/2/24");
map.put("name","早餐");
ThFinance finance = mapper.mapThFinance(map);
System.out.println(finance.toString());
}
结果:
@Param是MyBatis所提供的(org.apache.ibatis.annotations.Param),作为Dao层的注解,作用是用于传递参数,从而可以与SQL中的的字段名相对应,一般在2=<参数数<=5时使用最佳。
使用Map传参的缺点就在于可读性差,每次必须阅读他的键,才能明白其中的作用,并且不能限定其传递的数据类型,下面是使用@Param的情况:
接口:
public interface ThFinanceMapper {
//在这里,@Param("id")与参数financeId绑定,@Param("name")与参数payName绑定
ThFinance paramThFinance(@Param("id") String financeId,@Param("name") String payName);
}
映射文件:
DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.th.mapper.ThFinanceMapper">
<select id="paramThFinance" resultType="ThFinance">
select * from th_finance where finance_id=#{id} and finance_payName=#{name}
select>
mapper>
单元测试:
@Test
public void t4(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
ThFinanceMapper mapper = sqlSession.getMapper(ThFinanceMapper.class);
ThFinance finance = mapper.paramThFinance("2022/2/24","早餐");
System.out.println(finance.toString());
}
结果:
除了以上几种,我们还可以根据下标传递参数,但是这种方法并不推荐使用,因为可读性要比使用map集合还要更差:
接口:
public interface ThFinanceMapper {
//注意,这里我们并没有使用@Param
ThFinance indexThFinance(String financeId,String payName);
}
映射文件:
DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.th.mapper.ThFinanceMapper">
<select id="indexThFinance" resultType="ThFinance">
select * from th_finance where finance_id=#{arg0} and finance_payName=#{arg1}
select>
mapper>
单元测试:
@Test
public void t5(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
ThFinanceMapper mapper = sqlSession.getMapper(ThFinanceMapper.class);
ThFinance finance = mapper.indexThFinance("2022/2/24","早餐");
System.out.println(finance.toString());
}
结果:
在一些复杂的查询之中,我们会遇到一个对象里面包含另一个对象,或者遇到一个对象里面包含一个集合,甚至更加复杂的结果,这个时候单凭简单的实体类对象是无法满足我们的要求的,所以我们需要进行结果集映射!
属性:
子元素:
属性:
子元素:
<select id="findALll" resultMap="studentTea">
SELECT s.`name` sname,s.age sage,t.`name` tname
from student s,teacher t
where s.tid=t.id
select>
<resultMap id="studentTea" type="student">
<result property="name" column="sname"/>
<result property="age" column="sage"/>
<association property="teacher" javaType="Teacher">
<result property="name" column="tname"/>
association>
resultMap>
<select id="findALll" resultMap="studentTea">
select * from student
select>
<resultMap id="studentTea" type="student">
<association property="teacher" column="tid" javaType="Teacher" select="getTeacher"/>
resultMap>
<select id="getTeacher" resultType="teacher">
select * from teacher where id=#{id}
select>
<select id="getTeacher" resultMap="TeacherStu">
select s.id sid,t.id tid,t.name tname,s.name sname,s.age sage from student s,teacher t
where s.tid=t.id and t.id=#{id}
select>
<resultMap id="TeacherStu" type="Teacher">
<result property="name" column="tname"/>
<result property="id" column="tid"/>
<collection property="students" ofType="student">
<result property="id" column="sid"/>
<result property="name" column="sname"/>
<result property="age" column="tid"/>
collection>
resultMap>
<select id="getTeacher" resultMap="TeacherStu">
select * from teacher where id=#{id}
select>
<resultMap id="TeacherStu" type="teacher">
<collection property="students" javaType="ArrayList" ofType="student" select="getTeacherStu" column="id"/>
resultMap>
<select id="getTeacherStu" resultType="student">
select * from student where tid=#{tid}
select>
在做结果集映射时,我们需要注意的是,如果使用了collection和association,那么自动映射将会失效,我们必须手动将全部的属性和查询结果的column关联起来,当然,我们也可以在核心配置文件中设置autoMappingBehavior的属性,将它设置为FULL!
<select id="findId" resultType="ThFinance">
select * from th_finance where finance_id=#{aabb}
select>
模糊查询:
<select id="indexThFinance" resultType="ThFinance">
select * from th_finance where finance_payName like "%"#{name}"%"
select>
<select id="likeThFinance" resultType="ThFinance">
select * from th_finance where finance_payName like concat('%',#{name},'%')
select>
分页:
属性 | 描述 |
---|---|
id |
在命名空间中唯一的标识符,可以被用来引用这条语句。 |
parameterType |
将会传入这条语句的参数的类全限定名或别名。这个属性是可选的,因为 MyBatis 可以通过类型处理器(TypeHandler)推断出具体传入语句的参数,默认值为未设置(unset)。 |
parameterMap | 用于引用外部 parameterMap 的属性,目前已被废弃。请使用行内参数映射和 parameterType 属性。 |
resultType |
期望从这条语句中返回结果的类全限定名或别名。 注意,如果返回的是集合,那应该设置为集合包含的类型,而不是集合本身的类型。 resultType 和 resultMap 之间只能同时使用一个。 |
resultMap |
对外部 resultMap 的命名引用。结果映射是 MyBatis 最强大的特性,如果你对其理解透彻,许多复杂的映射问题都能迎刃而解。 resultType 和 resultMap 之间只能同时使用一个。 |
flushCache |
将其设置为 true 后,只要语句被调用,都会导致本地缓存和二级缓存被清空,默认值:false。 |
useCache |
将其设置为 true 后,将会导致本条语句的结果被二级缓存缓存起来,默认值:对 select 元素为 true。 |
timeout |
这个设置是在抛出异常之前,驱动程序等待数据库返回请求结果的秒数。默认值为未设置(unset)(依赖数据库驱动)。 |
fetchSize |
这是一个给驱动的建议值,尝试让驱动程序每次批量返回的结果行数等于这个设置值。 默认值为未设置(unset)(依赖驱动)。 |
statementType |
可选 STATEMENT,PREPARED 或 CALLABLE。这会让 MyBatis 分别使用 Statement,PreparedStatement 或 CallableStatement,默认值:PREPARED。 |
resultSetType |
FORWARD_ONLY,SCROLL_SENSITIVE, SCROLL_INSENSITIVE 或 DEFAULT(等价于 unset) 中的一个,默认值为 unset (依赖数据库驱动)。 |
databaseId |
如果配置了数据库厂商标识(databaseIdProvider),MyBatis 会加载所有不带 databaseId 或匹配当前 databaseId 的语句;如果带和不带的语句都有,则不带的会被忽略。 |
resultOrdered |
这个设置仅针对嵌套结果 select 语句:如果为 true,将会假设包含了嵌套结果集或是分组,当返回一个主结果行时,就不会产生对前面结果集的引用。 这就使得在获取嵌套结果集的时候不至于内存不够用。默认值:false 。 |
resultSets |
这个设置仅适用于多结果集的情况。它将列出语句执行后返回的结果集并赋予每个结果集一个名称,多个名称之间以逗号分隔。 |
数据变更语句 insert,update 和 delete 的实现非常接近:
<insert id="insert" parameterType="com.th.pojo.Employee">
insert employee(emp_name,emp_desc) values (#{empName},#{empDesc});
insert>
<update id="update" parameterType="com.th.pojo.Employee">
update employee set emp_name=#{empName},emp_desc=#{empDesc} where id=#{id}
update>
<delete id="delete" parameterType="int">
delete from employee where id=#{id}
delete>
属性 | 描述 |
---|---|
id |
在命名空间中唯一的标识符,可以被用来引用这条语句。 |
parameterType |
将会传入这条语句的参数的类全限定名或别名。这个属性是可选的,因为 MyBatis 可以通过类型处理器(TypeHandler)推断出具体传入语句的参数,默认值为未设置(unset)。 |
parameterMap |
用于引用外部 parameterMap 的属性,目前已被废弃。请使用行内参数映射和 parameterType 属性。 |
flushCache |
将其设置为 true 后,只要语句被调用,都会导致本地缓存和二级缓存被清空,默认值:(对 insert、update 和 delete 语句)true。 |
timeout |
这个设置是在抛出异常之前,驱动程序等待数据库返回请求结果的秒数。默认值为未设置(unset)(依赖数据库驱动)。 |
statementType |
可选 STATEMENT,PREPARED 或 CALLABLE。这会让 MyBatis 分别使用 Statement,PreparedStatement 或 CallableStatement,默认值:PREPARED。 |
useGeneratedKeys |
(仅适用于 insert 和 update)这会令 MyBatis 使用 JDBC 的 getGeneratedKeys 方法来取出由数据库内部生成的主键(比如:像 MySQL 和 SQL Server 这样的关系型数据库管理系统的自动递增字段),默认值:false。 |
keyProperty |
(仅适用于 insert 和 update)指定能够唯一识别对象的属性,MyBatis 会使用 getGeneratedKeys 的返回值或 insert 语句的 selectKey 子元素设置它的值,默认值:未设置(unset )。如果生成列不止一个,可以用逗号分隔多个属性名称。 |
keyColumn |
(仅适用于 insert 和 update)设置生成键值在表中的列名,在某些数据库(像 PostgreSQL)中,当主键列不是表中的第一列的时候,是必须设置的。如果生成列不止一个,可以用逗号分隔多个属性名称。 |
databaseId |
如果配置了数据库厂商标识(databaseIdProvider),MyBatis 会加载所有不带 databaseId 或匹配当前 databaseId 的语句;如果带和不带的语句都有,则不带的会被忽略。 |
可以将某些重用的SQL语句提取出来,使用include标签去引用,实现代码的复用:
<sql id="th"> where valid = #{id} sql>
<select id = 'findId'>select * from user <include refid = 'th'>include>
注意:
1、#
将传入的数据都当成一个字符串,会对自动传入的数据加一个双引号。如:
where id=#{id},如果传入的值是1,那么解析成sql时的值为where id=“1”
如果传入的值是abc,则解析成的sql为where id=“abc”。
2、$
将传入的数据直接显示生成在sql中。
如:where id=${id},如果传入的值是1,那么解析成sql时的值为where username=1;
如果传入的值是;drop table user;,则解析成的sql为:
select * from student where id=1;drop table student;
3、#
方式能够很大程序防止sql注入,$
方式无法防止sql注入。
4、$
方式一般用于传入数据库对象,比如表名。
5、一般能用#
的就不要使用$
,若不得不使用,则要做好前期校验工作,防止sql注入攻击。
6、在mybatis中,涉及到动态表名和列名时,只能使用${xxx}
这样的参数形式。所以这样的参数需要我们在代码中手工进行处理来防止注入。
动态SQL与JSTL标签类似,它根据不同的条件生成不同的SQL语句。
<select id="findAll" parameterType="map" resultType="student">
select * from student where 1=1
<if test="name!=null">
and name=#{name}
if>
<if test="tid!=null">
and tid=#{tid}
if>
select>
MyBatis 提供了 choose 元素,它有点像 Java 中的 switch 语句。
<select id="findAll2" parameterType="map" resultType="student">
select * from student
<where>
<choose>
<when test="tid!=null">
tid=#{tid}
when>
<when test="name!=null">
name=#{name}
when>
choose>
where>
select>
trim相当于where和set的父级,通过自定义 trim 元素来定制 where 元素的功能。比如,和 where 元素等价的自定义 trim 元素为:
<trim prefix="WHERE" prefixOverrides="AND |OR ">
...
trim>
prefixOverrides 属性会忽略指定符号分隔的文本序列(注意此例中的空格是必要的)。上述例子会移除所有 prefixOverrides 属性中指定的内容,并且插入 prefix 属性中指定的内容。
where 元素只会在子元素返回任何内容的情况下才插入 “WHERE” 子句。而且,若子句的开头为 “AND” 或 “OR”,where 元素也会将它们去除。
如果 where 元素与你期望的不太一样,你也可以通过自定义 trim 元素来定制 where 元素的功能。
<select id="findAll" parameterType="map" resultType="student">
select * from student
<where>
<if test="name!=null">
and name=#{name}
if>
<if test="tid!=null">
and tid=#{tid}
if>
where>
select>
用于动态更新语句的类似解决方案叫做 set。set 元素可以用于动态包含需要更新的列,忽略其它不更新的列。
<update id="updateAuthorIfNecessary">
update Author
<set>
<if test="username != null">username=#{username},if>
<if test="password != null">password=#{password},if>
<if test="email != null">email=#{email},if>
<if test="bio != null">bio=#{bio}if>
set>
where id=#{id}
update>
<update id="update" parameterType="map">
update student
<set>
tid=#{tid},
set>
<where>
id=#{id}
where>
update>
以上例子中,set 元素会动态地在行首插入 SET 关键字,并会删掉额外的逗号(这些逗号是在使用条件语句给列赋值时引入的),比如最后一个要修改的参数为空,那么SQL语句就会多出一个逗号,而set元素会自动删除这个多余的逗号,所以尽情的去写逗号,不需要有所顾虑。
动态 SQL 的另一个常见使用场景是对集合进行遍历(尤其是在构建 IN 条件语句的时候)。
<select id="selectPostIn" resultType="domain.blog.Post">
SELECT *
FROM POST P
<where>
<foreach item="item" index="index" collection="list"
open="ID in (" separator="," close=")" nullable="true">
#{item}
foreach>
where>
select>
以上例子实际SQL语句为:SELECT * FROM POST P where 1=1 and ID in (#{item},#{item},#{item});
in条件实际可能会更多,但通过遍历集合,便可将所有条件全部加入到in条件中!
<select id="findAll3" parameterType="map" resultType="student">
select * from student
<where>
<foreach collection="ids" item="id" open="and (" close=")" separator="or">
id=#{id}
foreach>
where>
select>
以上例子实际SQL语句为:select * from student where (id=#{id} or id=#{id} or id=#{id})
foreach 元素的功能非常强大,它允许你指定一个集合,声明可以在元素体内使用的集合项(item)和索引(index)变量。它也允许你指定开头与结尾的字符串以及集合项迭代之间的分隔符。这个元素也不会错误地添加多余的分隔符
提示
你可以将任何可迭代对象(如 List、Set 等)、Map 对象或者数组对象作为集合参数传递给 foreach。当使用可迭代对象或者数组时,index 是当前迭代的序号,item 的值是本次迭代获取到的元素。当使用 Map 对象(或者 Map.Entry 对象的集合)时,index 是键,item 是值。
<select id="selectUser" resultType="user">
<bind name="pattern" value="'%' + _parameter.getTitle() + '%'" />
SELECT * FROM User
WHERE name LIKE #{pattern}
select>
动态SQL就是拼接SQL语句,只要保证SQL的正确性,按照SQL的语法,去进行各种组合就可以了!
可以先在mysql中写出完整的SQL语句,然后再去拆分,修改成为动态SQL!
使用注解来映射简单语句会使代码显得更加简洁,但对于稍微复杂一点的语句,Java 注解不仅力不从心,还会让你本就复杂的 SQL 语句更加混乱不堪。 因此,如果你需要做一些很复杂的操作,最好用 XML 来映射语句。
使用注解CRUD,是没有mapper.xml文件的,所以你需要使用全限定类名的方式去注册!
@Select("select * from teacher")
List<Teacher> findAll();
如果有参数,那么需要加上@param(),将参数传递过去!
//请注意,@param("id")中的id与#{id}中的id需要相同
@Select("select * from employee where id=#{id}")
Employee findId(@Param("id") int id);
当你的条件是对象是,则自动匹配,如下:
//你需要将对象中的字段名与#{}中的值相对应,这样才能匹配的上
@Insert("insert employee values(null,#{name},#{desc})")
int insert(Employee employee);
还有一些其他的注解,他们并不是增删改,仅作为一个补充,了解即可!
@SuppressWarnings("all") //抑制警告
另外,在企业中,常用UUID生成的随机数来作为id主键:
UUID.randomUUID().toString().replaceAll("-","");//replaceAll("-","")会移除掉中间的“-”
后续会用到Redis缓存,Mybatis缓存了解即可
什么是缓存?
为什么使用缓存?
MyBatis 内置了一个强大的事务性查询缓存机制,它可以非常方便地配置和定制。
MyBatis 有两级缓存:一级缓存和二级缓存
默认情况下,只启用了本地的会话缓存,它仅仅对一个会话中的数据进行缓存。(SqlSession级别)
要启用全局的二级缓存,只需要在你的 SQL 映射文件中添加一行:
<cache/>
这个简单语句的效果如下:
可用的清除策略有:
FIFO
– 先进先出:按对象进入缓存的顺序来移除它们。SOFT
– 软引用:基于垃圾回收器状态和软引用规则移除对象。WEAK
– 弱引用:更积极地基于垃圾收集器状态和弱引用规则移除对象。测试
@Test
public void t4() {
SqlSession sqlSession = MybatisUtils.getSqlSession();
StudentMapper studentMapper = sqlSession.getMapper(StudentMapper.class);
Map map=new HashMap();
map.put("name","张三");
List<Student> list = studentMapper.findAll(map);
for (Student student : list) {
System.out.println(student);
}
System.out.println("===========================================");
List<Student> list2 = studentMapper.findAll(map);
for (Student student : list2) {
System.out.println(student);
}
sqlSession.close();
}
缓存失效情况
增删改操作,可能会改变原来的数据,所以必定会刷新缓存
使用不同的Mapper.xml也会使缓存失效
手动清理缓存
sqlSession.clearCache();
步骤:
开启全局缓存
<setting name="cacheEnabled" value="true"/>
要在使用二级缓存的Mapper中开启
<cache/>
如果你没有序列化,那么就会报错: Cause: java.io.NotSerializableException: com.th.pojo.Student
你可以把它的详细参数写出来,这样即便你的实体类没有序列化,也仍旧可以运行
<cache
eviction="FIFO"
flushInterval="60000"
size="512"
readOnly="true"/>
注意
实体类最好序列化,序列化只需要实现Serializable接口即可
implements Serializable
反射的概念是由Smith在1982年首次提出的,主要是指程序可以访问、检测和修改其本身状态或行为的一种能力。
在 Java 环境中,反射机制允许程序在执行时获取某个类自身的定义信息,如属性和方法等,也可以实现动态创建类的对象、变更属性的内容或执行特定的方法的功能。从而使 Java 具有动态语言的特性,增强了程序的灵活性和可移植性。
Mybatis的反射是一个特别强大的模块。比如:
在mybaits中配置 useGeneratedKeys=“true” ,主键是自动生成,那么在==appVersionDao.insert(appVersion);==这一步后,Mybatis会自动生成主键并设置到appVersion里面,所以在同一个事务中,可以直接通过appVersion.getId获取到主键,而不需要额外查询一次!