03_Mybatis-Plus入门CRUD

03_Mybatis-Plus入门CRUD


通用的CRUD(入门DEMO)

  1. 提出问题:


    假设我们已经存在一张tbl_employee表,且已有对应的实体类Employee,实现tbl_employee表的CRUD操作我们需要做什么?
  2. 实现方式:

  • 基于Mybatis:
    • 需要编写EmployeeMapper接口,并手动编写CRUD方法。
    • 提供EmployeeMapper.xml映射文件,并手动编写每个方法对应的SQL语句。
  • 基于MP
    • 只需要创建EmployeeMapper接口,并继承BaseMapper接口,这就是使用MP需要完成的所有操作,甚至不用创建SQL映射文件。
  1. 具体实现

    首先要编写Mapper接口:
package top.tomxwd.mp.mapper;
import com.baomidou.mybatisplus.mapper.BaseMapper;
import top.tomxwd.mp.beans.Employee;
/**
 * Mapper接口
 *
 * 基于Mybatis实现:在Mapper接口中编写CRUD相关方法,还要提供对应的SQL映射文件 以及方法对应的sql语句
 *
 * 基于MP:让XxxMapper接口继承BaseMapper接口即可
 *          BaseMapper<T> : 泛型指定的就是当前Mapper接口所操作的实体类类型
 *
 */
public interface EmployeeMapper extends BaseMapper<Employee> {
}

第二步要在实体类上的主键打@TabledId注解

/**
 * @TableId:
 *  value:指定表中的主键列的列名,如果实体属性名和列名一致,可以省略
 *  type:指定主键策略
 */
@TableId(value="id",type=IdType.AUTO)
private Integer id;

第三步要在实体类上打@TableName注解

/*
 * Mybatis-Plus会默认使用实体类的类名到数据库中找对应的表
 */
@TableName(value="tbl_employee")
public class Employee {}

以上就是MP的CRUD操作所需要做的事情。

  1. 测试:
private ApplicationContext ioc = new ClassPathXmlApplicationContext("applicationContext.xml");
private EmployeeMapper employeeMapper = ioc.getBean("employeeMapper",EmployeeMapper.class);
/**
 * 通用的插入操作
 * @throws Exception
 */
@Test
public void testCommonInsert() throws Exception {
  // 初始化Employee对象
  Employee employee = new Employee();
  employee.setAge(18);
  employee.setEmail("marry@tomxwd.top");
  employee.setGender(0);
  employee.setLastName("Marry");
  // 插入到数据库 返回受影响的条数
  Integer result = employeeMapper.insert(employee);
  System.out.println("result:" + result);
}

输出结果:

省略其他日志信息之后:
DEBUG 06-08 17:41:51,646 ==>  Preparing: INSERT INTO tbl_employee ( last_name, email, gender, age ) VALUES ( ?, ?, ?, ? )  (JakartaCommonsLoggingImpl.java:54)
DEBUG 06-08 17:41:51,679 ==> Parameters: Marry(String), marry@tomxwd.top(String), 0(Integer), 18(Integer) (JakartaCommonsLoggingImpl.java:54)
DEBUG 06-08 17:41:51,683 <==    Updates: 1 (JakartaCommonsLoggingImpl.java:54)
result:1

主键策略以及@TableId注解

描述
idType.AUTO 数据库ID自增
idType.INPUT 用户输入ID
idType.ID_WORKER 全局唯一ID,内容为空自动填充(默认配置)
idType.UUID 全局唯一id,内容为空自动填充
/**
 * @TableId:
 *  value:指定表中的主键列的列名,如果实体属性名和列名一致,可以省略
 *  type:指定主键策略
 */
@TableId(value="id",type=IdType.AUTO)
private Integer id;

如果实体类field名和表中的字段名一样,则value可以不写。


@TableName注解

表名注解。

描述
value 表名(默认空)
resultMap xml字段映射resultMap的ID

@TableField注解

字段注解:

其一:(value)比如遇到表中是last_name,实体类属性是lastName,而且全局策略配置没有进行配置,则需要用到该字段,与@TableName类似,都是用来解决不统一的问题。用value来指定表中的字段名。

其二:(exist)里面有个exist属性,表示这个是不是数据库表里面的字段,因为以后会有这种情况。


MP全局策略配置(重要)

问题引出:


数据库中的字段名是last_name,而实体类属性名为lastName,但是根据日志信息得到的插入信息确实是last_name字段,为什么?


而且在mybatis-config.xml文件里也没有配置对应的驼峰规则等,为什么?


说明MP帮我们去解决了这个问题。

定义Mybatis-Plus的全局策略配置

在spring配置文件(applicationContext.xml)中进行配置。

为什么要进行全局配置?

比如每个实体类都以tbl_开头,那么每一个实体类都需要去声明@TableName,每一个主键id都需要配置自动递增,则需要每一个id字段去设置@TabledId注解。相对麻烦,如果用全局配置,则一步到位。

  1. 字段驼峰原则
  2. 配置全局主键策略
  3. 配置全局表前缀策略
  4. 后续会使用更多的策略来配置
<!-- 定义Mybatis-Plus的全局策略配置 -->
<!-- 定义Mybatis-Plus的全局策略配置 -->
<bean id="globalConfiguration" class="com.baomidou.mybatisplus.entity.GlobalConfiguration">
  <!-- 2.3版本之后,dbColumnUnderline默认就是true -->
  <!-- 驼峰原则 -->
  <property name="dbColumnUnderline" value="true"></property>
  <!-- 配置全局主键策略 0为自动递增 -->
  <property name="idType" value="0"></property>
  <!-- 全局的表前缀策略配置 -->
  <property name="tablePrefix" value="tbl_"></property>
</bean>

切记,在配置完全局策略配置之后,需要把改配置注入到Mybatis配置中去,也就是SqlSessionFactorybean,此时才会生效。否则报错。

<!-- 配置MybatisSqlSessionFactoryBean -->
<bean id="sqlSessionFactoryBean" class="com.baomidou.mybatisplus.spring.MybatisSqlSessionFactoryBean">
  <!-- 数据源 -->
  <property name="dataSource" ref="dataSource"></property>
  <property name="configLocation" value="classpath:mybatis-config.xml"></property>
  <!-- 别名处理 -->
  <property name="typeAliasesPackage" value="top.tomxwd.mp.beans"></property>
  <!-- 注入全局MP策略配置  -->
  <property name="globalConfig" ref="globalConfiguration"></property>
</bean>

插入数据获取主键值

以前的做法:

Integer insertEmployee(Employee employee)
<insert useGeneratedKeys="true" keyProperty="id">SQL...</insert>

MP的做法:

MP会自动把主键值回写到实体类中。


insertAllColumn方法

普通的insert方法是使用非空来判断是否插入值,而insertAllColumn方法是插入所有的字段。

/**
 * insertAllColumn方法
 * @throws Exception
 */
@Test
public void testInsertAllColumn() throws Exception {
  Employee e = new Employee();
  e.setLastName("Tony");
  e.setEmail("tony@tomxwd.top");
  e.setAge(50);
  Integer result = employeeMapper.insertAllColumn(e);
  System.out.println(result);
  Integer id = e.getId();
  System.out.println(id);
}

此时日志记录为:

DEBUG 06-08 20:33:09,786 ==>  Preparing: INSERT INTO tbl_employee ( last_name,email,gender,age ) VALUES ( ?,?,?,? )  (JakartaCommonsLoggingImpl.java:54)
DEBUG 06-08 20:33:09,820 ==> Parameters: Tony(String), tony@tomxwd.top(String), null, 50(Integer) (JakartaCommonsLoggingImpl.java:54)
DEBUG 06-08 20:33:09,824 <==    Updates: 1 (JakartaCommonsLoggingImpl.java:54)

发现数据库结果:gender为null,没有用到数据库中的default值。


updateById方法

null则不更新,返回结果是影响条数。


相关代码:

/**
 * 通用更新操作
 * @throws Exception
 */
@Test
public void testUpdateById() throws Exception {
  Employee e = new Employee();
  e.setId(9);
  e.setGender(1);
  Integer result = employeeMapper.updateById(e);
  System.out.println(result);
}

日志输出:

DEBUG 06-08 20:38:33,846 ==>  Preparing: UPDATE tbl_employee SET gender=? WHERE id=?  (JakartaCommonsLoggingImpl.java:54)
DEBUG 06-08 20:38:33,888 ==> Parameters: 1(Integer), 9(Integer) (JakartaCommonsLoggingImpl.java:54)
DEBUG 06-08 20:38:33,890 <==    Updates: 1 (JakartaCommonsLoggingImpl.java:54)

updateAllColumnById方法

跟updateById的区别在于,null值也会插入到里面。

相关代码:

/**
 * UpdateAllColumnById方法
 * @throws Exception
 */
@Test
public void testUpdateAllColumnById() throws Exception {
  Employee e = new Employee();
  e.setId(9);
  e.setGender(1);
  Integer result = employeeMapper.updateAllColumnById(e);
  System.out.println(result);
}

日志输出:

DEBUG 06-08 20:44:18,682 ==>  Preparing: UPDATE tbl_employee SET last_name=?,email=?,gender=?,age=? WHERE id=?  (JakartaCommonsLoggingImpl.java:54)
DEBUG 06-08 20:44:18,742 ==> Parameters: null, null, 1(Integer), null, 9(Integer) (JakartaCommonsLoggingImpl.java:54)
DEBUG 06-08 20:44:18,746 <==    Updates: 1 (JakartaCommonsLoggingImpl.java:54)

selectById方法

相关代码:

/**
 * selectById方法
 * @throws Exception
 */
@Test
public void testSelectById() throws Exception {
  Employee employee = employeeMapper.selectById(8);
  System.out.println(employee);
}

日志输出:

DEBUG 06-08 20:48:39,273 ==>  Preparing: SELECT id,last_name AS lastName,email,gender,age FROM tbl_employee WHERE id=?  (JakartaCommonsLoggingImpl.java:54)
DEBUG 06-08 20:48:39,308 ==> Parameters: 8(Integer) (JakartaCommonsLoggingImpl.java:54)
DEBUG 06-08 20:48:39,326 <==      Total: 1 (JakartaCommonsLoggingImpl.java:54)

selectOne方法(条件查询)

相关代码:

/**
 * 多个列来查询
 * SelectOne
 * @throws Exception
 */
@Test
public void testSelectOne() throws Exception {
  // 通过多个列进行查询 比如 id和lastName来查询
  Employee employee = new Employee();
  employee.setId(7);
  employee.setLastName("Marry");
  Employee e = employeeMapper.selectOne(employee);
  System.out.println(e);
}

日志输出:

DEBUG 06-08 20:52:39,303 ==>  Preparing: SELECT id,last_name AS lastName,email,gender,age FROM tbl_employee WHERE id=? AND last_name=?  (JakartaCommonsLoggingImpl.java:54)
DEBUG 06-08 20:52:39,348 ==> Parameters: 7(Integer), Marry(String) (JakartaCommonsLoggingImpl.java:54)
DEBUG 06-08 20:52:39,364 <==      Total: 1 (JakartaCommonsLoggingImpl.java:54)

需要注意的是!该方法是查询一条数据的,不可以返回多个数据,否则报错!!!


selectBatchIds方法(批量查询根据ID)

相关代码:

/**
 * 批量查询,根据Id
 * @throws Exception
 */
@Test
public void testSelectBatchIds() throws Exception {
  ArrayList<Integer> Ids = new ArrayList<Integer>();
  Ids.add(1);
  Ids.add(2);
  Ids.add(3);
  List<Employee> list = employeeMapper.selectBatchIds(Ids);
  System.out.println(list);
}

日志输出:

DEBUG 06-08 21:06:49,409 ==>  Preparing: SELECT id,last_name AS lastName,email,gender,age FROM tbl_employee WHERE id IN ( ? , ? , ? )  (JakartaCommonsLoggingImpl.java:54)
DEBUG 06-08 21:06:49,440 ==> Parameters: 1(Integer), 2(Integer), 3(Integer) (JakartaCommonsLoggingImpl.java:54)
DEBUG 06-08 21:06:49,457 <==      Total: 3 (JakartaCommonsLoggingImpl.java:54)

控制台输出:

[
  Employee [id=1, lastName=Tom, email=tom@tomxwd.top, gender=1, age=22],
  Employee [id=2, lastName=Jerry, email=jerry@tomxwd.top, gender=0, age=25],
  Employee [id=3, lastName=Black, email=black@tomxwd.top, gender=1, age=30]
]

selectByMap方法(根据Map对象进行条件查询)

需要注意的是,Map中封装的key是column名,也就是数据库中的列名,而不是实体类的属性名。
相关代码:

/**
 * 根据Map对象查询,即是封装为Map对象,条件查询
 * @throws Exception
 */
@Test
public void testSelectByMap() throws Exception {
  Map<String,Object> columnMap = new HashMap<String, Object>();
  columnMap.put("last_name", "Marry");
  List<Employee> list = employeeMapper.selectByMap(columnMap);
  System.out.println(list);
}

日志输出:

DEBUG 06-08 21:13:16,779 ==>  Preparing: SELECT id,last_name AS lastName,email,gender,age FROM tbl_employee WHERE last_name = ?  (JakartaCommonsLoggingImpl.java:54)
DEBUG 06-08 21:13:16,808 ==> Parameters: Marry(String) (JakartaCommonsLoggingImpl.java:54)
DEBUG 06-08 21:13:16,824 <==      Total: 3 (JakartaCommonsLoggingImpl.java:54)

控制台输出:

[Employee [id=5, lastName=Marry, email=marry@tomxwd.top, gender=0, age=18], Employee [id=6, lastName=Marry, email=marry@tomxwd.top, gender=0, age=18], Employee [id=7, lastName=Marry, email=marry@tomxwd.top, gender=0, age=18]]

selectPage方法(分页查询)

第一个参数:Page对象,继承了Pagination对象,而Pagination对象又继承了RowBounds对象(Mybatis的分页对象)

第二个参数是条件对象,后续说明,此处不用。
相关代码:

/**
 * 分页查询
 * 第一个对象为Page对象,继承了Pagination对象,而Pagination对象继承RowBounds(Mybatis的分页对象)
 * 第二个对象是条件对象
 * @throws Exception
 */
@Test
public void testSelectPage() throws Exception {
  List<Employee> list = employeeMapper.selectPage(new Page<Employee>(2, 2), null);
  System.out.println(list);
}

日志输出:

DEBUG 06-08 21:20:29,110 ==>  Preparing: SELECT id,last_name AS lastName,email,gender,age FROM tbl_employee  (JakartaCommonsLoggingImpl.java:54)
DEBUG 06-08 21:20:29,181 ==> Parameters:  (JakartaCommonsLoggingImpl.java:54)

控制台输出:

[
  Employee [id=3, lastName=Black, email=black@tomxwd.top, gender=1, age=30],
  Employee [id=4, lastName=White, email=white@tomxwd.top, gender=0, age=35]
]

根据日志信息可知,并没有看到limit子句,说明底层还是用了Mybatis提供的RowBounds分页。使用的是内存的分页方式。

如果想要做到真正的物理分页(limit)还是要依赖一些分页插件来做(PageHelper等)。


deleteById方法(根据id删除)

相关代码:

/**
 * 根据id删除数据
 * @throws Exception
 */
@Test
public void testDeleteById() throws Exception {
  Integer result = employeeMapper.deleteById(9);
  System.out.println(result);
}

日志输出:

DEBUG 06-08 21:30:41,429 ==>  Preparing: DELETE FROM tbl_employee WHERE id=?  (JakartaCommonsLoggingImpl.java:54)
DEBUG 06-08 21:30:41,470 ==> Parameters: 9(Integer) (JakartaCommonsLoggingImpl.java:54)
DEBUG 06-08 21:30:41,473 <==    Updates: 1 (JakartaCommonsLoggingImpl.java:54)

控制台输出:

1

deleteByMap方法(map来封装列名信息)

map的key是列名,而不是实体类的属性名。

相关代码:

/**
 * 根据columnMap条件删除数据
 * @throws Exception
 */
@Test
public void testDeleteByMap() throws Exception {
  Map<String, Object> columnMap = new HashMap<String, Object>();
  columnMap.put("last_name", "Marry");
  columnMap.put("gender", 0);
  Integer result = employeeMapper.deleteByMap(columnMap);
  System.out.println(result);
}

日志输出:

DEBUG 06-08 21:35:50,642 ==>  Preparing: DELETE FROM tbl_employee WHERE gender = ? AND last_name = ?  (JakartaCommonsLoggingImpl.java:54)
DEBUG 06-08 21:35:50,674 ==> Parameters: 0(Integer), Marry(String) (JakartaCommonsLoggingImpl.java:54)
DEBUG 06-08 21:35:50,677 <==    Updates: 3 (JakartaCommonsLoggingImpl.java:54)

控制台输出:

3

deleteBatchIds方法(根据Id批量删除)

相关代码:

/**
 * 根据Id批量删除
 * @throws Exception
 */
@Test
public void testDeleteBatchIds() throws Exception {
  List<Integer> Ids = new ArrayList<Integer>();
  Ids.add(10);
  Ids.add(11);
  Integer result = employeeMapper.deleteBatchIds(Ids);
  System.out.println(result);
}

日志输出:

DEBUG 06-08 21:43:24,751 ==>  Preparing: DELETE FROM tbl_employee WHERE id IN ( ? , ? )  (JakartaCommonsLoggingImpl.java:54)
DEBUG 06-08 21:43:24,838 ==> Parameters: 10(Integer), 11(Integer) (JakartaCommonsLoggingImpl.java:54)
DEBUG 06-08 21:43:24,840 <==    Updates: 2 (JakartaCommonsLoggingImpl.java:54)

控制台输出:

2

MP启动注入SQL原理分析

问题:

XxxMapper继承了BaseMapper<T>,BaseMapper中提供了通用的CRUD方法,方法来源于BaseMapper,有方法就必须有SQL,因为Mybatis最终还是要通过SQL语句来操作数据。

前置知识:

Mybatis源码中有比较重要的一些对象,Mybatis框架的执行流程。

  • Configuration 全局配置对象
  • MappedStatement
  • ......

通过现象看到本质

  1. employeeMapper的本质:org.apache.ibatis.binding.MapperProxy
  2. MapperProxy中,有一个sqlSession-->SqlSessionFactory。
  3. SqlSessionFactory-->Configuration-->MappedStatements,(每一个MappedStatement都表示Mapper接口中的一个方法与Mapper映射文件中的一个SQL)。在这里面-->sqlSource-->sql里面就是要执行的sql语句。


    也就是说,MP在启动的时候会挨个分析XxxMapper中的方法,并且将对应的SQL语句处理好,保存到Configuration对象的MappedStatements中。
本质

对象介绍:

  1. sqlMethod:枚举对象,MP支持的SQL方法
  2. TableInfo:数据库表反射信息,可以获取到数据库表相关的信息。
  3. sqlSource:SQL语句处理对象
  4. MapperBuilderAssistant:用于缓存、SQL参数、查询返回结果集处理等。

    通过MapperBuilderAssistant添加到Configrration中的mappedstatements中去。
DEBUG 06-08 21:43:24,522 Invoking afterPropertiesSet() on bean with name 'employeeMapper' (AbstractAutowireCapableBeanFactory.java:1670)
DEBUG 06-08 21:43:24,567 addMappedStatement: top.tomxwd.mp.mapper.EmployeeMapper.deleteById (JakartaCommonsLoggingImpl.java:54)
DEBUG 06-08 21:43:24,570 addMappedStatement: top.tomxwd.mp.mapper.EmployeeMapper.deleteBatchIds (JakartaCommonsLoggingImpl.java:54)
DEBUG 06-08 21:43:24,573 addMappedStatement: top.tomxwd.mp.mapper.EmployeeMapper.updateById (JakartaCommonsLoggingImpl.java:54)
DEBUG 06-08 21:43:24,575 addMappedStatement: top.tomxwd.mp.mapper.EmployeeMapper.updateAllColumnById (JakartaCommonsLoggingImpl.java:54)
DEBUG 06-08 21:43:24,575 addMappedStatement: top.tomxwd.mp.mapper.EmployeeMapper.selectById (JakartaCommonsLoggingImpl.java:54)
DEBUG 06-08 21:43:24,576 addMappedStatement: top.tomxwd.mp.mapper.EmployeeMapper.selectBatchIds (JakartaCommonsLoggingImpl.java:54)
DEBUG 06-08 21:43:24,579 addMappedStatement: top.tomxwd.mp.mapper.EmployeeMapper.insert (JakartaCommonsLoggingImpl.java:54)
DEBUG 06-08 21:43:24,581 addMappedStatement: top.tomxwd.mp.mapper.EmployeeMapper.insertAllColumn (JakartaCommonsLoggingImpl.java:54)
DEBUG 06-08 21:43:24,584 addMappedStatement: top.tomxwd.mp.mapper.EmployeeMapper.delete (JakartaCommonsLoggingImpl.java:54)
DEBUG 06-08 21:43:24,586 addMappedStatement: top.tomxwd.mp.mapper.EmployeeMapper.deleteByMap (JakartaCommonsLoggingImpl.java:54)
DEBUG 06-08 21:43:24,588 addMappedStatement: top.tomxwd.mp.mapper.EmployeeMapper.update (JakartaCommonsLoggingImpl.java:54)
DEBUG 06-08 21:43:24,590 addMappedStatement: top.tomxwd.mp.mapper.EmployeeMapper.updateForSet (JakartaCommonsLoggingImpl.java:54)
DEBUG 06-08 21:43:24,592 addMappedStatement: top.tomxwd.mp.mapper.EmployeeMapper.selectByMap (JakartaCommonsLoggingImpl.java:54)
DEBUG 06-08 21:43:24,602 addMappedStatement: top.tomxwd.mp.mapper.EmployeeMapper.selectOne (JakartaCommonsLoggingImpl.java:54)
DEBUG 06-08 21:43:24,604 addMappedStatement: top.tomxwd.mp.mapper.EmployeeMapper.selectCount (JakartaCommonsLoggingImpl.java:54)
DEBUG 06-08 21:43:24,608 addMappedStatement: top.tomxwd.mp.mapper.EmployeeMapper.selectList (JakartaCommonsLoggingImpl.java:54)
DEBUG 06-08 21:43:24,610 addMappedStatement: top.tomxwd.mp.mapper.EmployeeMapper.selectPage (JakartaCommonsLoggingImpl.java:54)
DEBUG 06-08 21:43:24,612 addMappedStatement: top.tomxwd.mp.mapper.EmployeeMapper.selectMaps (JakartaCommonsLoggingImpl.java:54)
DEBUG 06-08 21:43:24,614 addMappedStatement: top.tomxwd.mp.mapper.EmployeeMapper.selectMapsPage (JakartaCommonsLoggingImpl.java:54)
DEBUG 06-08 21:43:24,616 addMappedStatement: top.tomxwd.mp.mapper.EmployeeMapper.selectObjs (JakartaCommonsLoggingImpl.java:54)

从日志信息中可以看出,在启动的时候,会把每一个方法构建成每个MappedStatement。
整个构造流程

  1. AutoSqlInjector(SQL自动注入器)
  2. injectDeleteByIdSql方法构建出SqlSource(languageDriver.createSqlSource(configuration,sql,modelClass;)
  3. this.addDeleteMappedStatement(mapperClass,sqlMethod.getMethod(),sqlSource)
  4. builderAssistant.addMappedStatement->configuration.addMappedStatement(statement);最终注入到configuration中。

总结

  1. 以上是基本的CRUD操作,我们仅仅需要继承一个BaseMapper接口,就可以实现大部分的单标CRUD操作。BaseMapper提供了多达17个方法给我们使用,可以很方便的实现单一、批量、分页等操作。极大减少开发负担,难道这就是MP的强大之处吗?
  2. 提出需求:

    现在有一个需求,我们需要分页查询Employee表中,年龄在18-50之间性别为难而且姓名为Xx的用户,这时候我们怎么实现上述需求呢?


    Mybatis:需要在SQL映射文件中编写带有条件查询的SQL,并且基于PageHelper插件完成分页。

    实现上面一个简单的需求,往往需要我们做很多重复单调的工作。普通的Mapper能够解决这类痛点吗?


    MP:依旧不用编写SQL语句,MP提供了功能强大的条件构造器EntityWrapper。
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,222评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,455评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,720评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,568评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,696评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,879评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,028评论 3 409
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,773评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,220评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,550评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,697评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,360评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,002评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,782评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,010评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,433评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,587评论 2 350

推荐阅读更多精彩内容

  • 前言: mybatis在持久层框架中还是比较火的,一般项目都是基于ssm。虽然mybatis可以直接在xml中通过...
    贪挽懒月阅读 1,018,956评论 63 386
  • MyBatis是目前最流行的JDBC持久层框架,著名的ssm框架中的m。关于springboot集成mybatis...
    郭艺宾阅读 1,198评论 0 2
  • 前言: 关于mybatis-plus的简介以及基本使用,我在《mybatis-plus的使用 ------ 入门》...
    贪挽懒月阅读 161,674评论 58 178
  • 1. 简介 1.1 什么是 MyBatis ? MyBatis 是支持定制化 SQL、存储过程以及高级映射的优秀的...
    笨鸟慢飞阅读 5,460评论 0 4
  • 前言 项目开发一直使用MyBatis , 简单,方便,容易上手,调优比其他的ORM框架容易的多。最近一次偶然的搜索...
    秋的蓝阅读 4,494评论 0 2