Spring boot 實(shí)戰(zhàn)指南:Mybatis、動態(tài)綁定侈百、多數(shù)據(jù)源瓮下、分頁插件、Mybatis-Plus

文章目錄
二钝域、整合 Mybatis 多數(shù)據(jù)源
三讽坏、整合分頁插件 PageHelper
1.搭建數(shù)據(jù)庫、項(xiàng)目配置
3.在代碼中使用PageHelper
Mybatis在整個體系中的作用是負(fù)責(zé)連接并訪問數(shù)據(jù)庫層例证。搞過開發(fā)的同學(xué)都知道路呜,沒有數(shù)據(jù)庫的項(xiàng)目一無是處,所以Mybatis的學(xué)習(xí)是很有必要的织咧。

準(zhǔn)備工作:
數(shù)據(jù)庫:在進(jìn)入正式學(xué)習(xí)前胀葱,先確保Mysql已經(jīng)在電腦上安裝好了,最好再安裝一個可視化管理工具Navicat Premium for mysql烦感。當(dāng)然巡社,你還要會mysql的語法和基本操作等。
spring boot項(xiàng)目創(chuàng)建以及一些前置知識:可以看我上一篇博客
一手趣、整合Mybatis
整合Mybatis可以基于注解晌该,也可以基于xml文件, 二者的區(qū)別:

1.搭建數(shù)據(jù)庫環(huán)境
新建一個數(shù)據(jù)庫boot_demo绿渣,然后執(zhí)行以下sql語句:

-- 創(chuàng)建表
USE `boot_demo`;
DROP TABLE IF EXISTS `tb_user`;
CREATE TABLE `tb_user`  (
  `user_id` int(11) NOT NULL ,
  `user_name` varchar(20)  DEFAULT NULL,
  `user_age` int(11)  DEFAULT NULL,
   PRIMARY KEY (`user_id`)
) ENGINE = InnoDB;

-- 插入數(shù)據(jù)
REPLACE INTO `tb_user` (`user_id`, `user_name`, `user_age`) VALUES ('100', 'test01', '100');

2.基于注解整合Mybatis

(1)創(chuàng)建項(xiàng)目

項(xiàng)目信息填寫如下:

image.png

選擇初始依賴:

image.png

完善目錄結(jié)構(gòu):

在main/java/com/tracy/mybatisdemo下依次新建 entity 朝群、dao 和 controller 文件夾。一般來說中符,應(yīng)該再創(chuàng)建一個service包姜胖,前端調(diào)用controller接口,controller調(diào)用service淀散,service再調(diào)用dao右莱,但這章為了簡化操作省去了service部分,到后面項(xiàng)目實(shí)戰(zhàn)的時候我會創(chuàng)建更完善的目錄結(jié)構(gòu)档插。

(2)具體代碼實(shí)現(xiàn)

  • 實(shí)體類User:

在entity包下創(chuàng)建User類慢蜓,代碼如下:

package com.tracy.mybatisdemo.entity;

import lombok.Data;

//此注解來源于Lombok插件,運(yùn)行時會自動為類添加 Getter郭膛、Setter 晨抡、有參構(gòu)造、toString 、equals 和 hashCode 方法
@Data
public class User {
 
    private Integer userId;
    private String userName;
    private Integer userAge;
}

持久層UserDao接口:
在dao包下創(chuàng)建UserDao接口:

package com.tracy.mybatisdemo.dao;

import com.tracy.mybatisdemo.entity.User;
import org.apache.ibatis.annotations.*;
import org.springframework.stereotype.Repository;

import java.util.List;

@Repository
public interface UserDao {
 
    @Select("select user_id,user_name,user_age from tb_user")
    List<User> findAll();

    @Select("select user_id,user_name,user_age from tb_user where user_id = #{userId}")
    User findById(Integer userId);

    @Insert("insert into tb_user (user_id,user_name,user_age) values (#{userId},#{userName},#{userAge})")
    Integer insert(User user);

    @Update("update tb_user set user_name=#{userName},user_age=#{userAge} where user_id = #{userId}")
    Integer update(User user);

    @Delete("delete from tb_user where user_id=#{userId}")
    Integer delete(Integer userId);
}

配置包掃描:
為了使每個dao接口都被掃描到耘柱,可以在每個dao接口上加上@Mapper注解如捅,但當(dāng)dao接口比較多的時候,推薦直接在啟動類上通過注解 @MapperScan("com.tracy.mybatisdemo.dao") 的形式掃描整個dao包:

@SpringBootApplication
@MapperScan("com.tracy.mybatisdemo.dao")
public class MybatisDemoApplication {
 

    public static void main(String[] args) {
 
        SpringApplication.run(MybatisDemoApplication.class, args);
    }

}

控制層UserController類:
在controller包下創(chuàng)建UserController類:

package com.tracy.mybatisdemo.controller;

import com.tracy.mybatisdemo.dao.UserDao;
import com.tracy.mybatisdemo.entity.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import java.util.List;

@RestController
@RequestMapping("/user")
public class UserController {
 
    @Autowired
    private UserDao userDao;

    @GetMapping("/findAll")
    public List<User> findAll(){
 
        return userDao.findAll();
    }

    @GetMapping("/findById")
    public User findById(Integer userId){
 
        return userDao.findById(userId);
    }

    @PostMapping("/insert")
    public String insert(User user){
 
        userDao.insert(user);
        return "插入成功后的數(shù)據(jù)為" + userDao.findById(user.getUserId());
    }

    @PutMapping("/update")
    public String update(User user){
 
        userDao.update(user);
        return "更新成功后的數(shù)據(jù)為" + userDao.findById(user.getUserId());
    }

    @DeleteMapping("/delete")
    public String delete(Integer userId){
 
        userDao.delete(userId);
        return "刪除成功的id" + userId;
    }
}

添加數(shù)據(jù)庫配置:
在application.yml中添加以下配置:

# 數(shù)據(jù)源
spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/boot_demo?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8
    username: root
    password: 你的密碼

# Mybatis配置
# 開啟駝峰式命名規(guī)則自動轉(zhuǎn)換
mybatis:
  configuration:
    map-underscore-to-camel-case: true

(3)測試

測試工具我使用的是postman调煎,怎么安裝和使用可以網(wǎng)上百度一下镜遣。

  • 測試 localhost:8080/user/findAll GET
image.png
  • 測試 localhost:8080/user/findById GET
image.png
  • 測試 localhost:8080/user/insert POST
image.png
  • 測試 localhost:8080/user/update PUT
image.png
  • 測試 localhost:8080/user/delete DELETE
image.png

成功!

3.基于xml整合Mybatis

基于注解的Mybatis使用只能應(yīng)付一些比較簡單的數(shù)據(jù)庫查詢語句汛蝙,雖然省事烈涮,但在一定程度上也喪失了靈活性,因此窖剑,有必要學(xué)習(xí)一下基于xml整合Mybatis。

  • 首先戈稿,請先刪除UserDao接口中每個方法上的注解語句:
package com.tracy.mybatisdemo.dao;

import com.tracy.mybatisdemo.entity.User;
import org.springframework.stereotype.Repository;

import java.util.List;

@Repository
public interface UserDao {
 
    List<User> findAll();
    
    User findById(Integer userId);

    Integer insert(User user);

    Integer update(User user);

    Integer delete(Integer userId);
}

添加xml映射文件:
在resources目錄下創(chuàng)建目錄mapper西土,仔仔mapper目錄下創(chuàng)建UserMapper.xml文件:

注意 mapper namespace=“com.tracy.mybatisdemo.dao.UserDao” 一定要與dao包下的接口對應(yīng)起來。

<?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.tracy.mybatisdemo.dao.UserDao">
    <!--查詢所有用戶-->
    <select id="findAll" resultType="user">
        select * from tb_user
    </select>
    <!--根據(jù)id查詢單個用戶-->
    <select id="findById" parameterType="int" resultType="user">
        select * from tb_user where user_id = #{
 userId}
    </select>
    <!--插入用戶-->
    <insert id="insert" parameterType="user">
        insert into tb_user (user_id,user_name,user_age) values (#{
 userId},#{
 userName},#{
 userAge})
    </insert>
    <!--更新用戶信息-->
    <update id="update" parameterType="map">
        update tb_user set user_name = #{
 userName}, user_age = #{
 userAge}
        where user_id = #{
 userId}
    </update>
    <!--刪除用戶-->
    <delete id="delete" parameterType="int">
        delete from tb_user where user_id = #{
 userId}
    </delete>
</mapper>

添加Mybatis實(shí)體映射配置:
在application.yml配置文件中增加mybatis部分的配置:

# Mybatis配置
# 開啟駝峰式命名規(guī)則自動轉(zhuǎn)換
mybatis:
  configuration:
    map-underscore-to-camel-case: true
  type-aliases-package: com.tracy.mybatisdemo.entity
  mapper-locations: classpath:mapper/*Mapper.xml

type-aliases-package: com.tracy.mybatisdemo.entity表示將UserMapper.xml中的resultType與com.tracy.mybatisdemo.entity包下的實(shí)體類綁定起來鞍盗,否則UserMapper.xml中的resultType需要寫上完整的包名com.tracy.mybatisdemo.entity.user需了。

  • mapper-locations: classpath:mapper/ Mapper.xml 表示將dao路徑下的各個接口與resources/mapper路徑下的各個xml文件映射起來,classpath等價于resources目錄般甲。

測試:
前面已經(jīng)演示過了肋乍,url和過程都是一模一樣的,請用postman或者別的測試工具自行測試吧敷存。

4.Mybatis的動態(tài)SQL
if 在 where 子句中做簡單的條件判斷墓造。

我們以UserMapper.xml中的update方法的實(shí)現(xiàn)為例:

原來的寫法:
當(dāng)我們調(diào)用這個接口時,必須把用戶名锚烦、用戶年齡參數(shù)都傳入觅闽,也就是說我們必須修改每一個屬性值。但是如果我們只想選擇性地修改屬性值呢涮俄,比如蛉拙,有時候我們只想修改user_name,有時候又只想修改user_age彻亲。

<!--更新用戶信息-->
    <update id="update" parameterType="map">
        update tb_user set user_name = #{
 userName}, user_age = #{
 userAge}
        where user_id = #{
 userId}
    </update>

使用if進(jìn)行動態(tài)SQL綁定:
我們?yōu)槊總€參數(shù)的傳入加上一個if判斷孕锄,test="user_name!=null"表明了它的判斷條件,只有當(dāng)該參數(shù)傳入不為空時才進(jìn)行修改苞尝,這就是一種動態(tài)綁定的策略畸肆。

<!--更新用戶信息-->
    <update id="update" parameterType="map">
        update tb_user set user_id = #{
 userId}
        <if test="userName!=null">
            user_name = #{
 userName}
        </if>
        <if test="userAge!=null">
            user_age = #{
 userAge}
        </if>
        where user_id = #{
 userId}
    </update>

(2)choose
相當(dāng)于java語言中的Switch語句。

仍以update方法為例:
每個when語句都是一個條件野来,第一個條件滿足了就跳出choose語句恼除,否則判斷下一個when條件。如果所有的when條件都不滿足,就直接選擇otherwise中的條件豁辉。

<!--更新用戶信息-->
    <update id="update" parameterType="map">
        update tb_user set
        <choose>
            <when test="userName!=null">
                user_name = #{
 userName}
            </when>
            <when test="userAge!=null">
                user_age = #{
 userAge}
            </when>
            <otherwise>
                user_id = #{
 userId}
            </otherwise>
        </choose>
        where user_id = #{
 userId}
    </update>

(3)trim令野、where、set
trim:
先來看看這個語句徽级,如果兩個if條件都不成立气破,那sql語句就會變成update tb_user set where user_id = #{userId},這就會導(dǎo)致語法上的錯誤:

<!--更新用戶信息-->
    <update id="update" parameterType="map">
        update tb_user set 
        <if test="userName!=null">
            user_name = #{
 userName}
        </if>
        <if test="userAge!=null">
            user_age = #{
 userAge}
        </if>
        where user_id = #{
 userId}
    </update>

使用trim語句餐抢,prefix表示整個trim語句的前綴是set现使,suffixOverrides屬性表示消除每個子句末尾可能會帶來的冗余符號(不冗余則不消除),prefixOverrides消除的是子句頭部的冗余:

<!--更新用戶信息-->
    <update id="update" parameterType="map">
        update tb_user
        <trim prefix="set" suffixOverrides=",">
            <if test="userName!=null">
                user_name = #{
 userName},
            </if>
            <if test="userAge!=null">
                user_age = #{
 userAge},
            </if>
        </trim>
        where user_id = #{
 userId}
    </update>

可使用專門的set語句:

<!--更新用戶信息-->
    <update id="update" parameterType="map">
        update tb_user
        <set>
            <if test="userName!=null">
                user_name = #{
 userName}
            </if>
            <if test="userAge!=null">
                user_age = #{
 userAge}
            </if>
            <if test="userId!=null">
                user_id = #{
 userId}
            </if>
        </set>
        where user_id = #{
 userId}
    </update>

可使用專門的where語句:
在where元素中至少有一個if子句成立旷痕;where元素能智能地處理 and 和 or 條件碳锈。

<!--更新用戶信息-->
    <update id="update" parameterType="map">
        update tb_user
        <trim prefix="set" suffixOverrides=",">
            <if test="userName!=null">
                user_name = #{
 userName},
            </if>
            <if test="userAge!=null">
                user_age = #{
 userAge},
            </if>
            <if test="userId!=null">
                user_id = #{
 userId},
            </if>
        </trim>
        <where>
            <if test="userId!=null">
                user_id = #{
 userId},
            </if>
        </where>

(4)foreach

當(dāng)需要對一個集合進(jìn)行遍歷時,foreach 元素是很有用的欺抗,尤其在 in 語句查詢時特別有用售碳。

(5)bind

二、整合 Mybatis 多數(shù)據(jù)源

如果開發(fā)人員配置了多個數(shù)據(jù)源绞呈,那么 Spring Boot 中 DataSource 和 Mybatis 的自動配置類將不會再生效贸人。

1.數(shù)據(jù)庫環(huán)境搭建

創(chuàng)建數(shù)據(jù)庫dabase1后執(zhí)行:

use `database1`;
-- ----------------------------
-- Table structure for teacher
-- ----------------------------
DROP TABLE IF EXISTS `teacher`;
CREATE TABLE `teacher` (
  `id` int(11) NOT NULL COMMENT '教師編號',
  `name` varchar(50) DEFAULT NULL COMMENT '教師姓名',
  `course` varchar(50) DEFAULT NULL COMMENT '所教課程',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

-- ----------------------------
-- Records of teacher
-- ----------------------------
INSERT INTO `teacher` VALUES ('1', 'teacher01', 'C語言');
INSERT INTO `teacher` VALUES ('2', 'teacher02', 'Java');

創(chuàng)建數(shù)據(jù)庫dabase2后執(zhí)行:

USE `database2`;
-- ----------------------------
-- Table structure for student
-- ----------------------------
DROP TABLE IF EXISTS `student`;
CREATE TABLE `student` (
  `id` int(11) NOT NULL COMMENT '學(xué)號',
  `name` varchar(50) DEFAULT NULL COMMENT '學(xué)生姓名',
  `age` int(11) DEFAULT NULL COMMENT '年齡',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

-- ----------------------------
-- Records of student
-- ----------------------------
INSERT INTO `student` VALUES ('1', 'student01', '20');
INSERT INTO `student` VALUES ('2', 'student02', '22');

2.實(shí)體類
在entity包下創(chuàng)建兩個實(shí)體類:

package com.tracy.mybatisdemo.entity;

import lombok.Data;

@Data
public class Teacher {
 
    private Integer id;
    private String name;
    /**
     * 所教課程
     */
    private String course;
}
package com.tracy.mybatisdemo.entity;

import lombok.Data;

@Data
public class Student {
 
    private Integer id;
    private String name;
    private Integer age;
}

3.在application.yml配置數(shù)據(jù)源

spring:
  datasource:
#    datasource01
    database1:
      driver-class-name: com.mysql.cj.jdbc.Driver
      jdbc-url: jdbc:mysql://localhost:3306/database1?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8
      username: root
      password: 
#    datasource01
    database2:
      driver-class-name: com.mysql.cj.jdbc.Driver
      jdbc-url: jdbc:mysql://localhost:3306/database2?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8
      username: root
      password:

4.配置類配置數(shù)據(jù)源

在config包下創(chuàng)建DatasourceConfig類:

package com.tracy.mybatisdemo.config;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;

import javax.sql.DataSource;

/**
 * 根據(jù)配置文件中的屬性值配置兩個數(shù)據(jù)源datasource01和datasource02
 */
@Configuration
public class DatasourceConfig {
 

    /**
     * 實(shí)例化數(shù)據(jù)源 datasource01
     */
    @Bean("datasource01")
    // 設(shè)置該數(shù)據(jù)源為默認(rèn)數(shù)據(jù)源
    @Primary
    // 以spring.datasource.database1為前綴的屬性值自動綁定到對應(yīng)的字段中
    @ConfigurationProperties(prefix = "spring.datasource.database1")
    public DataSource getDatasource01() {
 
        return DataSourceBuilder.create().build();
    }

    /**
     * 實(shí)例化數(shù)據(jù)源 datasource02
     */
    @Bean("datasource02")
    @ConfigurationProperties(prefix = "spring.datasource.database2")
    public DataSource getDatasource02() {
 
        return DataSourceBuilder.create().build();
    }
}

@Primary 注解指定默認(rèn)數(shù)據(jù)源,它是必要的佃声,否則會報錯艺智。

5.配置類配置 Mybatis

給每一個數(shù)據(jù)源都創(chuàng)建 SqlSessionFactory 和 SqlSession 。

數(shù)據(jù)源1:
在config包下創(chuàng)建SqlSessionConfig01類:

package com.tracy.mybatisdemo.config;

import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.SqlSessionTemplate;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;

import javax.sql.DataSource;

/**
 * 根據(jù)數(shù)據(jù)源datasource01配置sqlSessionFactory01和sqlSession01
 */
@Configuration
@MapperScan(basePackages = "com.tracy.mybatisdemo.dao.database1", sqlSessionFactoryRef = "sqlSessionFactory01")
public class SqlSessionConfig01 {
 

    /**
     * 向容器中實(shí)例化sqlSessionFactory01實(shí)例
     */
    @Bean("sqlSessionFactory01")
    // 設(shè)置為默認(rèn)SqlSessionFactory
    @Primary
    public SqlSessionFactory getSqlSessionFactory(
            // 根據(jù)名稱從容器中獲取實(shí)例
            @Qualifier("datasource01") DataSource dataSource) {
 
        try {
 
            // 實(shí)例化一個工具類圾亏,用來創(chuàng)建SqlSessionFactory
            SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
            factoryBean.setDataSource(dataSource);
            factoryBean.setMapperLocations(
                    // 設(shè)置Mybatis的xml文件位置
                    new PathMatchingResourcePatternResolver().getResources("classpath:mapper/database1/*.xml"));
            // 返回創(chuàng)建好的sqlSessionFactory實(shí)例
            return factoryBean.getObject();
        } catch (Exception e) {
 
            e.printStackTrace();
        }
        // 當(dāng)創(chuàng)建失敗時返回null
        return null;
    }

    /**
     * 向容器中實(shí)例化sqlSession01實(shí)例
     */
    @Bean("sqlSession01")
    // 設(shè)置為默認(rèn)SqlSession
    @Primary
    public SqlSessionTemplate getSqlSession(@Qualifier("sqlSessionFactory01") SqlSessionFactory sqlSessionFactory) {
 
        // 利用SqlSessionFactory實(shí)例構(gòu)建一個由SpringBoot管理的線程安全的SqlSession
        return new SqlSessionTemplate(sqlSessionFactory);
    }
}

在config包下創(chuàng)建SqlSessionConfig02類:

package com.tracy.mybatisdemo.config;

import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.SqlSessionTemplate;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;

import javax.sql.DataSource;

/**
 * 根據(jù)數(shù)據(jù)源datasource02配置sqlSessionFactory02和sqlSession02
 */
@Configuration
@MapperScan(basePackages = "com.tracy.mybatisdemo.dao.database2", sqlSessionFactoryRef = "sqlSessionFactory02")
public class SqlSessionConfig02 {
 

    /**
     * 向容器中實(shí)例化sqlSessionFactory02實(shí)例
     */
    @Bean("sqlSessionFactory02")
    // 設(shè)置為默認(rèn)SqlSessionFactory
    @Primary
    public SqlSessionFactory getSqlSessionFactory(
            // 根據(jù)名稱從容器中獲取實(shí)例
            @Qualifier("datasource02") DataSource dataSource) {
 
        try {
 
            // 實(shí)例化一個工具類十拣,用來創(chuàng)建SqlSessionFactory
            SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
            factoryBean.setDataSource(dataSource);
            factoryBean.setMapperLocations(
                    // 設(shè)置Mybatis的xml文件位置
                    new PathMatchingResourcePatternResolver().getResources("classpath:mapper/database2/*.xml"));
            // 返回創(chuàng)建好的sqlSessionFactory實(shí)例
            return factoryBean.getObject();
        } catch (Exception e) {
 
            e.printStackTrace();
        }
        // 當(dāng)創(chuàng)建失敗時返回null
        return null;
    }

    /**
     * 向容器中實(shí)例化sqlSession02實(shí)例
     */
    @Bean("sqlSession02")
    // 設(shè)置為默認(rèn)SqlSession
    @Primary
    public SqlSessionTemplate getSqlSession(@Qualifier("sqlSessionFactory02") SqlSessionFactory sqlSessionFactory) {
 
        // 利用SqlSessionFactory實(shí)例構(gòu)建一個由SpringBoot管理的線程安全的SqlSession
        return new SqlSessionTemplate(sqlSessionFactory);
    }
}

6.編寫 Dao接口和 SQL 映射文件

數(shù)據(jù)庫database1:
在dao包下創(chuàng)建文件夾database1,然后在database1下創(chuàng)建TeacherDao接口:

package com.tracy.mybatisdemo.dao.database1;

import com.tracy.mybatisdemo.entity.Teacher;
import org.springframework.stereotype.Repository;

import java.util.List;

@Repository
public interface TeacherDao {
 

    List<Teacher> findAll();
}

在 resources/mapper 下新建 database1 文件夾 召嘶, 在該文件夾下新建 Mapper 接口同名的映射文件即 TeacherMapper.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.tracy.mybatisdemo.dao.database1.TeacherDao">
    <!--查詢所有教師信息-->
    <select id="findAll" resultType="com.tracy.mybatisdemo.entity.Teacher">
        select * from teacher
    </select>
</mapper>

數(shù)據(jù)庫database2:
在dao包下創(chuàng)建文件夾database2父晶,然后在database2下創(chuàng)建StudentDao接口:

package com.tracy.mybatisdemo.dao.database2;

import com.tracy.mybatisdemo.entity.Student;
import org.springframework.stereotype.Repository;

import java.util.List;

@Repository
public interface StudentDao {
 

    List<Student> findAll();

}

在 resources/mapper 下新建 database2 文件夾 , 在該文件夾下新建 Mapper 接口同名的映射文件即 StudentMapper.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.tracy.mybatisdemo.dao.database2.StudentDao">
    <!--查詢所有學(xué)生信息-->
    <select id="findAll" resultType="com.tracy.mybatisdemo.entity.Student">
        select * from student
    </select>
</mapper>

7.編寫controller

在controller包下創(chuàng)建TestController類:

package com.tracy.mybatisdemo.controller;

import com.tracy.mybatisdemo.dao.database1.TeacherDao;
import com.tracy.mybatisdemo.dao.database2.StudentDao;
import com.tracy.mybatisdemo.entity.Student;
import com.tracy.mybatisdemo.entity.Teacher;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

@RestController
public class TestController {
 

    // 從容器中獲取teacherMapper(dataSource01操作)
    @Autowired
    private TeacherDao teacherDao;

    // 從容器中獲取studentMapper(dataSource02操作)
    @Autowired
    private StudentDao studentDao;

    /**
     * 從database1數(shù)據(jù)庫中查詢所有教師信息
     */
    @GetMapping("/teacher")
    public List<Teacher> findAllTeacher() {
 
        List<Teacher> teachers = teacherDao.findAll();
        return teachers;
    }

    /**
     * 從database2數(shù)據(jù)庫中查詢所有學(xué)生信息
     */
    @GetMapping("/student")
    public List<Student> findAllStudent() {
 
        List<Student> students = studentDao.findAll();
        return students;
    }

}

啟動項(xiàng)目弄跌,然后在瀏覽器中訪問http://localhost:8080/teacherhttp://localhost:8080/student甲喝。

image.png
image.png

三、整合分頁插件 PageHelper

Mybatis 內(nèi)部其實(shí)提供了分頁功能铛只。實(shí)現(xiàn)原理是將數(shù)據(jù)一次性查詢到內(nèi)存中埠胖,再進(jìn)行切分,從而實(shí)現(xiàn)分頁淳玩,是一種邏輯分頁方式直撤。當(dāng)數(shù)據(jù)量過大的時候,一次性讀取數(shù)據(jù)對數(shù)據(jù)庫和程序的性能都有很大的影響蜕着,因此這種方式不推薦使用谋竖。

而PageHelper 插件是一種物理分頁方式红柱。其實(shí)現(xiàn)原理是在執(zhí)行查詢的時候,獲取頁面參數(shù)蓖乘,通過攔截器在 SQL 語句中添加分頁參數(shù)生成分頁 SQL锤悄, 最終實(shí)現(xiàn)分頁查詢。

1.搭建數(shù)據(jù)庫嘉抒、項(xiàng)目配置

在mysql中創(chuàng)建數(shù)據(jù)庫page零聚,然后執(zhí)行:
USE `page`;
DROP TABLE IF EXISTS `tb_user`;
CREATE TABLE `tb_user`  (
  `user_id` int(11) NOT NULL AUTO_INCREMENT,
  `user_name` varchar(20) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `user_age` int(11) NULL DEFAULT NULL,
  PRIMARY KEY (`user_id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Compact;

-- ----------------------------
-- Records of tb_user
-- ----------------------------
INSERT INTO `tb_user` VALUES (1, 'user01', 18);
INSERT INTO `tb_user` VALUES (2, 'user02', 19);
INSERT INTO `tb_user` VALUES (3, 'user03', 18);
INSERT INTO `tb_user` VALUES (4, 'user04', 19);
INSERT INTO `tb_user` VALUES (5, 'user05', 18);
INSERT INTO `tb_user` VALUES (6, 'user06', 19);
INSERT INTO `tb_user` VALUES (7, 'user07', 18);
INSERT INTO `tb_user` VALUES (8, 'user08', 19);
INSERT INTO `tb_user` VALUES (9, 'user09', 18);
INSERT INTO `tb_user` VALUES (10, 'user10', 19);
INSERT INTO `tb_user` VALUES (11, 'user11', 18);
INSERT INTO `tb_user` VALUES (12, 'user12', 19);
INSERT INTO `tb_user` VALUES (13, 'user13', 18);
INSERT INTO `tb_user` VALUES (14, 'user14', 19);
INSERT INTO `tb_user` VALUES (15, 'user15', 18);
INSERT INTO `tb_user` VALUES (16, 'user16', 19);
INSERT INTO `tb_user` VALUES (17, 'user17', 18);
INSERT INTO `tb_user` VALUES (18, 'user18', 19);
INSERT INTO `tb_user` VALUES (19, 'user19', 18);
INSERT INTO `tb_user` VALUES (20, 'user20', 19);
在啟動類上配置包掃描:
@MapperScan("com.tracy.mybatisdemo.dao")
在application.yml中配置:
# 數(shù)據(jù)源
spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/page?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8
    username: root
    password: 你的密碼

# Mybatis配置
# 開啟駝峰式命名規(guī)則自動轉(zhuǎn)換
mybatis:
  configuration:
    map-underscore-to-camel-case: true
2.添加依賴

在pom.xml中添加以下依賴:

<!--PageHelper分頁插件-->
<dependency>
    <groupId>com.github.pagehelper</groupId>
    <artifactId>pagehelper-spring-boot-starter</artifactId>
    <version>1.4.3</version>
</dependency>
3.在代碼中使用PageHelper
(1)entity

在entity包下創(chuàng)建User類:

package com.tracy.mybatisdemo.entity;

import lombok.Data;

// 添加Getter,Setter些侍,toString等方法
@Data
public class User {
 
    private Integer userId;
    private String userName;
    private Integer userAge;
}

在dao包下創(chuàng)建UserDao接口:

這里為了簡化使用注解綁定sql隶症。

package com.tracy.mybatisdemo.dao;

import com.tracy.mybatisdemo.entity.User;
import org.apache.ibatis.annotations.Select;
import org.springframework.stereotype.Repository;

import java.util.List;

@Repository
public interface UserDao {
 
    @Select("select user_id,user_name,user_age from tb_user")
    List<User> findAll();
}
(3)controller

在controller包下創(chuàng)建UserController類:

package com.tracy.mybatisdemo.controller;

import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.tracy.mybatisdemo.dao.UserDao;
import com.tracy.mybatisdemo.entity.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

@RestController
@RequestMapping("/user")
public class UserController {
 
    @Autowired
    private UserDao userDao;

    @GetMapping("/findAll")
    public PageInfo<User> findAll(){
 
        //startPage靜態(tài)方法,傳遞兩個參數(shù)(當(dāng)前頁碼岗宣,每頁查詢條數(shù))
        PageHelper.startPage(1,3);
        //緊跟著的第一個select 方法會被分頁
        List<User> list=userDao.findAll();
        PageInfo pageInfo = new PageInfo(list);
        return pageInfo;
    }
}
(4)測試

啟動項(xiàng)目蚂会,然后在瀏覽器訪問http://localhost:8080/user/findAll

頁面上出現(xiàn):

[{
 "userId":1,"userName":"user01","userAge":18},{
 "userId":2,"userName":"user02","userAge":19},{
 "userId":3,"userName":"user03","userAge":18}]

成功!

(5)返回類PageInfo

此類是插件里封裝好的類狈定,可以了解一下:

public class PageInfo<T> implements Serializable {
 
private static final long serialVersionUID = 1L;
//當(dāng)前頁
private int pageNum;
//每頁的數(shù)量
private int pageSize;
//當(dāng)前頁的數(shù)量
private int size;
//由于startRow 和endRow 不常用颂龙,這里說個具體的用法
//可以在頁面中"顯示startRow 到endRow 共size 條數(shù)據(jù)"
//當(dāng)前頁面第一個元素在數(shù)據(jù)庫中的行號
private int startRow;
//當(dāng)前頁面最后一個元素在數(shù)據(jù)庫中的行號
private int endRow;
//總記錄數(shù)
private long total;
//總頁數(shù)
private int pages;
//結(jié)果集
private List<T> list;
//前一頁
private int prePage;
//下一頁
private int nextPage;
//是否為第一頁
private boolean isFirstPage = false;
//是否為最后一頁
private boolean isLastPage = false;
//是否有前一頁
private boolean hasPreviousPage = false;
//是否有下一頁
private boolean hasNextPage = false;
//導(dǎo)航頁碼數(shù)
private int navigatePages;
//所有導(dǎo)航頁號
private int[] navigatepageNums;
//導(dǎo)航條上的第一頁
private int navigateFirstPage;
//導(dǎo)航條上的最后一頁
private int navigateLastPage;
}

四、整合 Mybatis-Plus

Mybatis-Plus (簡稱 MP )是由國內(nèi) baomidou 組織開源的 Mybatis 的增強(qiáng)工具纽什。在原生 Mybatis 的基礎(chǔ)上只做增強(qiáng)不做改變,為簡化開發(fā)躲叼,提高效率而生芦缰。

在使用過程中,MP 提供了一套通用的 Mapper 和 Service 操作枫慷,只需要繼承接口让蕾,進(jìn)行簡單的配置便可以進(jìn)行單表的 CRUD 操作。對于一些復(fù)雜的查詢或听,提供了使用數(shù)據(jù)庫字段和 POJO 屬性兩種方式來構(gòu)造條件進(jìn)行查詢探孝。此外,它自帶樂觀鎖誉裆、性能分析插件顿颅、代碼生成器和物理分頁插件等特色功能。

1.數(shù)據(jù)庫搭建足丢、配置

數(shù)據(jù)庫搭建和上一章一模一樣粱腻。

配置application.yml:

# 數(shù)據(jù)源
spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/page?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8
    username: root
    password: 

# MyBatis-Plus的設(shè)置
# 別名包掃描路徑,為路徑下的所有類創(chuàng)建別名
mybatis-plus:
  type-aliases-package: com.tracy.mybatisdemo.entity
  # xml掃描路徑。然后在Mapper接口寫上自定義方法并關(guān)聯(lián)XML語句斩跌,即可實(shí)現(xiàn)手寫SQL
  mapper-locations: classpath*:mapper/*.xml
  # MyBatis-Plus駝峰轉(zhuǎn)換绍些,配置后不論手寫SQL還是接口方法,都能自動映射(默認(rèn)on)
  configuration:
    map-underscore-to-camel-case: on
  # 配置生成SQL日志
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

2.添加依賴

<!--Mybatis-Plus啟動器-->
<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-boot-starter</artifactId>
    <version>3.4.1</version>
</dependency>

3.代碼實(shí)現(xiàn)

com.tracy.mybatisdemo目錄下需要5個包:entity耀鸦、dao柬批、service、controller、config氮帐。resources目錄下需要有mapper文件夾嗅虏。

(1)entity

創(chuàng)建一個 User 實(shí)體類:

package com.tracy.mybatisdemo.entity;

import lombok.Data;
import com.baomidou.mybatisplus.annotation.*;


// 建立實(shí)體類和數(shù)據(jù)庫表之間的對應(yīng)關(guān)系
@TableName("tb_user")
// 添加getter,setter揪漩,toString等方法
@Data
public class User {
 
    // 用于標(biāo)識數(shù)據(jù)庫表的主鍵字段旋恼,MP 默認(rèn)數(shù)據(jù)庫表中名為 id 的字段是主鍵,如若不是奄容,需通過該注解進(jìn)行標(biāo)識冰更。
    // type = IdType.AUTO 表示數(shù)據(jù)庫主鍵自增
    @TableId(value = "user_id", type = IdType.AUTO)
    private Integer id;
    
    // 建立實(shí)體類字段和數(shù)據(jù)庫表屬性之間的對應(yīng)關(guān)系,當(dāng)兩者相同時可省略該注解昂勒。
    @TableField
    private String userName;

    private Integer userAge;

}

在 MP 中蜀细,自動支持實(shí)體類字段按照駝峰轉(zhuǎn)下劃線形式的進(jìn)行轉(zhuǎn)換。
創(chuàng)建UserDao接口:


package com.tracy.mybatisdemo.dao;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.tracy.mybatisdemo.entity.User;
import org.springframework.stereotype.Repository;


@Repository
// 繼承Mybatis-Plus提供的BaseMapper戈盈,提供基礎(chǔ)的CRUD及分頁方法
public interface UserDao extends BaseMapper<User> {
 
}

BaseMapper:

image.png

Wrapper是一個條件構(gòu)造器奠衔,作用就是幫我們寫 SQL 語句中 where 字段后的那部分內(nèi)容。

若通用方法無法滿足業(yè)務(wù)需求塘娶,你可以在 Mapper 接口中添加自定義方法归斤,同時在 XML 中添加 SQL ,與傳統(tǒng) Mybatis 的寫法一致刁岸。

(3)service

  • 接口:

在service包下創(chuàng)建UserService接口:

package com.tracy.mybatisdemo.service;

import com.baomidou.mybatisplus.extension.service.IService;
import com.tracy.mybatisdemo.entity.User;

public interface UserService extends IService<User> {
 
}

繼承 MP 提供的 IService 接口:

image.png

為了避免和 Mapper 接口中的方法混淆脏里,Service 層中的方法命名和 Mapper 有些區(qū)別。

增加:insert → save

刪除:delete → remove

更新:udpate → update

查詢: select → get虹曙,list

  • 實(shí)現(xiàn):

在service包下創(chuàng)建UserServiceImpl類:

package com.tracy.mybatisdemo.service;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.tracy.mybatisdemo.dao.UserDao;
import com.tracy.mybatisdemo.entity.User;
import org.springframework.stereotype.Service;

@Service
public class UserServiceImpl extends ServiceImpl<UserDao, User> implements UserService{
 
}

在 ServiceImpl 類中迫横,它會獲取泛型參數(shù)中的 UserDao 接口和 User 類,利用這兩者來封裝 Service 層的操作:


image.png
(4)config

配置分頁插件
在config包下新建 MybatisPlusConfig配置類:

package com.tracy.mybatisdemo.config;

import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
@MapperScan("com.tracy.mybatisdemo.dao") //配置dao包掃描
public class MybatisPlusConfig {
 
    /**
     * 添加分頁插件
     */
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
 
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        // 新的分頁插件,一緩和二緩遵循mybatis的規(guī)則
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
        return interceptor;
    }
}

分頁插件配置完成后酝碳,通過如下方式便可進(jìn)行分頁:

Page<User> userPage = new Page<>();
// 設(shè)置當(dāng)前頁
userPage.setCurrent(pageNum);
// 設(shè)置頁面大小
userPage.setSize(pageSize);
// 方式1.無條件分頁查詢
Page<User> page = userService.page(userPage);
// 方式2.條件分頁查詢
Page<User> pageByWrapper = userService.page(userPage,new LambdaQueryWrapper<User>() .isNotNull(User::getUserName));
條件構(gòu)造器

針對復(fù)雜 SQL 矾踱,可以采用條件構(gòu)造器構(gòu)造條件。

QueryWapper

針對 QueryWapper 疏哗,它使用數(shù)據(jù)庫 Column 來構(gòu)造條件呛讲,在編譯期間無法檢查出錯誤。


// 構(gòu)建一個條件構(gòu)造器
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
// 查詢名字不為空且年齡大于18的用戶沃斤,使用數(shù)據(jù)庫字段
queryWrapper
    .isNotNull("user_name")
    .ge("user_age",18);
// 條件查詢
List<User> users = userService.list(queryWrapper);
LambdaQueryWrapper

針對 LambdaQueryWrapper 圣蝎,它使用 POJO 對象字段來構(gòu)造條件,可以在程序編譯的時候就能發(fā)現(xiàn)錯誤衡瓶。

// 構(gòu)建一個條件構(gòu)造器
LambdaQueryWrapper<User> lambdaWrapper = new LambdaQueryWrapper<>();
// 查詢名字不為空且年齡大于18的用戶徘公,使用實(shí)體類字段
lambdaWrapper
        .isNotNull(User::getUserName)
        .ge(User::getUserAge,18);
// 條件查詢
List<User> users = userService.list(lambdaWrapper);

(5)controller

在controller包下創(chuàng)建:

package com.tracy.mybatisdemo.controller;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;


import com.tracy.mybatisdemo.entity.User;
import com.tracy.mybatisdemo.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import java.util.List;

@RestController
@RequestMapping("/user")
public class UserController {
 
    @Autowired
    public UserService userService;

    /**
     * 查詢所有-list()方法
     */
    @GetMapping("/list")
    public String list() {
 
        List<User> list = userService.list();
        return list.toString();
    }

    /**
     * 根據(jù)年齡查詢用戶
     */
    @GetMapping("/queryByAge")
    public String queryByAge(Integer age) {
 
        // 查詢名字不為空且年齡大于給定年齡的用戶
        // 條件查詢方式1:使用QueryWrapper,使用數(shù)據(jù)庫字段
        List<User> list = userService.list(new QueryWrapper<User>().isNotNull("user_name").ge("user_age", age));
        // 條件查詢方式2:使用LambdaQueryWrapper,使用POJO字段
        List<User> list1 = userService
                .list(new LambdaQueryWrapper<User>().isNotNull(User::getUserName).ge(User::getUserAge, age));
        // 條件查詢方式3:使用鏈?zhǔn)絨uery,使用數(shù)據(jù)庫字段
        List<User> list2 = userService.query().isNotNull("user_name").ge("user_age", age).list();
        // 條件查詢方式4:使用鏈?zhǔn)絣ambdaquery,使用POJO字段
        List<User> list3 = userService.lambdaQuery().isNotNull(User::getUserName).ge(User::getUserAge, age).list();
        // 只返回其中一種方式的查詢結(jié)果
        return list.toString();
    }

    /**
     * 添加用戶-save()
     */
    @PostMapping("/save")
    public boolean save(String userName, Integer userAge) {
 
        User user = new User();
        user.setUserName(userName);
        user.setUserAge(userAge);
        return userService.save(user);
    }

    /**
     * 刪除用戶-removeById()
     */
    @DeleteMapping("/remove")
    public boolean remove(Integer userId) {
 
        return userService.removeById(userId);
    }

    /**
     * 更新用戶-updateById()
     */
    @PutMapping("/update")
    public boolean update(User user) {
 
        // 注意,參數(shù)是一個對象
        return userService.updateById(user);
    }

    /**
     * 分頁查詢
     */
    @GetMapping("/page")
    public Page<User> page(Integer pageNum, Integer pageSize) {
 
        Page<User> userPage = new Page<>();
        // 設(shè)置當(dāng)前頁
        userPage.setCurrent(pageNum);
        // 設(shè)置頁面大小
        userPage.setSize(pageSize);
        // 方式1.無條件分頁查詢
        Page<User> page = userService.page(userPage);
        // 方式2.條件分頁查詢
        Page<User> pageByWrapper = userService.page(userPage,
                new LambdaQueryWrapper<User>().isNotNull(User::getUserName));
        return page;
    }

}

運(yùn)行項(xiàng)目后請自行在postman中測試每個方法哮针,不再贅述关面。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末坦袍,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子等太,更是在濱河造成了極大的恐慌捂齐,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,372評論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件缩抡,死亡現(xiàn)場離奇詭異奠宜,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)瞻想,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,368評論 3 392
  • 文/潘曉璐 我一進(jìn)店門压真,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人蘑险,你說我怎么就攤上這事滴肿。” “怎么了佃迄?”我有些...
    開封第一講書人閱讀 162,415評論 0 353
  • 文/不壞的土叔 我叫張陵泼差,是天一觀的道長。 經(jīng)常有香客問我呵俏,道長堆缘,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,157評論 1 292
  • 正文 為了忘掉前任普碎,我火速辦了婚禮套啤,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘随常。我一直安慰自己,他們只是感情好萄涯,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,171評論 6 388
  • 文/花漫 我一把揭開白布绪氛。 她就那樣靜靜地躺著,像睡著了一般涝影。 火紅的嫁衣襯著肌膚如雪枣察。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,125評論 1 297
  • 那天燃逻,我揣著相機(jī)與錄音序目,去河邊找鬼。 笑死伯襟,一個胖子當(dāng)著我的面吹牛猿涨,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播姆怪,決...
    沈念sama閱讀 40,028評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼叛赚,長吁一口氣:“原來是場噩夢啊……” “哼澡绩!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起俺附,我...
    開封第一講書人閱讀 38,887評論 0 274
  • 序言:老撾萬榮一對情侶失蹤肥卡,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后事镣,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體步鉴,經(jīng)...
    沈念sama閱讀 45,310評論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,533評論 2 332
  • 正文 我和宋清朗相戀三年璃哟,在試婚紗的時候發(fā)現(xiàn)自己被綠了氛琢。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,690評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡沮稚,死狀恐怖艺沼,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情蕴掏,我是刑警寧澤障般,帶...
    沈念sama閱讀 35,411評論 5 343
  • 正文 年R本政府宣布,位于F島的核電站盛杰,受9級特大地震影響挽荡,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜即供,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,004評論 3 325
  • 文/蒙蒙 一定拟、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧逗嫡,春花似錦青自、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至抹锄,卻和暖如春逆瑞,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背伙单。 一陣腳步聲響...
    開封第一講書人閱讀 32,812評論 1 268
  • 我被黑心中介騙來泰國打工获高, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人吻育。 一個月前我還...
    沈念sama閱讀 47,693評論 2 368
  • 正文 我出身青樓念秧,卻偏偏與公主長得像,于是被迫代替她去往敵國和親扫沼。 傳聞我的和親對象是個殘疾皇子出爹,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,577評論 2 353

推薦閱讀更多精彩內(nèi)容