Mybatis

環(huán)境:

JDK1.8噪矛,Mysql5.7医清,maven3.6.1起暮,IDEA

回顧:

JDBC,Mysql会烙,java基礎(chǔ)负懦,Maven,junit

SSM框架:

配置文件:最好的方式:看官網(wǎng)文檔:https://mybatis.org/mybatis-3/

1.簡(jiǎn)介:

1.1 什么是Mybatis

  • MyBatis 是一款優(yōu)秀的持久層框架柏腻。
  • 它支持自定義 SQL纸厉、存儲(chǔ)過程以及高級(jí)映射。
  • MyBatis 免除了幾乎所有的 JDBC 代碼以及設(shè)置參數(shù)和獲取結(jié)果集的工作五嫂。
  • MyBatis 可以通過簡(jiǎn)單的 XML 或注解來配置和映射原始類型颗品、接口和 Java POJO(Plain Old Java Objects肯尺,普通老式 Java 對(duì)象)為數(shù)據(jù)庫(kù)中的記錄。
  • MyBatis本是apache的一個(gè)開源項(xiàng)目iBatis躯枢,2010年這個(gè)項(xiàng)目由apache software foundation遷移到了google code则吟,并且改名為MyBatis。2013年11月遷移到Github锄蹂。
獲取Mybatis:
  • maven倉(cāng)庫(kù):
<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis</artifactId>
    <version>3.5.7</version>
</dependency>

1.2 持久化

數(shù)據(jù)持久化

  • 持久化就是將程序的數(shù)據(jù)在持久狀態(tài)和瞬時(shí)狀態(tài)轉(zhuǎn)化的過程氓仲。
  • 內(nèi)存:斷電即失
  • 數(shù)據(jù)庫(kù)(jdbc),io文件持久化

為什么需要持久化得糜?

  • 有一些對(duì)象敬扛,不能讓他丟掉。
  • 內(nèi)存太貴

1.3 持久層

  • 完成持久化工作的代碼塊
  • 層界限十分明顯掀亩。

1.4 為什么需要Mybatis舔哪?

  • 幫助程序員把數(shù)據(jù)存入到數(shù)據(jù)庫(kù)中
  • 傳統(tǒng)JDBC代碼太復(fù)雜了欢顷,簡(jiǎn)化槽棍、框架、自動(dòng)化
  • ...

2.第一個(gè)Mybatis程序

思路:搭建環(huán)境--導(dǎo)入Mybatis--編寫代碼--測(cè)試

2.1 搭建環(huán)境

數(shù)據(jù)庫(kù):

CREATE DATABASE `mybatis`;
USE `mybatis`;

CREATE TABLE `user`(
`id` INT(20) NOT NULL PRIMARY KEY,
`name` VARCHAR(30) DEFAULT NULL,
`pwd` VARCHAR(30) DEFAULT NULL
)ENGINE=INNODB DEFAULT CHARSET=utf8;

INSERT INTO `user`(`id`,`name`,`pwd`)VALUES
(1,'張三','123456'),
(2,'李四','123456'),
(3,'王五','123456')

新建項(xiàng)目:
1.普通maven項(xiàng)目


注意位置 不要用系統(tǒng)自帶的

2.刪除src目錄
3.導(dǎo)入maven依賴

    <!--  導(dǎo)入依賴  -->
    <dependencies>
        <!--  mysql驅(qū)動(dòng)  -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.27</version>
        </dependency>
        <!--  mybatis  -->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.5.7</version>
        </dependency>
        <!--  junit  -->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.13.2</version>
            <scope>test</scope>
        </dependency>
    </dependencies>

2.1 創(chuàng)建一個(gè)模塊

  • 編寫mybatis的核心配置文件

  • 編寫mybatis的工具類
    在resources下配置mybatis-config.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!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="com.mysql.cj.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=true&amp;characterEncoding=UTF-8&amp;useUnicode=true&amp;serverTimezone=GMT""/>
                <property name="username" value="root"/>
                <property name="password" value="123456"/>
            </dataSource>
        </environment>
    </environments>
</configuration>
從 XML 中構(gòu)建 SqlSessionFactory抬驴,從 SqlSessionFactory 中獲取 SqlSession

在util下配置com.qwe.util.MybatisUtils

import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

import java.io.IOException;
import java.io.InputStream;

//SqlSessionFactory  -->sqlSession
public class MybatisUtils {
    private static SqlSessionFactory sqlSessionFactory;

    static {
        try {
            //使用Mybatis第一步:獲取sqlSessionFactory對(duì)象
            //這里出錯(cuò)后面會(huì)空指針異常
            String resource = "mybatis-config.xml";
            InputStream inputStream = Resources.getResourceAsStream(resource);
            sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    //既然有了 SqlSessionFactory炼七,顧名思義,我們可以從中獲得 SqlSession 的實(shí)例布持。
    // SqlSession 提供了在數(shù)據(jù)庫(kù)執(zhí)行 SQL 命令所需的所有方法豌拙。你可以通過 SqlSession 實(shí)例來直接執(zhí)行已映射的 SQL 語(yǔ)句。
    public static SqlSession getSqlSession() {
        return sqlSessionFactory.openSession();
    }

}

2.3 編寫代碼

  • 實(shí)體類
  • Dao接口
  • 接口實(shí)現(xiàn)類
探究已映射的 SQL 語(yǔ)句

一個(gè)語(yǔ)句既可以通過 XML 定義题暖,也可以通過注解定義按傅。事實(shí)上 MyBatis 提供的所有特性都可以利用基于 XML 的映射語(yǔ)言來實(shí)現(xiàn),這使得 MyBatis 在過去的數(shù)年間得以流行胧卤。這里給出一個(gè)基于 XML 映射語(yǔ)句的示例唯绍,它應(yīng)該可以滿足上個(gè)示例中 SqlSession 的調(diào)用。

<?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="org.mybatis.example.BlogMapper">
  <select id="selectBlog" resultType="Blog">
    select * from Blog where id = #{id}
  </select>
</mapper>
  • 實(shí)體類 com.qwe.pojo.User
public class User {
    private int id;
    private String name;
    private String pwd;

    public User() { }

    public User(int id, String name, String pwd) {
        this.id = id;
        this.name = name;
        this.pwd = pwd; }

    public int getId() {
        return id; }

    public void setId(int id) {
        this.id = id; }

    public String getName() {
        return name; }

    public void setName(String name) {
        this.name = name; }

    public String getPwd() {
        return pwd; }

    public void setPwd(String pwd) {
        this.pwd = pwd; }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", pwd='" + pwd + '\'' +
                '}'; }
}
  • Dao接口 com.qwe.dao.UserDao
import com.qwe.pojo.User;
import java.util.List;

public interface UserDao {
    //獲取UserList
    List<User> getUserList();
}
  • 接口實(shí)現(xiàn)類 由原來的UserDaoImpl轉(zhuǎn)變?yōu)橐粋€(gè)Mapper配置文件 UserMapper.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">
<!-- namespace綁定一個(gè)對(duì)應(yīng)的Dao/Mapper接口 -->
<mapper namespace="com.qwe.dao.UserDao">//必須指定命名空間
    <!--  查詢語(yǔ)句 id原來的方法名字 type類型 map集合-->
    <select id="getUserList" resultType="com.qwe.pojo.User">
    select * from mybatis.user
  </select>
</mapper>

2.4 測(cè)試

注意點(diǎn):
1 org.apache.ibatis.binding.BindingException:Type interface com.*.dao.UserDao is not known to the MapperRegistry
解決方法:mybatis-config.xml

<!--  每一個(gè)Mapper.xml文件都需要在Mybatis核心配置文件中注冊(cè)枝誊!  -->
    <mappers>
        <mapper resource="com/qwe/dao/UserMapper.xml"/>
    </mappers>

2 java.io.IOException: Could not find resource org/mybatis/example/mybatis-config.xml
解決辦法:maven中約定大于配置 在父項(xiàng)目和子項(xiàng)目pom.xml文件加入

 <!--在build中配置resources况芒,來防止我們資源導(dǎo)出失敗的問題-->
    <build>
        <resources>
            <resource>
                <directory>src/main/resources</directory>
                <includes>
                    <include>**/*.properties</include>
                    <include>**/*.xml</include>
                </includes>
                <filtering>true</filtering>
            </resource>
            <resource>
                <directory>src/main/java</directory>
                <includes>
                    <include>**/*.properties</include>
                    <include>**/*.xml</include>
                </includes>
                <filtering>true</filtering>
            </resource>
        </resources>
    </build>
  • junit測(cè)試
public class UserDaoTest {
    @Test
    public void test(){
        //第一步:獲取SqlSession對(duì)象
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        //執(zhí)行SQL
        //方式一:getMapper
        UserDao userDao = sqlSession.getMapper(UserDao.class);
        List<User> userList = userDao.getUserList();

        //方式二:以前的方法
//        List<User> userList = sqlSession.selectList("com.qwe.dao.UserDao.getUserList");
        
        for (User user:userList){
            System.out.println(user);
        }

        //關(guān)閉SqlSession
        sqlSession.close();
    }
}

步驟:
1 在pom.xml中導(dǎo)入mysql和mybatis的jar包
2 需要一個(gè)sqlSesionFactory對(duì)象創(chuàng)建了mybatis-config.xml
3 建造工具類MybatisUtils
4 實(shí)體類里接口UserDao,UserMapper.xml代替實(shí)現(xiàn)類

作用域(Scope)

SqlSessionFactoryBuilder

這個(gè)類可以被實(shí)例化叶撒、使用和丟棄绝骚,一旦創(chuàng)建了 SqlSessionFactory,就不再需要它了祠够。

SqlSessionFactory

SqlSessionFactory 一旦被創(chuàng)建就應(yīng)該在應(yīng)用的運(yùn)行期間一直存在压汪。 最簡(jiǎn)單的就是使用單例模式或者靜態(tài)單例模式。

SqlSession

SqlSession 的實(shí)例不是線程安全的古瓤,因此是不能被共享的止剖,所以它的最佳的作用域是請(qǐng)求或方法作用域。 換句話說,每次收到 HTTP 請(qǐng)求滴须,就可以打開一個(gè) SqlSession舌狗,返回一個(gè)響應(yīng)后,就關(guān)閉它扔水。 一個(gè)確保 SqlSession 關(guān)閉的標(biāo)準(zhǔn)模式:

try (SqlSession session = sqlSessionFactory.openSession()) {
  // 你的應(yīng)用邏輯代碼
}

3.增刪改(必須要提交事務(wù))

1.namespace

namespace中的包名要和接口Dao/Mapper的包名一致

2.select

選擇痛侍,查詢語(yǔ)句

  • id:就是對(duì)應(yīng)的namespace中的方法名
  • resultType:sql語(yǔ)句執(zhí)行的返回值
  • parameterType:參數(shù)類型
3.Insert

1 編寫接口 UserMapper

    //增加用戶
    int addUser(User user);

2 編寫對(duì)應(yīng)的mapper中的sql語(yǔ)句 UserMapper.xml

    <insert id="addUser" parameterType="com.qwe.pojo.User" >
        insert into mybatis.user (id,name,pwd) values(#{id},#{name},#{pwd});
    </insert>

3 測(cè)試 UserMapperTest

    @Test
    public void addUser(){
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        int res = mapper.addUser(new User(4, "哈哈", "123456"));
        if (res>0){
            System.out.println("插入成功");
        }
        //提交事務(wù)
        sqlSession.commit();
        sqlSession.close();
    }
4.Update

1 編寫接口

    //修改用戶
    int updateUser(User user);

2 編寫對(duì)應(yīng)的mapper中的sql語(yǔ)句

    <update id="updateUser"  parameterType="com.qwe.pojo.User">
        update mybatis.user set name=#{name},pwd=#{pwd} where id=#{id};
    </update>

3 測(cè)試

    @Test
    public void updateUser(){
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        mapper.updateUser(new User(4,"趙四","123456"));
        sqlSession.commit();
        sqlSession.close();

    }
5.Delete

1 編寫接口

    //刪除用戶
     int deleteUser(int id);

2 編寫對(duì)應(yīng)的mapper中的sql語(yǔ)句

    <delete id="deleteUser" parameterType="int">
        delete from mybatis.user where id =#{id};
    </delete>

3 測(cè)試

    @Test
    public void deleteUser(){
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        mapper.deleteUser(4);
        sqlSession.commit();
        sqlSession.close();
    }
6.萬能的Map

假設(shè)實(shí)體類或數(shù)據(jù)庫(kù)中的表、字段或者參數(shù)過多魔市,應(yīng)當(dāng)考慮Map

    //萬能的map
    int addUser2(Map<String,Object> map);
    <insert id="addUser2" parameterType="map">
        insert into mybatis.user(id,pwd) values(#{userid},#{password});
    </insert>
    @Test
    public void addUser2(){
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        Map<String,Object> map = new HashMap<String, Object>();
        map.put("userid",4);
        map.put("password","123456");
        mapper.addUser2(map);
        sqlSession.close();
    }

Map傳遞參數(shù)主届,直接在sql中取出key即可 parameterType="map"
對(duì)象傳遞參數(shù),直接在sql中取對(duì)象的屬性即可 parameterType="Object"
只有一個(gè)基本類型參數(shù)的情況下待德,可以直接在sql中取到
多個(gè)參數(shù)用Map或者注解

7.模糊查詢
    List<User> getUserLike(String value);
    <select id="getUserLike" resultType="com.qwe.pojo.User">
        select * from mybatis.user where name like "%"#{value }"%"
    </select>
    @Test
    public void getUserLike(){
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        List<User> userList = mapper.getUserLike("王");
        for (User user : userList) {
            System.out.println(user);
        }
        sqlSession.close();
    }

1 java代碼執(zhí)行的時(shí)候君丁,傳遞通配符% %

 List<User> userList = mapper.getUserLike("%王%");

2 在sql拼接中使用通配符

select * from mybatis.user where name like "%"#{value }"%"

4.配置解析

4.1 核心文件配置

  • mybatis-config.xml
  • MyBatis的配置文件包括了慧深深影響MyBatis行為的設(shè)置和屬性信息。
    配置文檔的頂層結(jié)構(gòu)如下:
    configuration(配置)
  • properties(屬性)
  • settings(設(shè)置)
  • typeAliases(類型別名)
  • typeHandlers(類型處理器)
  • objectFactory(對(duì)象工廠)
  • plugins(插件)
  • environments(環(huán)境配置)
    * environment(環(huán)境變量)
    * transactionManager(事務(wù)管理器)
    * dataSource(數(shù)據(jù)源)
  • databaseIdProvider(數(shù)據(jù)庫(kù)廠商標(biāo)識(shí))
  • mappers(映射器)

4.2環(huán)境配置(environments)

MyBatis 可以配置成適應(yīng)多種環(huán)境
不過要記捉堋:盡管可以配置多個(gè)環(huán)境绘闷,但每個(gè) SqlSessionFactory 實(shí)例只能選擇一種環(huán)境。

environments 元素定義了如何配置環(huán)境较坛。

<environments default="development">
  <environment id="development">
    <transactionManager type="JDBC">
      <property name="..." value="..."/>
    </transactionManager>
    <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>

注意一些關(guān)鍵點(diǎn):
1 默認(rèn)使用的環(huán)境 ID(比如:default="development")印蔗。
2 每個(gè) environment 元素定義的環(huán)境 ID(比如:id="development")。
3 事務(wù)管理器的配置(比如:type="JDBC")丑勤。
4 數(shù)據(jù)源的配置(比如:type="POOLED")华嘹。
默認(rèn)環(huán)境和環(huán)境 ID 顧名思義。 環(huán)境可以隨意命名法竞,但務(wù)必保證默認(rèn)的環(huán)境 ID 要匹配其中一個(gè)環(huán)境 ID耙厚。

事務(wù)管理器(transactionManager)
在 MyBatis 中有兩種類型的事務(wù)管理器(也就是 type="[JDBC|MANAGED]"):

  • JDBC – 這個(gè)配置直接使用了 JDBC 的提交和回滾設(shè)施,它依賴從數(shù)據(jù)源獲得的連接來管理事務(wù)作用域岔霸。 (默認(rèn))
  • MANAGED – 這個(gè)配置幾乎沒做什么. 默認(rèn)情況下它會(huì)關(guān)閉連接薛躬。然而一些容器并不希望連接被關(guān)閉。
    如果你正在使用 Spring + MyBatis秉剑,則沒有必要配置事務(wù)管理器泛豪,因?yàn)?Spring 模塊會(huì)使用自帶的管理器來覆蓋前面的配置。

數(shù)據(jù)源(dataSource)
連接數(shù)據(jù)庫(kù)
有三種內(nèi)建的數(shù)據(jù)源類型(也就是 type="[UNPOOLED|POOLED|JNDI]"):

  • UNPOOLED– 這個(gè)數(shù)據(jù)源的實(shí)現(xiàn)會(huì)每次請(qǐng)求時(shí)打開和關(guān)閉連接侦鹏。但是性能要求不高诡曙。(沒有池)
    池:用完可以回收
  • POOLED– 這種數(shù)據(jù)源的實(shí)現(xiàn)利用“池”的概念將 JDBC 連接對(duì)象組織起來,避免了創(chuàng)建新的連接實(shí)例時(shí)所必需的初始化和認(rèn)證時(shí)間略水。能使并發(fā) Web 應(yīng)用快速響應(yīng)請(qǐng)求价卤。(默認(rèn))
  • JNDI

Mybatis默認(rèn)的事務(wù)管理器就是JDBC,連接池:POOLED
學(xué)會(huì)實(shí)用配置多套運(yùn)行環(huán)境渊涝。

4.3屬性(properties)

我們可以通過properties屬性來實(shí)現(xiàn)引用配置文件慎璧。
這些屬性可以在外部進(jìn)行配置床嫌,并可以進(jìn)行動(dòng)態(tài)替換。你既可以在典型的 Java 屬性文件中配置這些屬性胸私,也可以在 properties 元素的子元素中設(shè)置厌处。【db.properties】

編寫一個(gè)配置文件
db.properties

driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/mybatis?useSSL=true&characterEncoding=UTF-8&useUnicode=true&serverTimezone=GMT
username=root
password=123456

在xml中岁疼,所有標(biāo)簽都可以規(guī)定其順序:properties阔涉,settings,typeAliases捷绒,typeHandlers瑰排,objectWraperFactory,reflectorFactory暖侨,plugins椭住,environment,databaseldProvider字逗,mappers

在核心配置文件中引入

    <properties resource="db.properties">
        <property name="password" value="123456"/>
    </properties>
  • 可以直接引入外部文件
  • 可以直接在其中增加屬性配置
  • 如果db.properties和核心配置文件中有同一字段京郑,優(yōu)先使用外部配置文件

4.4類型別名(typeAliases)

  • 類型別名是為java類型設(shè)置一個(gè)短的名字。
  • 存在的意義僅在于用來減少類完全限定名的冗余扳肛。
    mybatis-config.xml
    <!--  可以給實(shí)體類起別名  -->
    <typeAliases>
        <typeAlias type="com.qwe.pojo.User" alias="User"/>
    </typeAliases>

UserMapper.xml

    <select id="getUserList" resultType="User">
    select * from mybatis.user
  </select>
  • 也可以指定一個(gè)包名傻挂,MyBatis 會(huì)在包名下面搜索需要的 Java Bean,掃描實(shí)體類的包挖息,它的默認(rèn)別名就為這個(gè)類的類名,首字母小寫兽肤。
    mybatis-config.xml
    <typeAliases>
        <package name="com.qwe.pojo"/>
    </typeAliases>

UserMapper.xml

    <select id="getUserList" resultType="user">
    select * from mybatis.user
  </select>
  • 在實(shí)體類比較少的時(shí)候套腹,使用第一種方式,如果實(shí)體類十分多资铡,建議使用第二種电禀。第一種可以DIY別名,第二種不行笤休,如果非要改可以在實(shí)體類上面通過注解起別名尖飞。
@Alias("hello")
public class Author {
    ...
}

4.5設(shè)置

這是 MyBatis 中極為重要的調(diào)整設(shè)置,它們會(huì)改變 MyBatis 的運(yùn)行時(shí)行為店雅。

設(shè)置名 描述值 有效值 默認(rèn)值
logImpl 指定 MyBatis 所用日志的具體實(shí)現(xiàn)政基,未指定時(shí)將自動(dòng)查找。 SLF4J / LOG4J(deprecated since 3.5.9)/ LOG4J2/ JDK_LOGGING/ COMMONS_LOGGING /STDOUT_LOGGING / NO_LOGGING 未設(shè)置
cacheEnabled 全局性地開啟或關(guān)閉所有映射器配置文件中已配置的任何緩存闹啦。 true /false true
lazyLoadingEnabled 延遲加載的全局開關(guān)沮明。當(dāng)開啟時(shí),所有關(guān)聯(lián)對(duì)象都會(huì)延遲加載窍奋。 特定關(guān)聯(lián)關(guān)系中可通過設(shè)置 fetchType 屬性來覆蓋該項(xiàng)的開關(guān)狀態(tài)荐健。 true /false false

4.6其他配置

  • typeHandlers(類型處理器)
  • objectFactroy(對(duì)象工廠)
  • plugins(插件)
    * mybatis-generator-core
    * mybatis-plus
    * 通用mapper

4.7映射器(mappers)

需要告訴 MyBatis 到哪里去找到這些語(yǔ)句酱畅。最好的辦法是直接告訴 MyBatis 到哪里去找映射文件颓鲜。
MapperRegistry:注冊(cè)綁定我們的Mapper文件课锌;
方式一:使用相對(duì)于類路徑的資源引用

    <mappers>
        <mapper resource="com/qwe/dao/UserMapper.xml"/>
    </mappers>

方式二:使用映射器接口實(shí)現(xiàn)類的完全限定類名

    <mappers>
        <mapper class="com.qwedao.UserMapper"/>
    </mappers>

注意點(diǎn):

  • 接口和他的Mapper配置文件必須同名
  • 接口和他的Mapper配置文件必須在同一個(gè)包下

方式三:將包內(nèi)的映射器接口實(shí)現(xiàn)全部注冊(cè)為映射器

    <mappers>
        <package name="com.qwe.dao"/>
    </mappers>

注意點(diǎn):

  • 接口和他的Mapper配置文件必須同名
  • 接口和他的Mapper配置文件必須在同一個(gè)包下

4.8生命周期和作用域

生命周期和作用域是至關(guān)重要的缤言,因?yàn)殄e(cuò)誤的使用會(huì)導(dǎo)致非常嚴(yán)重的并發(fā)問題锦积。

流程圖

SqlSessionFactoryBulider:

  • 一旦創(chuàng)建了SqlSessionFactory既琴,就不再需要它了
  • 局部變量

SqlSessionFactory:

  • 可以想象為:數(shù)據(jù)庫(kù)連接池
  • SqlSessionFactory一旦被創(chuàng)建就應(yīng)該在應(yīng)用的運(yùn)行期間一直存在瓮孙,沒有任何理由丟棄它或者重新創(chuàng)建另一個(gè)實(shí)例卖漫。
  • 因此SqlSessionFactory的最佳作用域是應(yīng)用作用域捞魁。

SqlSession:

  • 連接到連接池的一個(gè)請(qǐng)求
  • SqlSession 的實(shí)例不是線程安全的在张,因此是不能被共享的用含,所以它的最佳的作用域是請(qǐng)求或方法作用域。
  • 用完之后需要趕緊關(guān)閉帮匾,否則資源被占用


    需要關(guān)閉的原因

    這里的每一個(gè)Mapper啄骇,就代表一個(gè)具體的業(yè)務(wù)。

5.解決屬性名和字段名不一致的問題

5.1問題

數(shù)據(jù)庫(kù)中的字段


數(shù)據(jù)庫(kù)中的字段

新建一個(gè)項(xiàng)目瘟斜,拷貝之前的缸夹,測(cè)試實(shí)體類字段不一致的情況
User.java

public class User {
    private int id;
    private String name;
    private String password;//原來是pwd

測(cè)試結(jié)果:User{id=1, name='張三', password='null'}
解決方法:

  • 起別名
    UserMapper.xml
    <select id="getUserById" parameterType="int" resultType="User">
    select id,name,pwd as password from mybatis.user where id =#{id};
    </select>

4.2resultMap

結(jié)果集映射
UserMapper.xml

    <!--  結(jié)果集映射  -->
    <resultMap id="UserMap" type="User">
        <!-- column數(shù)據(jù)庫(kù)中的字段,property實(shí)體類中的屬性  -->
        <result column="id" property="id"/>
        <result column="name" property="name"/>
        <result column="pwd" property="password"/>
    </resultMap>
    <select id="getUserById" resultMap="UserMap">
    select * from mybatis.user where id =#{id};
    </select>
  • resultMap元素是MyBatis中最重要最強(qiáng)大的元素
  • ResultMap的設(shè)計(jì)思想是螺句,對(duì)于簡(jiǎn)單的語(yǔ)句根本不需要配置顯式的結(jié)果映射虽惭,而對(duì)于復(fù)雜一點(diǎn)的語(yǔ)句只需要描述他們的關(guān)系就行了。
  • ResultMap最優(yōu)秀的地方在于蛇尚,雖然你已經(jīng)對(duì)它相當(dāng)了解了芽唇,但是根本就不需要顯式的用到他們。

6.日志

6.1 日至工廠

如果一個(gè)數(shù)據(jù)庫(kù)操作出現(xiàn)了異常取劫,我們需要排錯(cuò)匆笤,日志就是最好的助手。
![logImpl](https://upload-images.jianshu.io/upload_images
/27766806-953d0bf381a080ff.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

  • SLF4J
  • LOG4J【掌握】
  • LOG4J2
  • JDK_LOGGING
  • STDOUT_LOGGING【掌握】
  • NO_LOGGING
    在Mybatis中具體使用哪一個(gè)日志實(shí)現(xiàn)谱邪,在設(shè)置中設(shè)置炮捧。
    STDOUT_LOGGING標(biāo)準(zhǔn)日志輸出

mybatis-config.xml

    <settings>
        <setting name="logImpl" value="STDOUT_LOGGING"/>
    </settings>
運(yùn)行

6.2Log4j

什么是Log4j?

  • Log4j是Apache的一個(gè)開源項(xiàng)目惦银,通過使用Log4j咆课,我們可以控制日志信息輸送的目的地是控制臺(tái)、文件扯俱、GUI組件
  • 我們也可以控制每一條日志的輸出格式
  • 通過定義每一條日志信息的級(jí)別书蚪,我們能夠更加細(xì)致地控制日志的生成過程。
  • 通過一個(gè)配置文件來靈活地進(jìn)行配置蘸吓,而不需要修改應(yīng)用的代碼善炫。

1.先導(dǎo)入Log4j的包

<dependency>
    <groupId>log4j</groupId>
    <artifactId>log4j</artifactId>
    <version>1.2.17</version>
</dependency>

2.log4.properties

#將等級(jí)為DEBUG的日志信息輸出到console和file這兩個(gè)目的地,console和file的定義在下面的代碼
log4j.rootLogger = DEBUG,console,file

#控制臺(tái)輸出的相關(guān)設(shè)置
log4j.appender.console=org.apache.log4j.ConsoleAppender
log4j.appender.console.Target=System.out
log4j.appender.console.Threshole=DEBUG
log4j.appender.console.layout=org.apache.log4j.PatternLayout
log4j.appender.console.layout.ConversionPattern=[%c]-%m%n

#文件輸出的相關(guān)設(shè)置
log4j.appender.file=org.apache.log4j.RollingFileAppender
log4j.appender.file.File=./log/qwe.log
log4j.appender.file.MaxFileSize=10mb
log4j.appender.file.Threshold=DEBUG
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=[%p][%d{yy-MM-dd}][%c]%m%n

#日志輸出級(jí)別
log4j.logger.org.mybatis=DEBUG
log4j.logger.java.sql=DEBUG
log4j.logger.java.sql.Statement=DEBUG
log4j.logger.java.sql.ResultSet=DEBUG
log4j.logger.java.sql.PreparedStatement=DEBUG

3.配置log4j為日志的實(shí)現(xiàn)

    <settings>
        <setting name="logImpl" value="LOG4J"/>
    </settings>

4.log4j的使用:直接測(cè)試運(yùn)行剛才的查詢



簡(jiǎn)單使用
1.在要使用Log4j的類中库继,導(dǎo)入包import org.apache.log4j.Logger;
2.日志對(duì)象箩艺,參數(shù)為當(dāng)前類的class

static Logger logger = Logger.getLogger(UserDaoTest.class);

3.日志級(jí)別

        logger.info("info:進(jìn)入了testLog4j");
        logger.debug("debug:進(jìn)入了testLog4j");
        logger.error("error:進(jìn)入了testLog4j");

7.分頁(yè)

7.1 使用Limit分頁(yè)

SELECT * from user limit startIndex,pagesize;//從第幾個(gè)開始窜醉,每頁(yè)顯示幾個(gè)
//SELECT * from user limit 2; #[0,2]

使用Mybatis實(shí)現(xiàn)分頁(yè)艺谆,核心SQL
1.接口

    //分頁(yè)
    List<User> getUserByLimit(Map<String,Integer> map);

2.Mapper.xml

    <select id="getUserByLimit" parameterType="map" resultMap="UserMap">
        select * from mybatis.user limit #{startindex},#{pagesize}
    </select>

3.測(cè)試

    @Test
    public void getUserByLimit(){
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        HashMap<String, Integer> map = new HashMap<String, Integer>();
        map.put("startindex",0);
        map.put("pagesize",2);
        List<User> userList = mapper.getUserByLimit(map);
        for (User user : userList) {
            System.out.println(user);
        }
        sqlSession.close();
    }

7.2 RowBounds分頁(yè)

1.接口

    //分頁(yè)2
    List<User> getUserByRowBounds();

2.Mapper.xml

    <select id="getUserByRowBounds" resultMap="UserMap">
        select * from mybatis.user
    </select>

3.測(cè)試

   @Test
   public void getUserByRowBounds(){
       SqlSession sqlSession = MybatisUtils.getSqlSession();

      //RowBounds實(shí)現(xiàn)
       RowBounds rowBounds = new RowBounds(1, 2);

       //通過java代碼層面實(shí)現(xiàn)分頁(yè)
       List<User> userList = sqlSession.selectList("com.qwe.dao.UserMapper.getUserByRowBounds",null,rowBounds);
       for (User user : userList) {
           System.out.println(user);
       }
       sqlSession.close();
   }

7.3分頁(yè)插件

分頁(yè)插件

8.使用注解開發(fā)

8.1面向接口編程


8.2 使用注解開發(fā)

1.注解在接口上實(shí)現(xiàn)

public interface UserMapper {
    @Select("select * from user")
    List<User> getUsers();
}

2.需要在核心配置文件中綁定接口榨惰!

    <mappers>
        <mapper class="com.qwe.dao.UserMapper"/>
    </mappers>

3.測(cè)試

    @Test
    public void test(){
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        //底層主要應(yīng)用反射
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        List<User> users = mapper.getUsers();
        for (User user : users) {
            System.out.println(user);
        }
        sqlSession.close();
    }

本質(zhì):反射機(jī)制實(shí)現(xiàn)
底層:動(dòng)態(tài)代理


代理

Mybatis詳細(xì)的流程
配置文件-工具類-實(shí)現(xiàn)類-測(cè)試類
Resources獲取加載全局配置文件--->實(shí)例化SqlSessionFactoryBuilder構(gòu)造器--->解析配置文件流XMLConfigBuilder--->Configuration所有的配置信息--->SqlSessionFactory實(shí)例化--->transaction事務(wù)管理--->創(chuàng)建executor執(zhí)行器--->創(chuàng)建sqlSession--->實(shí)現(xiàn)CRUD(有可能回滾到事務(wù)管理器)--->查看是否執(zhí)行成功(不成功回滾)--->提交事務(wù)--->關(guān)閉

8.3 CRUD

我們可以在工具類創(chuàng)建的時(shí)候?qū)崿F(xiàn)自動(dòng)提交事務(wù)!
MybatisUtils.java

    public static SqlSession getSqlSession() {
        return sqlSessionFactory.openSession(true);
    }

編寫接口静汤,增加注釋

public interface UserMapper {
    @Select("select * from user")
    List<User> getUsers();

    //方法存在多個(gè)參數(shù)琅催,所有的參數(shù)前面必須加上@Param("id")注解
    @Select("select * from user where id =#{id}")
    User getUserByID(@Param("id") int id);

    @Insert("insert into user(id,name,pwd) values(#{id},#{name},#{password})")
    int addUser(User user);

    @Update("update user set name=#{name},pwd=#{password} where id = #{id}")
    int updateUser(User user);

    @Delete("delete from user where id = #{id}")
    int deleteUser(@Param("id") int id);
}

測(cè)試類
注意:我們必須要將接口注冊(cè)綁定到我們的核心配置文件中!

public class UserMapperTest {
    @Test
    public void test(){
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        //底層主要應(yīng)用反射
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
//        List<User> users = mapper.getUsers();
//        for (User user : users) {
//            System.out.println(user);
//        }

//        User userByID = mapper.getUserByID(1);
//        System.out.println(userByID);

//        mapper.addUser(new User(5,"hello","123456"));

//        mapper.updateUser(new User(5,"aaa","123456"));

        mapper.deleteUser(5);

        sqlSession.close();
    }
}

關(guān)于@Param()注解
  • 基本類型的參數(shù)或者String類型虫给,需要加上
  • 引用類型不需要加
  • 如果只有一個(gè)基本類型的話藤抡,可以忽略,但是建議大家都加上
  • 我們?cè)赟QL中引用的就是我們這里的@Param("id")中設(shè)定的屬性名

9 Lombok

使用步驟:
1.在IDEA中安裝Lombok插件



2.在項(xiàng)目中導(dǎo)入lombok的jar包

<!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.18.22</version>
    <scope>provided</scope>
</dependency>

3.在實(shí)體類上加lombok注解

@Getter and @Setter
@FieldNameConstants 字段屬性常量
@ToString 
@EqualsAndHashCode
@AllArgsConstructor, 全部的構(gòu)造參數(shù)@RequiredArgsConstructor and @NoArgsConstructor無參構(gòu)造
@Log, @Log4j, @Log4j2, @Slf4j, @XSlf4j, @CommonsLog, @JBossLog, @Flogger, @CustomLog
@Data
@Builder
@SuperBuilder
@Singular
@Delegate
@Value
@Accessors鏈?zhǔn)讲僮?@Wither
@With
@SneakyThrows

@Data:無參構(gòu)造抹估,get缠黍、set、tostring药蜻、hashcode瓷式、equals
@AllArgsConstructor
@NoArgsConstructor
@EqualsAndHashCode
@ToString

4.優(yōu)缺點(diǎn)
優(yōu)點(diǎn):

  • 能通過注解的形式自動(dòng)生成構(gòu)造器,getter/setter语泽、equals贸典。hashcode、toString等方法踱卵,提高了一定的開發(fā)效率
  • 讓代碼變得簡(jiǎn)潔廊驼,不用過多的去關(guān)注相應(yīng)的方法
  • 屬性做修改時(shí),也簡(jiǎn)化了維護(hù)為這些屬性所生成的getter/setter方法等
    缺點(diǎn):
  • 不支持多種參數(shù)構(gòu)造器的重載
  • 雖然省去了手動(dòng)創(chuàng)建getter/setter方法的麻煩颊埃,但大大降低了源代碼的可讀性和完整性蔬充,降低了閱讀源代碼的舒適度。

10.多對(duì)一處理

多對(duì)一:

  • 多個(gè)學(xué)生班利,對(duì)應(yīng)一個(gè)老師
  • 對(duì)于學(xué)生這邊而言,多個(gè)學(xué)生關(guān)聯(lián)一個(gè)老師榨呆。多對(duì)一
  • 對(duì)于老師而言罗标,集合,一個(gè)老師有很多學(xué)生积蜻。一對(duì)多

SQL:

CREATE TABLE `teacher`(
`id`INT(10)NOT NULL,
`name`VARCHAR(30)DEFAULT NULL,
PRIMARY KEY(`id`)
)ENGINE=INNODB DEFAULT CHARSET=utf8
INSERT INTO teacher(`id`,`name`)VALUES(1,'秦老師');

CREATE TABLE `student`(
`id`INT(10)NOT NULL,
`name`VARCHAR(30)DEFAULT NULL,
`tid` INT(10)DEFAULT NULL,
KEY`fkid`(`tid`),
CONSTRAINT`fktid`FOREIGN KEY(`tid`)REFERENCES`teacher`(`id`)
)ENGINE=INNODB DEFAULT CHARSET=utf8
INSERT INTO `student`(`id`,`name`,`tid`)VALUES('1','小明','1');
INSERT INTO `student`(`id`,`name`,`tid`)VALUES('2','小紅','1');
INSERT INTO `student`(`id`,`name`,`tid`)VALUES('3','小張','1');
INSERT INTO `student`(`id`,`name`,`tid`)VALUES('4','小李','1');
INSERT INTO `student`(`id`,`name`,`tid`)VALUES('5','小王','1');

測(cè)試環(huán)境搭建

1.導(dǎo)入lombok

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.22</version>
            <scope>provided</scope>
        </dependency>

2.新建實(shí)體類Teacher闯割,Student

@Data
public class Teacher {
    private int id;
    private String name;

}

@Data
public class Student {
    private int id;
    private String name;

    //學(xué)生需要關(guān)聯(lián)一個(gè)老師
    private Teacher teacher;
}

3.建立Mapper接口

public interface TeacherMapper {
    @Select("select * from teacher where id = #{tid}")
    Teacher getTeacher (@Param("tid") int id);
}
public interface StudentMapper {
}

4.建立Mapper.xml文件

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.qwe.dao.TeacherMapper">
</mapper>
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.qwe.dao.StudentMapper">
</mapper>

5.在核心配置文件中綁定注冊(cè)我們的Mapper接口或者文件

<mappers>
    <mapper class="com.qwe.dao.TeacherMapper"/>
    <mapper class="com.qwe.dao.StudentMapper"/>
</mappers>

6.測(cè)試查詢是否能夠成功

public class MyTest {
    public static void main(String[] args) {

        SqlSession sqlSession = MybatisUtils.getSqlSession();
        TeacherMapper mapper = sqlSession.getMapper(TeacherMapper.class);
        Teacher teacher = mapper.getTeacher(1);
        System.out.println(teacher);
        sqlSession.close();
    }
}

按照查詢嵌套處理

<!--  思路:查詢所有學(xué)生信息,根據(jù)查詢出來的學(xué)生的tid尋找對(duì)應(yīng)的老師  -->
    <select id="getStudent" resultMap="StudentTeacher">
        select * from student;
    </select>
    <resultMap id="StudentTeacher" type="Student">
        <result property="id" column="id"/>
        <result property="name" column="name"/>
<!--    復(fù)雜屬性竿拆,我們需要單獨(dú)處理     對(duì)象:association  集合:collection    -->
        <association property="teacher" column="tid" javaType="Teacher" select="getTeacher"/>
    </resultMap>
    <select id="getTeacher" resultType="Teacher">
        select * from teacher where id = #{id}
    </select>

按照結(jié)果嵌套處理

    <select id="getStudent2" resultMap="StudentTeacher2">
        select s.id sid,s.name sname,t.name tname
        from student s,teacher t
        where s.tid =t.id
    </select>
    <resultMap id="StudentTeacher2" type="Student">
        <result property="id" column="sid"/>
        <result property="name" column="sname"/>
        <association property="teacher" javaType="Teacher">
            <result property="name" column="tname"/>
        </association>
    </resultMap>
回顧Mysql多對(duì)一查詢方式:
  • 子查詢
  • 聯(lián)表查詢

11.一對(duì)多處理

比如:一個(gè)老師擁有多個(gè)學(xué)生宙拉!
對(duì)于老師而言,就是一對(duì)多的關(guān)系丙笋。

環(huán)境搭建(同多對(duì)一)

實(shí)體類

@Data
public class Student {
    private int id;
    private String name;
    //學(xué)生只有一個(gè)老師
    private int tid;
}
@Data
public class Teacher {
    private int id;
    private String name;
    //一個(gè)老師擁有多個(gè)學(xué)生
    private List<Student> students;
}

    <!--  按結(jié)果嵌套查詢  -->
    <select id="getTeacher" resultMap="TeacherStudent">
        select s.id sid,s.name sname,t.name tname,t.id tid
        from student s,teacher t
        where s.tid=t.id and t.id=#{tid}
    </select>
    <resultMap id="TeacherStudent" type="Teacher">
        <result property="id" column="tid"/>
        <result property="name" column="tname"/>
<!--負(fù)載的屬性我們需要單獨(dú)處理 對(duì)象association 集合collection
    javaType ="" 指定屬性的類型谢澈!
    集合中的泛型信息煌贴,我們使用ofType獲取
     -->
        <collection property="students" ofType="Student">
            <result property="id" column="sid"/>
            <result property="name" column="sname"/>
            <result property="tid" column="tid"/>
        </collection>
    </resultMap>
    
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市锥忿,隨后出現(xiàn)的幾起案子牛郑,更是在濱河造成了極大的恐慌,老刑警劉巖敬鬓,帶你破解...
    沈念sama閱讀 218,122評(píng)論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件淹朋,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡钉答,警方通過查閱死者的電腦和手機(jī)础芍,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,070評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來数尿,“玉大人仑性,你說我怎么就攤上這事∑龃矗” “怎么了虏缸?”我有些...
    開封第一講書人閱讀 164,491評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)嫩实。 經(jīng)常有香客問我刽辙,道長(zhǎng),這世上最難降的妖魔是什么甲献? 我笑而不...
    開封第一講書人閱讀 58,636評(píng)論 1 293
  • 正文 為了忘掉前任宰缤,我火速辦了婚禮,結(jié)果婚禮上晃洒,老公的妹妹穿的比我還像新娘慨灭。我一直安慰自己,他們只是感情好球及,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,676評(píng)論 6 392
  • 文/花漫 我一把揭開白布氧骤。 她就那樣靜靜地躺著,像睡著了一般吃引。 火紅的嫁衣襯著肌膚如雪筹陵。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,541評(píng)論 1 305
  • 那天镊尺,我揣著相機(jī)與錄音朦佩,去河邊找鬼。 笑死庐氮,一個(gè)胖子當(dāng)著我的面吹牛语稠,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播弄砍,決...
    沈念sama閱讀 40,292評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼仙畦,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼输涕!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起议泵,我...
    開封第一講書人閱讀 39,211評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤占贫,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后先口,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體型奥,經(jīng)...
    沈念sama閱讀 45,655評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,846評(píng)論 3 336
  • 正文 我和宋清朗相戀三年碉京,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了厢汹。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,965評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡谐宙,死狀恐怖烫葬,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情凡蜻,我是刑警寧澤搭综,帶...
    沈念sama閱讀 35,684評(píng)論 5 347
  • 正文 年R本政府宣布,位于F島的核電站划栓,受9級(jí)特大地震影響兑巾,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜忠荞,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,295評(píng)論 3 329
  • 文/蒙蒙 一蒋歌、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧委煤,春花似錦堂油、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,894評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至讥邻,卻和暖如春寓免,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背计维。 一陣腳步聲響...
    開封第一講書人閱讀 33,012評(píng)論 1 269
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留撕予,地道東北人鲫惶。 一個(gè)月前我還...
    沈念sama閱讀 48,126評(píng)論 3 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像实抡,于是被迫代替她去往敵國(guó)和親欠母。 傳聞我的和親對(duì)象是個(gè)殘疾皇子欢策,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,914評(píng)論 2 355

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