历史:加拿大人Clinton Begin,2001年发布iBATIS,2004年提供给了Apache基金会,2010年改名为MyBatis
Github:https://github.com/mybatis/mybatis-3
文档:http://www.mybatis.org/mybatis-3/
概念
JDBC:Java DataBase Connectivity,JAVA的数据库连接API
DAO:Data Access Objects,包含数据访问功能的JAVA对象
POJO:Plain Ordinary Java Object,纯净普通的JAVA对象,即无继承父类无实现接口、不包含业务逻辑、含getter/setter的简单JAVA对象,用来映射数据库。
ORM:Object Relational Mapping,对象关系映射,即JAVA对象和数据库表的映射
ORM框架:提供ORM能力的框架,Hibernate和MyBatis都是ORM框架
术语
environment:目标数据库
resource:配置文件
namespace:SQL操作的汇总,即SQL操作interface
与Hibernate的对比
Hibernate根据映射关系自动生成SQL语句,MyBatis需要手写SQL语句。这样,使用MyBatis的工作量更大,但更具灵活性、可优化性
IDEA 配置 MyBatis
1、到官网或Maven中央仓库上查看可用版本,配置Maven依赖
<dependency>
<groupId>mysql</groupId> <!-- mysql需要在配置文件里配置时区,default-time-zone='+08:00' -->
<artifactId>mysql-connector-java</artifactId>
<version>6.0.6</version> <!-- 版本得跟mysql版本一致 -->
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.6</version>
</dependency>
2、给IDEA添加插件 MyBatisCodeHelper,Settings -> Plugins -> 搜索MyBatisCodeHelper
MyBatis应用的组成
1、MyBatis 主配置文件
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<properties resource="db.properties"></properties> <!-- 不用Maven也能引入properties文件 -->
<settings> <!-- 一些设置 -->
<setting name ="cacheEnabled" value="true"/>
<setting name="lazyLoadingEnabled" value="true"/> <!-- 是否开启延迟加载 -->
<!-- lazyLoadingEnabled为true时,aggressiveLazyLoading才有意义 -->
<setting name="aggressiveLazyLoading" value="true"/> <!-- 开启延迟加载的情况下,是否预先加载第二层 -->
</settings>
<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/db1" /> <!-- 指明仓库 -->
<property name="username" value="root" />
<property name="password" value="pass" />
</dataSource>
</environment>
</environments>
<mappers> <!-- 映射配置文件列表 -->
<mapper resource="hogenMapper.xml"></mapper>
</mappers>
</configuration>
``
2、作为 SQL参数的POJO
public class CityParams {
private String name;
private String id;
getter 和 setter
}
3、作为 SQL 结果的POJO
```java
public class City {
private Long id;
private String cityName ;
getter 和 setter
}
4、SQL操作接口
public interface CityMapper {
public City getCity(long id); // SQL操作函数
public List<City> findCityByBean(CityParams cityParams ); // 返回列表
public void insertCity(CityParams cityParams);
// 单参数,且该参数为非基本类型,则需要注解参数名,才能在 映射文件 中 引用该参数本身 ,否则只能引用它的成员
public List<City> findCityByAnnotation(@Param("cityIds") String[] cityIds) ;
}
5、MyBatis映射配置文件,与SQL操作接口 组成映射器
<?xml version="1.0" encoding="UTF-8"?>
<mapper namespace="com.maven.hogen.CityMapper"> <!-- SQL操作接口 -->
<cache/> <!-- 本映射器开启二级缓存 -->
<!-- 映射的双方分别为 SQL操作函数 和 SQL语句 -->
<select id="getCity" parameterType="long" resultType="com.maven.hogen.City"> <!-- SQL操作函数的入参、返回类型 -->
select id,name as cityName,countryCode from city where id= #{id} <!-- select的字段得跟resultType的字段一致 -->
</select>
<!-- 多参数SQL 映射的 函数,其入参为POJO -->
<select id="findCityByBean" parameterType="com.maven.hogen.CityParams" resultType="com.maven.hogen.City">
select id,name as cityName,countryCode from city where name= #{name} and id=#{id}
</select>
<!-- 多参数SQL 映射的 函数,其入参为注解过的参数 -->
<select id="findCityByAnnotation" resultType="com.maven.hogen.City">
select id,name as cityName,countryCode from city where name= #{CityParams.name} and id=#{id}
</select>
<!-- 定义一个SQL结果和POJO之间的字段映射规则,供<select>使用 -->
<resultMap id="cityMap" type="com.maven.hogen.City">
<id property="id" column="id"/>
<result property="cityName" column="city_name"/>
</resultMap>
<select id="getCity" parameterType="long" resultMap="cityMap">
select id,city_name from city where id= #{id}
</select>
<!-- useGeneratedKeys表示插入后,将数据库自增ID回填给入参, keyProperty指明回填的POJO字段 -->
<insert id="insertRole" parameterType="city" useGeneratedKeys="true" keyProperty="id">
insert into city(role_name, note) values(#{cityName} , #{note})
</ insert >
<update id="updateRole" parameterType="role">
update t_role set role_name=#{roleName}, note= #{note} where id = #{id}
</update>
<delete id="deleteRole" parameterType="long">
delete from t role where id = #{id}
</delete>
<!-- 一对一级联,即City包含一个Airport,查询City时带上Airport -->
<resultMap type="com.maven.hogen.City" id="cityMap">
<!-- 查询字段 与 pojo字段的对应关系,同名同类型字段自动对应 -->
<id column="id" property="id"/> <!-- 字段映射,作为association参数的字段必须写,否则会为空 -->
<!-- City中包含一个airport,通过getAirport来查询,column是外层SQL得到的列,是要给getAirport传的参数 -->
<association property="airport" column="airport_id,airport_name" select=”com.maven.hogen.AirportMapper.getAirport”/>
<!-- 基础类型的成员,除了用级联,也可以在主查询语句的SELECT子句中增加嵌套 或 连接查询来实现 -->
<association property="carCount" column="id" select=”com.maven.hogen.CityMapper.getCarCount”/>
</resultMap>
<!-- 一对多级联,即City包含一个Street列表,查询City时带上Street列表 -->
<resultMap type="com.maven.hogen.City" id="cityMap">
<!-- City中包含一个streets,通过getStreet来查询一个street,column是外层SQL得到的列,是要给getStreet传的参数 -->
<collection property="streets" column="street_id" select="com.maven.hogen.StreetMapper.getStreet" fetchType="lazy"/>
</resultMap>
</mapper>
多对多关系 可以 拆分成两个一对多
延迟加载:<association>和<resultMap>中含有fetchType="lazy",那么这个级联将延迟加载,即查询City时,不会马上带上airport,当调用city.getAirport()时,才发sql查询airport。fetchType设置会覆盖主配置文件中的lazyLoadingEnabled和aggressiveLazyLoading。原理:映射器返回的City实例,实际是City实例的代理对象,getAirport被修饰,sql查询得到aireport,再返回
6、使用
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSession;
InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml"); // 从resource目录,读入基础配置文件
SqlSessionFactory SqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); // 用建造者模式建造 SqlSession 工厂
SqlSession sqlSession = SqlSessionFactory.openSession() ; // 用工厂模式生产 SqlSession
CityMapper cityMapper = sqlSession.getMapper(CityMapper.class) ; // 用反射机制 生成 映射接口的子实例
City city = cityMapper.getCity(1L); // 调用接口的方法 相当于 SQL操作
System.out.println(city.getCityName());
sqlSession.close();
缓存
一级缓存:SqlSession 上的缓存,默认开启
二级缓存:SqlSessionFactory上的缓存,需要二级缓存的POJO得可序列化,即实现java.io.Serializable
给单个映射设置缓存规则
<select flushCache="false" useCache="true"/>
<insert flushCache="true"/>
<update flushCache="true"/>
<delete flushCache="true"/>
动态SQL
减少JAVA代码中的判断,减少SQL拼接
<!-- if 结构 -->
<selec>
select id,city_name from city where 1=1
<if test="cityName !=null and roleName !="">
and city_name like concat('%', #{cityName}, '%')
</ if>
</select>
<!-- if...else if...else 结构 -->
<select>
select id,city_name from city where 1=1
<choose>
<when test="id != null">
and id = #{id}
</when>
<when test="cityName != null">
and city_name like concat('%', #{cityName}, '%')
</when>
<otherwise>
and countryCode is not null
</otherwise>
</choose>
</select>
<!-- 用<where>代替where 1=1 -->
<select>
select id,city_name from city
<where> <!-- 能把多余的and去掉 -->
<if test="cityName != null">
and city_name like concat('%', #{cityName}, '%')
</if>
</where>
</select>
<update>
update city
<set> <!-- 能把多余的逗号去掉 -->
<if test="cityName !=null">
city_name= #{cityName},
</if>
</set>
where id=#{id}
</update>
<!-- for 结构 -->
<select>
select * from city where city_name in
<!-- collection是入参,open、separator、close用于拼接语句 -->
<foreach item="cityName" index="index" collection="list" open="(" separator="," close=")">
#{cityName}
</foreach>
< /select>
SQL注入
#{}是经过预编译的,是安全的;${}是未经过预编译的,仅仅是取变量的值,是非安全的,存在SQL注入
网友评论