【java学习】MyBatis使用——Java 数据持久层框架
1,概念
MyBatis是一个数据持久层(ORM)框架,封装了jdbc。把实体类和SQL语句之间建立了映射关系,是一种半自动化的ORM实现。MyBATIS需要开发人员自己来写sql语句,这可以增加了程序的灵活性,在一定程度上可以作为ORM的一种补充。
是由Apache开源项目iBatis3迁移到Github,命名为MyBatis。
1)特性
- 支持定制化SQL、存储过程及高级映射;
- 避免了几乎所有的JDBC代码和手动设置参数以及获取结果集;
- 可以使用简单的XML或注解用于配置和原始映射,将接口和java的POJO映射成数据库种的记录;
- 是一个半自动的ORM(Object Relation Mapping)框架。
2)和其他持久化层技术对比
- JDBC
SQL夹杂在java代码中耦合度高,导致硬编码内伤;
维护困难不易修改;
代码冗长,开发效率低。 - Hibernate和JPA
操作简单,开发效率高。
但过于复杂的sql需要绕过框架,完全由框架内部产生sql不易特殊化吹;
反射操作太多导致数据库性能下降。 - MyBatis
性能出色;开发效率逊色Hibernate和JPA。
sql和java编码分开,功能边界清晰。==》java做业务,sql做数据。
3)字符转义:<![CDATA[ ]]>
使用<![CDATA[]]>来包含不被xml解析器解析的内容:”<”和”&”。但要注意的是:
(1) 此部分不能再包含”]]>”;
(2) 不允许嵌套使用;
(3)”]]>”这部分不能包含空格或者换行。
比如<![CDATA[<]]> 表示文本内容“<”
2,使用
mybatis的sql操作有三种:
- 注解;
- xml;
- QueryWrapper + xm (推荐)
1)引入依赖
<dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.4.1</version></dependency><dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter</artifactId><version>2.1.4</version></dependency>
2)配置文件
配置连接、事务方式等。
3)po
@TableName(value = "tbl_user", autoResultMap = true)
public class User{...
}
4)Mapper及常见注解
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;@Mapper
public interface UserMapper extends BaseMapper<User> {}
关于sql操作,可以使用注解,也可以直接在xml中写sql,通过xml中标签的id来绑定。
对于简单sql可以直接使用mapper的包装类,对于复杂sql写xml中更灵活,所以不推荐使用注解进行开发。
org.apache.ibatis.annotations包常见注解见后文。
5)映射文件xml
<?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"><mapper namespace="com.test.mapper.postgresql.UserMapper "> --路径,必须和类名完全一致各种标签,对应mapper类的各种方法。注意:
1.id要和mapper接口中的方法名完全一致</mapper>
1>添加(insert标签)
<!--int insertUser();-->
<insert id="insertUser">insert into t_user values(null,'admin','男')
</insert>
2>删除(delete标签)
<!--int deleteUser();-->
<delete id="deleteUser">delete from t_user where id = 7
</delete>
3>修改(update 标签)
<!--int updateUser();-->
<update id="updateUser">update t_user set username='abc' where id = 7
</update> <update>update user<set><if test="username != null">username=#{username}</if></set>
</update>
set元素会前置set关键字,同时也会删除无关的逗号
等价于:
<trim prefix="set" suffixoverrides=",">
...
</trim>
4>查询对象(select 标签)
<!--User getUserById();-->
<select id="getUserById" resultType="com.luo.bean.User"> select* from t_user where id =7
</select >
查询必须设置返回值:
resultType(默认映射关系)、
resultMap(自定义映射关系、一对多多对一映射关系)。
查询集合:
<!--List<User> getAllUser();-->
<select id="getAllUser" resultType="com.luo.bean.User"> select* from t_user
</select >
5>foreach标签
批量操作,如批量增删改。sql会自动拼在缓存中,一次性提交。
批量更新或插入:
<insert id="createPartitionTable" parameterType="java.util.ArrayList" ><foreach collection="list" item="item" index="index" separator=";">CREATE TABLE if not EXISTS user_${item.sDate} PARTITION OF user FORVALUESFROM( '${item.sDatetime}' ) TO ( '${item.eDatetime}' )</foreach></insert><insert id="insertBatch" parameterType="java.util.List">INSERT INTO t_user(id, name, password)VALUES<foreach collection ="userList" item="user" separator =",">(#{user.id}, #{user.name}, #{user.password})</foreach ></insert>
7>sql标签
<sql id="sql-1">...
</sql>其他标签可以直接引用sql片段:
<...><include refid="sql-1"></include>
</...>
8>if标签
- 判断字符串是否为空
<if test="title != null and test != '' ">
and title = #{title}
</if>
- 判断集合是否为空
<if test="partAttributeList != null and partAttributeList.size > 0">
- boolean 类型的判断
即使是包装类型Boolean,也不需要判断null的问题
<if test="hasRelated">
9>choose-when标签
<choose><when test="title != null">and ...</when><when test="author != null">and...</when><otherwise>and .....</otherwise>
</choose>
10>where标签
where元素只会在至少有一个子元素条件返回SQL子句的情况下,才会插入“where子句”。
若语句的开头为“and”或“or”,where元素也会将他们去除;
<where><if test="...">and ...</if><if test="...">and ...</if>
</where>
11>resultMap 标签(结果集映射)
<resultMap id="userInfo" type="com.luo.test.User">#column="数据库字段名" property="对象类的属性名"<result column="pid" property="password" />...
</resultMap>
<select id="getUserInfo" resultMap="userInfo" parameterType="String">...</select>
12>bind标签
对传入的参数处理后让下文可以使用新的变量。
<if test="userName != null and userName != ''"><bind name="nameLike" value="'%' + userName + '%'"/>and user_name like #{nameLike}
</if>
13>函数使用
<insert id="testFun">DO $$DECLARE num INTEGER=0;BEGINselect count(*) into num as num from (select distinct cardnum from bus_data) as A;insert into ic_testselect num, dtfrom bus_data limit 100;END $$;</insert>
6)QueryWrapper
继承自 AbstractWrapper ,自身的内部属性 entity 也用于生成 where 条件
及 LambdaQueryWrapper, 可以通过 new QueryWrapper().lambda() 方法获取。
1>常见查询操作
操作方法 | 说明 | 举例 |
---|---|---|
eq | 等于 |
wrapper.eq("last_name", "皮皮虾");
|
ne | 不等于 | |
gt | 大于 | |
ge | 大于等于 | |
lt | 小于 | |
le | 小于等于 | |
like | ’%值%’ | |
notLike | ‘%值%’ | |
likeLeft | ‘%值’ | |
likeRight | ‘值%’ | |
between | 在值1和值2之间 |
betweenWrapper.between("age", 10, 20);
|
notBetween | 不在值1和值2之间 | |
isNull | 字段 IS NULL |
isNullWrapper.isNull("email");
|
isNotNull | 字段 IS NOT NULL | |
in | 字段 IN (v0, v1, …) |
inWrapper.in("age", 8, 16, 26); queryWrapper.in("type",typeList)
|
notIn | 字段 NOT IN (value.get(0), value.get(1), …) | |
or | 或者 | orWrapper.gt(“age”, 20).or().eq(“gender”, 1); |
and | 和 | |
orderByAsc | 升序:ORDER BY 字段, … ASC |
Wrapper.orderByAsc("id");
|
orderByDesc | 降序:ORDER BY 字段, … DESC | |
inSql | 字段 IN ( sql语句 ) |
inSqlWrapper .inSql("select id from employee where id < 10");
|
notInSql | 字段 NOT IN ( sql语句 ) | |
exists | 拼接 EXISTS ( sql语句 ) | existsWrapper.exists(“select last_name,gender from employee where id = 1”); |
notExists | 拼接 NOT EXISTS ( sql语句 ) |
QueryWrapper<Employee> wrapper = new QueryWrapper<>();//eq() 等于wrapper.eq("last_name", "皮皮虾");Employee one = employeeService.getOne(wrapper);System.out.println(one);
2>LambdaQueryWrapper
public List<ServiceDictionary> findServiceDictionaryByTypeOrId(DictionaryDto dictionaryDto) {LambdaQueryWrapper<ServiceDictionary> lambdaQueryWrapper = Wrappers.lambdaQuery();
//设置某个字段的值 lambdaQueryWrapper.in(!CollectionUtils.isEmpty(dictionaryDto.getTypes()),ServiceDictionary::getDictionaryTypeName,dictionaryDto.getTypes());lambdaQueryWrapper.in(!CollectionUtils.isEmpty(dictionaryDto.getIds()),ServiceDictionary::getId,dictionaryDto.getIds());lambdaQueryWrapper.orderBy(true,true,ServiceDictionary::getSort);return serviceDictionaryMapper.selectList(lambdaQueryWrapper);}
LambdaQueryWrapper<Setting> wrapper = new LambdaQueryWrapper<Setting>().eq(Setting::getId, settingDto.getId());Integer count = settingMapper.selectCount(wrapper);
7)缓存
缓存分为一级缓存和二级缓存,默认情况下,只有一级缓存开启(SqlSession级别的缓存,也称本地缓存)
二级缓存需要手动开启和配置,是基于namespace级别的缓存
为了提高扩展性,MyBatis定义了缓存接口Cache,实现Cache可以自定义二级缓存
1>一级缓存
仅仅对一个会话中的数据进行缓存(SqlSession):
映射语句文件(mapper.xml)中所有的select语句的结果将会被缓存,insert、udpate和delete会刷新缓存
缓存会使用最近最少使用算法刷新(LRU least Recently Used)清除不需要的缓存
缓存不会定时进行刷新(也就是说,没有刷新间隔)
缓存会保存列表和对象(无论查询方法返回哪一种)的1024个引用
缓存会被视为读/写缓存,这意味着获取到的对象不是共享的,可以安全的被调用者修改,而不干扰其他调用者或线程所做的潜在修改
缓存失效情况:
1、查询不同
2、增删改操作
3、查询不使用mapper.xml
4、手动清除缓存sqlSession.clearCache();
2>二级缓存
要启用全局的二级缓存,只需要在SQL映射文件(mapper.xml)中添加即可
注意:二级缓存只作用于cache标签所在的mapper.xml文件的语句中。
如果使用JavaAPI和xml混用,在共同接口的语句将不会被默认缓存,需要使用@CacheNamespaceRef指定缓存作用区域。
<setting name="CacheEnable" value="true"/>
<cache 清除策略:LRU、FIFO、SOFT、WEAKeviction="FIFO"刷新间隔flushInterval="60000"引用数目size="512"只读的缓存会给所有调用者返回缓存对象的相同实例,因此这些对象不能被修改,这就提升了可观的性能而可读写的缓存就会通过序列化返回缓存对象的拷贝,速度上会慢一些,但是更安全,因此默认值是falsereadOnly="true"/>
8)分页
1>limit语句
<select id="..." parameterType="map">...limit #{size} offset (#{page}-1)*#{size}
</select>
注意:
如果返回值是Page<>,因为查询了total,检索效率会有影响。直接通过limit查速度就会快很多。
2>Wrapper的Page参数
//注意:page从1开始
Page<TTemplateData> page = new Page<>(condition.getPage(), condition.getPageSize());
IPage<TTemplateData> templateIPage = tTemplateDataMapper.selectPage(page, templateWrapper);
3>xml的Page参数
Page<TLabel> page = new Page<>(condition.getPage(), condition.getPageSize());IPage<TLabel> labelIPage = tLabelMapper.selectPageList(page,keys,condition.getLabelTypeName());
IPage<TLabel> selectPageList(@Param("page") Page<TLabel> page,@Param("labelName") String labelName,@Param("labelTypeName") String labelTypeName);
<select id = "selectPageList" resultMap="BaseResultMap">select l.*,t.label_type_name labelTypeName from t_label l left join t_label_type t on l.label_type_id = t.id and t.status = 0where l.status = 0<if test="labelName != null and labelName != ''">and l.label_name like concat('%',#{labelName},'%')</if><if test="labelTypeName != null and labelTypeName != ''">and t.label_type_name = #{labelTypeName}</if>
</select>
3,MyBatis-plus
1)介绍
MyBatis-plus简单来说就是一款 Mybatis 增强工具,用于简化开发,提高效率。
在我们使用Mybatis时会发现,每当要写一个业务逻辑的时候都要在DAO层写一个方法,再对应一个SQL,即使是简单的条件查询、即使仅仅改变了一个条件都要在DAO层新增一个方法。MybatisPlus这个框架上手快、不用写sql语句和xml文件。直接创建查询类,就可以迅速连接数据库了。无阿点是:MybatisPlus这种过度封装的框架,强行把service层,dao层,entity层绑定在一起,耦合度太高了。
2)常用注解
见后文。
3)使用
Mybatis-Plus通过EntityWrapper(简称EW,MP封装的一个查询条件构造器)或者条件(与EW类似)来让用户自由的构建查询条件,简单便捷,没有额外的负担,能够有效提高开发效率。
public interface UserMapper extends BaseMapper<User> { }基本CRUD://插入:成功会自动回写主键到实体类
result = userMapper.insert(user);
也可以直接:user.insert();//更新:
result = userMapper.updateById(user);
也可以直接:result = user.updateById();//删除
result = userMapper.deleteById(user.getId());
也可以直接:result = t2.deleteById();//查询
User exampleUser = userMapper.selectById(user.getId());或者直接:User exampleUser = t1.selectById();
//批量查询:查询姓名为‘张三’的所有用户记录
List<User> userList = userMapper.selectList(new EntityWrapper<User>().eq("name", "张三")
); 或者直接:List<User> userList1 = user.selectList(...);
//分页查询: 10 条姓名为‘张三’的用户记录
List<User> userList = userMapper.selectPage(new Page<User>(1, 10),new EntityWrapper<User>().eq("name", "张三")
);或者直接:user.selectPage(...);
// 分页查询 10 条姓名为‘张三’、性别为男,且年龄在18至50之间的用户记录
List<User> userList = userMapper.selectPage(new Page<User>(1, 10),new EntityWrapper<User>().eq("name", "张三").eq("sex", 0).between("age", "18", "50")
);
4,原理
5,常用注解
1)类注解
1>@Mapper
在mapper接口使用@Mapper注解,编译之后会生成相应的接口实现类。xml文件可以不写。
import org.apache.ibatis.annotations.Mapper;@Mapper
public interface UserMapper extends BaseMapper<User> {}
使用 @Mapper,最终 Mybatis 会有一个拦截器,会自动的把 @Mapper 注解的接口生成动态代理类。
2>@MapperScan
每个接口可以不写@Mapper注解,统一在启动类上用@MapperScan注解。@MapperScan 配置一个或多个包路径,自动的扫描这些包路径下的类,自动的为它们生成代理类。
import org.mybatis.spring.annotation.MapperScan;@SpringBootApplication
//直接注入某个类
@MapperScan(basePackageClasses = com.lwh.mapper.UserMapper.class)
//直接注入某个路径
@MapperScan({"com.xttblog.mapper","com.xttblog.dao"})
@MapperScan(value = {"com.fanr.graduation.mapper"})
@ComponentScan(basePackages = {"com.xttblog.*"})
当使用了 @MapperScan 注解,将会生成 MapperFactoryBean, 如果没有标注 @MapperScan 也就是没有 MapperFactoryBean 的实例,就走 @Import 里面的配置,具体可以在 AutoConfiguredMapperScannerRegistrar 和 MybatisAutoConfiguration 类中查看源代码进行分析。
2)PO注解
1>@TableName
实体类对应表名,注解在类上。
import com.baomidou.mybatisplus.annotation.TableName;//autoResultMap = true表示:xml 字段映射 resultMap ID
@TableName(value = "tbl_user", autoResultMap = true)
// 注解指定表名
public class User extends Model<User> {}
2>@TableId
主键字段,注解在属性上。
通过value字段写不同的命名方式(可省略);通过type设置自增算法(默认雪花算法)。
import com.baomidou.mybatisplus.annotation.TableId;@TableId(value="uid",type=IdType.AUTO)
private long id;
值 | 描述 |
---|---|
IdType.AUTO | 数据库ID自增 |
IdType.NONE | 无状态,该类型为未设置主键类型(注解里等于跟随全局,全局里约等于 INPUT) |
IdType.INPUT | insert前自行set主键值 |
IdType.ASSIGN_ID | 分配ID(主键类型为Number(Long和Integer)或String)(since 3.3.0),使用接口IdentifierGenerator的方法nextId(默认实现类为DefaultIdentifierGenerator雪花算法) |
IdType.ASSIGN_UUID | 分配UUID,主键类型为String(since 3.3.0),使用接口IdentifierGenerator的方法nextUUID(默认default方法) |
除了通过type属性值来配置主键的规则外,还可以通过application.yml文件来配置:
mybatis-plus:global-config:db-config##注解设置id-type:auto##对于没有值的数据自动赋予空,设置为false可能查不出来导致返回字段不全的问题。 call-setters-on-nulls: true
3>@TableField
设置字段属性,通过value命名(value=
可以省略),默认驼峰命名法。
//实体临时字段
@TableField(exist=false)
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.extension.handlers.FastjsonTypeHandler;
import org.apache.ibatis.type.JdbcType;//fill为自动填充策略,//FieldFill: DEFAULT:默认不处理;INSERT:插⼊时填充字段;UPDATE:更新时填充字段;INSERT_UPDATE:插⼊和更新时填充字@TableField(jdbcType = JdbcType.TIMESTAMP, fill = FieldFill.INSERT)private Date timestamp;//pg中为timestamp类型@TableField(value = "category_name", jdbcType = JdbcType.VARCHAR)private String categoryName;@TableField(jdbcType = JdbcType.INTEGER)private Integer times;//pg中对应为int4@TableField(value = "user_info", typeHandler = FastjsonTypeHandler.class)private List<JSONObject> userList;@TableField(jdbcType = JdbcType.VARCHAR, typeHandler = JsonbTypeHandler.class)private Map<String, String> map = Collections.EMPTY_MAP;//pg中对应为jsonb@TableField(jdbcType = JdbcType.VARCHAR,typeHandler = JsonListTypeHandler.class)private List<String> list = new ArrayList<>();//pg中对应为jsonb@TableField(jdbcType = JdbcType.BIGINT)private Long time = System.currentTimeMillis();//pg中对应为int8@TableField(jdbcType=JdbcType.BOOLEAN)private Boolean state;@TableField(jdbcType = JdbcType.ARRAY)private int[] arr= new int[0];//pg中对应int4[]@Type(type = "DisplayAndJumpLinkJsonType")private DisplayAndJumpLink displayAndJumpLink;
自定义映射处理类:JsonListTypeHandler
//注意,此处用泛型,如果传值过小,会用List<Integer>接数据,用List<Long>会报错,可以用Number接数据,也可以重新写一个TypeHandler(继承BaseTypeHandler<List<Long>>,重写getNullableResult方法:result = value == null ? null : JSON.parseObject(value, new TypeReference<List<Long>>(){});)
public class JsonListTypeHandler extends BaseTypeHandler<List<?>> {@Overridepublic void setNonNullParameter(PreparedStatement preparedStatement, int i, List<?> objects, JdbcType jdbcType) throws SQLException {if (objects == null) {try {preparedStatement.setNull(i, JdbcType.OTHER.TYPE_CODE);} catch (SQLException e) {throw new TypeException("Error setting null for parameter #" + i + " with JdbcType " + jdbcType + " . "+ "Try setting a different JdbcType for this parameter or a different jdbcTypeForNull configuration property. "+ "Cause: " + e, e);}} else {try {preparedStatement.setObject(i, FastJsonUtil.toJSON(objects), JdbcType.OTHER.TYPE_CODE);} catch (Exception e) {throw new TypeException("Error setting non null for parameter #" + i + " with JdbcType " + jdbcType+ " . "+ "Try setting a different JdbcType for this parameter or a different configuration property. "+ "Cause: " + e, e);}}}@Overridepublic List<?> getNullableResult(ResultSet resultSet, String s) throws SQLException {List<?> result;try {String value = resultSet.getString(s);result = value == null ? null : FastJsonUtil.parse(value, List.class);} catch (Exception e) {throw new ResultMapException("Error attempting to get column '" + s + "' from result list. Cause: " + e, e);}if (resultSet.wasNull()) {return new ArrayList<>();} else {return result;}}@Overridepublic List<?> getNullableResult(ResultSet resultSet, int i) throws SQLException {List<?> result;try {String value = resultSet.getString(i);result = value == null ? null : FastJsonUtil.parse(value, List.class);} catch (Exception e) {throw new ResultMapException("Error attempting to get column #" + i + " from result list. Cause: " + e, e);}if (resultSet.wasNull()) {return new ArrayList<>();} else {return result;}}@Overridepublic List<?> getNullableResult(CallableStatement callableStatement, int i) throws SQLException {List<?> result;try {String value = callableStatement.getString(i);result = value == null ? null : FastJsonUtil.parse(value, List.class);} catch (Exception e) {throw new ResultMapException("Error attempting to get column #" + i + " from callable statement. Cause: " + e, e);}if (callableStatement.wasNull()) {return new ArrayList<>();} else {return result;}}
}
值 | 描述 |
---|---|
value | 字段值(驼峰命名方式,该值可无) |
el | 是否为数据库表字段( 默认 true 存在,false 不存在 ) |
exist | 是否为数据库表字段( 默认 true 存在,false 不存在 ) |
strategy | 字段验证 ( 默认 非 null 判断,查看 com.baomidou.mybatisplus.enums.FieldStrategy ) |
fill | 字段填充标记 ( 配合自动填充使用 ) |
JDBC Type | Java Type |
---|---|
CHAR | String |
VARCHAR | String |
LONGVARCHAR | String |
NUMERIC | java.math.BigDecimal |
DECIMAL | java.math.BigDecimal |
BIT | boolean |
BOOLEAN | boolean |
TINYINT | byte |
SMALLINT | short |
INTEGER | int |
BIGINT | long |
REAL | float |
FLOAT | double |
DOUBLE | double |
BINARY | byte[] |
VARBINARY | byte[] |
LONGVARBINARY | byte[] |
DATE | java.sql.Date |
TIME | java.sql.Time |
TIMESTAMP | java.sql.Timestamp |
CLOB | Clob |
BLOB | Blob |
ARRAY | Array |
DISTINCT | mapping of underlying type |
STRUCT | Struct |
REF | Ref |
DATALINK | java.net.URL[color=red][/color] |
4>@TableLogic
逻辑删除,在属性上命名。
- 数据库中创建逻辑删除状态列,设置默认值为0; is_deleted int
- 实体类添加注解
@TableLogic
private Integer isDeleted;//删除时执行的是update语句
5>其它
3)Mapper注解
1>@Param
User getUserById(@Param("id")Long id);
sql中参数的两种方式:
${}
本质是字符串拼接,容易引起sql注入,不推荐使用。
#{}
本质是占位符赋值
$
转换#
的方式:
name like concat('%',#{keyword,jdbcType=VARCHAR},'%')
<if test="ids != null">and id in<foreach collection="ids" item="item" open="(" separator="," close=")">#{item}</foreach></if>
2>@Select
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;public interface UserMapper extends BaseMapper<User> {//1. 可以直接使用 `@Select` 注解。//2. xml中可以使用select标签进行绑定与映射,id一定要与方法名(getUserById)保持一致。@Select("select* from t_user where id =7")User getUserById(@Param("id")Long id);
}
3>@Options
设置缓存时间,能够为对象生成自增的key。
@Insert("INSERT INTO city (name, state, country) VALUES(#{name}, #{state}, #{country})")@Options(useGeneratedKeys = true, keyProperty = "id")void insert(City city);--useGenerateKey=true : 设置是否使用JDBC的getGenereatedKeys方法获取主键并赋值到keyProperty设置的领域模型属性中
--useCache = true表示本次查询结果被缓存以提高下次查询速度,
--flushCache = false表示下次查询时不刷新缓存,
--timeout = 10000表示查询结果缓存10000秒。
4>@Insert
我希望通过dao层的接口插入的数据能够返回主键的id(最终插入成功后从instance_id这个字段里面把数据放到传入对象的instanceId成员变量里面):
@Insert("insert into instance (infos)"+ " ("+ " @{infos},"+ " NOW()"+ ")")@Options(useGeneratedKeys = true, keyProperty = "instanceId", keyColumn = "instance_id")int addInstance(Instance instance);
5>@Update
@Update("sql")
public void createIfNotExistsTable();
6>@Delete
@Delete("delete from t_user where id=#{id}")int deleteUser(@Param("id") Integer id);
7>@ResultType
指定返回类型
@Select(value = "SELECT s.ID,s.CATEGORY_NAME AS name, s.PARENT_ID,p.CATEGORY_NAME AS parent_name FROM cd_category s LEFT JOIN cd_category p ON p.ID=s.PARENT_ID where FIND_IN_SET(s.id, query_children_category(${id}))")
@ResultType(ItemCategoryModel.class)
public List<ItemCategoryModel> getItemCategoryTree(@Param(value = "id") Integer id);@Select("select role_id AS roleId from user_role where user_id = #{userId}")
@ResultType(Long.class)
List<Long> selectRoleIdListByUserId(@Param("userId") Long userId);
8>@Results和@Result
当数据库字段名与实体类对应的属性名不一致时,可以使用@Results映射来将其对应起来。column为数据库字段名,porperty为实体类属性名,jdbcType为数据库字段数据类型,id为是否为主键。
@SelectProvider(type=SqltoolMetadataSqlProvider.class, method="selectByExample")
@Results({@Result(column="ID", property="id", jdbcType=JdbcType.INTEGER, id=true),@Result(column="NAME", property="name", jdbcType=JdbcType.VARCHAR)
})
List<SqltoolMetadata> selectByExampleWithRowbounds(SqltoolMetadataCriteria example, RowBounds rowBounds);
9>@ResultMap
当这段@Results代码需要在多个方法用到时,为了提高代码复用性,我们可以为这个@Results注解设置id,然后使用@ResultMap注解来复用这段代码。
@Select({"select id, name, class_id from my_student"})
@Results(id="studentMap", value={@Result(column="id", property="id", jdbcType=JdbcType.INTEGER, id=true),@Result(column="class_id", property="classId", jdbcType=JdbcType.INTEGER)
})
List<Student> selectAll();@Select({"select id, name, class_id from my_student where id = #{id}"})
@ResultMap(value="studentMap")
Student selectById(integer id);
10>@One
当我们需要通过查询到的一个字段值作为参数,去执行另外一个方法来查询关联的内容,而且两者是一对一关系时,可以使用@One注解来便捷的实现。
@Select({"select id, name, class_id from my_student"})
@Results(id="studentMap", value={@Result(column="id", property="id", jdbcType=JdbcType.INTEGER, id=true),@Result(column="class_id", property="myClass", javaType=MyClass.class,one=@One(select="com.example.demo.mapper.MyClassMapper.selectById"))
})
List<Student> selectAllAndClassMsg();
11>@Many
与@One类似,只不过如果使用@One查询到的结果是多行,会抛出TooManyResultException异常,这种时候应该使用的是@Many注解,实现一对多的查询。
@Select({"select id, name, class_id from my_student"})
@Results(id="studentMap", value={@Result(column="id", property="id", jdbcType=JdbcType.INTEGER, id=true),@Result(column="class_id", property="classId", jdbcType=JdbcType.INTEGER),@Result(column="id", property="gradeList", javaType=List.class,many=@Many(select="com.example.demo.mapper.GradeMapper.selectByStudentId"))
})
List<Student> selectAllAndGrade();
12>@SelectProvider
用自定义的provider类构造SQL语句,sql语句由type指定的类中的method方法返回。
// mapper类:
@SelectProvider(type = SalesOrderProvider.class, method = "selectSalesInformation")List<SalesInformation> selectSalesInformation(@Param("createDateStart") String createDateStart, @Param("createDateEnd") String createDateEnd);
//SalesOrderProvider类public String selectSalesInformation(@Param("createDateStart") String createDateStart, @Param("createDateEnd") String createDateEnd){StringBuffer sql = new StringBuffer();sql.append(" SELECT * from user");return sql.toString();}
13>@InsertProvider
批量新增:
@InsertProvider(type = BatchPreparationMsg.class, method = "batchAdd")Integer batStuAdd(@Param("list") List<PreparationMsg> list);//BatchPreparationMsg类:public String batchAdd(@Param("list") List<TaskTemplate> list){StringBuilder sb = new StringBuilder();sb.append("insert into pep_preparation_msg(REF_TABLE,REF_ID,HOTEL_ID,TASK_ID,TYPE,NAME,CONTENT,USERGROUP,SHOW_FLAG,CREATE_USER,CREATE_TIME) values");MessageFormat mf = new MessageFormat("(#'{'list[{0}].refTable},#'{'list[{0}].refId},#'{'list[{0}].hotelId},#'{'list[{0}].taskId},#'{'list[{0}].type},#'{'list[{0}].name}," +"#'{'list[{0}].content},#'{'list[{0}].usergroup},#'{'list[{0}].showFlag},#'{'list[{0}].createUser},#'{'list[{0}].createTime})");for (int i = 0; i < list.size(); i++) {sb.append(mf.format(new Object[]{i}));if (i < list.size() - 1) {sb.append(",");}}return sb.toString();}
14>@UpdateProvider
类似@InsertProvider,批量更新。
15>@DeleteProvider
类似@InsertProvider,批量删除。
16>@SelectKey
类似标签<selectKey>
@SelectKey(statement="SELECT LAST_INSERT_ID()", keyProperty="clusterId", before=false, resultType=Integer.class)- statement属性:填入将会被执行的 SQL 字符串数组。- keyProperty属性:填入将会被更新的参数对象的属性的值。- before属性:填入 true 或 false 以指明 SQL 语句应被在插入语句的之前还是之后执行。- resultType属性:填入 keyProperty 的 Java 类型。- statementType属性:填入Statement、 PreparedStatement 和 CallableStatement 中的 STATEMENT、 PREPARED 或 CALLABLE 中任一值填入 。默认值是 PREPARED。
注意:
@SelectKey注解用在已经被 @Insert 或 @InsertProvider 或 @Update 或 @UpdateProvider 注解了的方法上。若在未被上述四个注解的方法上作 @SelectKey 注解则视为无效。
如果向数据库中插入一条数据,同时有希望返回该条记录的主键,该怎么处理了?有两种情况:
(1)数据库主键不是自增列,需要预先生成
(2)是自增列,插入之后才能获知
这两种情况都可以通过SelectKey解决,第一个种就是before,第二张是after。
17>@MapKey
mybatis返回map类型,希望多条数据放到Map<K,T>中而不是List<Map<K,T>>。
@Select("select id, name from users")
@MapKey("id")
Map<Integer, User<String>> getAMapOfUsers();
18>@ConstructorArgs
根据构造方法构造对象返回。
@Select("select id, name from product where name = #{value}")
@ConstructorArgs({@Arg(id = true, column="id", javaType = ProductId.class, jdbcType=JdbcType.INTEGER),@Arg(column="name")
})
Product getProductByNameUsingConstructor(String name);
19>@CacheNamespaceRef和@CacheNamespace
CacheNamespace的引用,表示开启二级缓存,相当于<cache>
标签。
<mapper namespace="cn.mybatis.mydemo.mapper.StudentMapper"><cache eviction="FIFO" flushInterval="60000" readOnly="false" size="1024"></cache>
</mapper>当然,前提还需要在全局配置文件中开启缓存:
<setting name="cacheEnabled" value="true"/>或者这样使用:
@CacheNamespaceRef(name="com.luo.test.UserMapper"
public interface UserMapper extends BaseMapper<User> {
}
【java学习】MyBatis使用——Java 数据持久层框架相关推荐
- mybatis和spring jdbc持久层框架事务支持分析
mybatis和spring jdbc持久层框架事务支持分析 持久层框架中的事务支持指的是持久层框架如何支持数据库事务,我们先梳理出原生数据库事务操作的主线脉络,它是通过java.sql 包下的C ...
- Java数据持久层框架 MyBatis之背景知识一
对于MyBatis的学习而言,最好去MyBatis的官方文档:http://www.mybatis.org/mybatis-3/zh/index.html 对于语言的学习而言,马上上手去编程,多多练习 ...
- 数据持久层框架 Hibernate
Hibernate是一种Java语言下的对象关系映射解决方案. 它是使用GNU宽通用公共许可证发行的自由.开源的软件.它为面向对象的领域模型到传统的关系型数据库的映射,提供了一个使用方便的框架.Hib ...
- Java数据持久层框架 MyBatis之API学习八(Java API详解)
对于MyBatis的学习而言,最好去MyBatis的官方文档:http://www.mybatis.org/mybatis-3/zh/index.html 对于语言的学习而言,马上上手去编程,多多练习 ...
- Java数据持久层框架 MyBatis之API学习六(Mapper XML 文件详解)
对于MyBatis的学习而言,最好去MyBatis的官方文档:http://www.mybatis.org/mybatis-3/zh/index.html 对于语言的学习而言,马上上手去编程,多多练习 ...
- Java数据持久层框架 MyBatis之API学习五(Mapper XML 文件)
对于MyBatis的学习而言,最好去MyBatis的官方文档:http://www.mybatis.org/mybatis-3/zh/index.html 对于语言的学习而言,马上上手去编程,多多练习 ...
- java开源持久层框架集
原文链接:http://www.open-open.com/3_18.htm Hibernate Hibernate是一个开放源代码的对象关系映射框架,它对JDBC进行了非常轻量级的对象封装,使得Ja ...
- .NET开源项目介绍及资源推荐:数据持久层
在.NET平台下,关于数据持久层框架非常多,本文主要对如下几种做简要的介绍并推荐一些学习的资源: 1.NHibernate 2.NBear 3.Castle ActiveRecord 4.iBATIS ...
- Java持久层框架之mybatis使用
一.什么是框架,框架从何而来,为什么使用框架? 框架(framework): 1.是一系列jar包,其本质是对JDK功能的拓展.(jar包,jar:class文件的压缩包) 2.框架是一组程序的集合, ...
最新文章
- 分享我的第一个asp.net core开发过程
- ASP.NET中Visio图形的控制与数据的动态显示
- NR 5G NG-RAN 架 构
- 网络营销外包——企业如何选择网站服务器?网络营销外包来帮你!
- 专访驭势科技吴甘沙:无人驾驶硝烟弥漫,“创造”才有未来|封面人物
- 【转】winform回车变为tab
- SpringMVC 参数校验
- 数据eda_银行数据EDA:逐步
- mysql 查询姓王_MySQL查询语句练习题,测试足够用了
- springboot应用启动原理(一) 将启动脚本嵌入jar
- mysql通信协议的半双工机制理解
- win10 忘记登录密码后 修改密码
- 【C#】Skip和Tack方法实现分页
- VC++数据库应用开发
- 工信部官宣:5G商用牌照即将发放!
- 【BUUCTF】[Zer0pts2020]Can you guess it?
- 即将公布的2020年10m分辨率全球土地利用数据(欧空局出品)
- 移动网络简介与RRC
- 形式逻辑(普通逻辑)5:直言判断
- UDP数据包协议格式详解