首页 > 其他分享 >Mybatis(b战狂神版笔记)

Mybatis(b战狂神版笔记)

时间:2023-03-01 09:47:02浏览次数:50  
标签:缓存 name 战狂 神版 mybatis sqlSession Mybatis id select

Mybatis

1. 简介

Mybatis是什么?

  1. MyBatis 是一款优秀的持久层框架
  2. 它支持自定义 SQL、存储过程以及高级映射
  3. MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。
  4. MyBatis本是apache的一个开源项目iBatis,2010年这个项目由apache software foundation迁移到了google code,并且改名为MyBatis。
  5. 2013年11月迁移到Github。

Mybatis获取

  1. maven仓库:https://mvnrepository.com/search?q=Mybatis
<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis</artifactId>
    <version>3.5.6</version>
</dependency>
  1. Github:https://github.com/mybatis/mybatis-3/releases
  2. 中文文档:https://mybatis.net.cn/getting-started.html

持久化

  • 数据持久化。将程序的数据在持久状态和瞬时状态转化的过程。
  • 内存:断电即失
  • 数据库(jdbc),io文件持久化

持久层

Dao层,Service层,Controller层...

  • 完成持久化工作的代码块就是持久层.
  • 层与层之间界限明显

Mybatis学习优点

  • 将数据存入数据库
  • 方便
  • sql与代码分离,提高维护性
  • 提供映射标签,支持对象与数据库的orn字段关系映射
  • 提供对象关系映射标签,支持对象关系组建维护
  • 提供xml标签,支持编写动态sql

2.第一个Mybatis程序

思路:搭建环境-->导入Mybaits-->编写代码--->测试

2.1搭建环境

  1. 搭建数据库

建立数据库表

  1. 新建Maven项目
    • 创建maven项目
    • 删除src目录
    • 导入依赖
      依赖

3.创建一个模块

  1. 编写mybatis的配置文件

    <?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.jdbc.Driver"/>
                     <property name="url" value="${jdbc:mysql://localhost:3306}"/>
                     <property name="username" value="${root}"/>
                     <property name="password" value="${1094148867g}"/>
                 </dataSource>
             </environment>
         </environments>
    
     </configuration>
    
  2. 编写mybatis工具类


//sqlSessionFactory
    //用来得到sqlSession
public class MybatisUtils {

    private static SqlSessionFactory sqlSessionFactory;
    static {

        //1.获取sqlSessionFactory对象
        String resource = "mybatis-cofig.xml";
        InputStream inputStream = null;
        try {
            inputStream = Resources.getResourceAsStream(resource);
        } catch (IOException e) {
            e.printStackTrace();
        }
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

        /*另一种不读取xml配置文件,只用java代码来生成SqlSessionFactory实例
        XML 文件等价的配置项。

DataSource dataSource = BlogDataSourceFactory.getBlogDataSource();
TransactionFactory transactionFactory = new JdbcTransactionFactory();
Environment environment = new Environment("development", transactionFactory, dataSource);
Configuration configuration = new Configuration(environment);
configuration.addMapper(BlogMapper.class);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(configuration);
         */

    }

    //2.从sQLSessionFactory中获取SqlSession对象

    public static SqlSession getSqlSession(){

        SqlSession sqlSession = sqlSessionFactory.openSession();

        return sqlSession;
    }

}
  1. 编写代码

    • 实体类
    public class User {
        private int id;
        private String name;
        private String pwd;
    
        public User(int id, String name, String pwd) {
            this.id = id;
            this.name = name;
            this.pwd = pwd;
        }
    
        public User() {
        }
    
        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接口
    public interface UserDao {
     List<User> getUserList();
     }
    
    • Dao接口实现类(xml)
      • 配置xml文件,由原来的UserDaoimpl转变为一个Mapping配置文件
        <?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绑定一个对应的DaoMapper接口-->
        <mapper namespace="com.peng.dao.UserDao">
        
            <!--select 查询语句所要作的sql操作。id=定位要使用sql的方法名。
            还可以在其后添加返回值,常用的又resultType和resultMap,前者是返回指定类型,但是需要
            映射类型地址,也就是所返返回值的dao对象-->
            <select id="getUserList" resultType="com.peng.pojo.user">
                select * from mybatis.user
            </select>
        </mapper>
        

测试

  1. 每一个Mapper(Dao接口实现类)都要在mybtis-config.xml中注册
    注意点:
    org.apache.ibatis.binding.BindingException: Type interface com.peng.dao.UserDao is not known to the MapperRegistry.

注册Mapper

  1. 有由于Maven中一般约定大于配置,所以可能出现配置文件(*Mapper.xml)找不到,需要在pom.xml中建立导入过滤
     <build>
         <resources>
             <resource>
                 <directory>src/main/resources</directory>
                 <includes>
                     <include>**/*.properties</include>
                     <include>**/*.xml</include>
                 </includes>
    
             </resource>
             <resource>
                 <directory>src/main/java</directory>
                 <includes>
                     <include>**/*.properties</include>
                     <include>**/*.xml</include>
                 </includes>
    
             </resource>
         </resources
    

3. CRUD

  1. namespace : namespace中的包名要和Dao/mapper接口包名一致
    namespace名称

  2. select : 选择,查询语句,该标签含有的参数有:

    • id:就是对应的namespace中的方法名称
    • resultType/resultMap:Sql语句返回值,赋值为指定类的项目绝对地址
    • parameterType:参数类型
    1. 编写接口
      接口编写
    2. 编写对应的mapper中的sql语句
      Mapper中的sql语句
    3. sqlSession调用
      select
    4. 注意:对表本身的修改(增删改)要提交。
  3. insert

  4. update

  5. delete

4.万能的Map

假如2,我们的实体类或者数据库中的表现,字段或者参数过多,我们应当考虑使用Map

    //万能Map
    int addUser2(Map map);
 <insert id="addUser2" parameterType="map">
        insert into mybatis.user (id,name,pwd) values(#{userid},#{username},#{userpwd});
    </insert>
 @Test
    public void test3_1(){

        SqlSession sqlSession = MybatisUtils.getSqlSession();

        UserDao mapper = sqlSession.getMapper(UserDao.class);

        Map<String, Object> map = new HashMap<String,Object>();

        /*
         用map来传递参数,
         好处,在修改时不用创建User对象,因为当该对象的字段过多时就很繁琐。
         可以直接用map来创建待修改的字段

         注意:
         map的key值时与UserMapper.xml中的对应方法的字段名相同的。
         */
        map.put("userid",5);
        map.put("username","heihei");
        map.put("userpwd","13414515");

        mapper.addUser2(map);

        //提交是事务
        sqlSession.commit();

        sqlSession.close();
    }
  • Map传递参数直接在sql中取出key即可。
  • 对象传递参数,直接在sql中取对象的属性即可。
  • 只有一个基本类型参数的情况下,可以直接在sql中取到。
  • 多个参数用Map,或者注解。

5.模糊查询

  1. java代码执行的时候,传递通配符%
  2. 在sql拼接中使用通配符.
    public void test6(){
    
         //第一步:获取SqlSession对象
         SqlSession sqlSession= MybatisUtils.getSqlSession();
         //第二步:执行SQL
         //方式一.getMapper,获取UserDao对象
         UserDao userDaoImpl = sqlSession.getMapper(UserDao.class);
    
         List<User> userList = userDaoImpl.getUserLike("%李%");
         //通配符要在java代码中体现,而非在Mapper中体现。
    
         for (User user : userList) {
             System.out.println(user);
         }
    
         //第三步:关闭sqlSession
         sqlSession.close();
     }
    
      <select id="getUserLike">
        select * from mybatis.user where name like ”%“#{value}"%"
    </select>
    

4.配置解析

1.核心配置文件

  • configuration(配置)
    • properties(属性)
    • settings(设置)
    • typeAliases(类型别名)
    • typeHandlers(类型处理器)
    • objectFactory(对象工厂)
    • plugins(插件)
    • environments(环境配置)
      • environment(环境变量
        • transactionManager(事务管理器)
        • dataSource(数据源)
    • databaseIdProvider(数据库厂商标识)
    • mappers(映射器)

2. 环境变量

  • MyBatis 可以配置成适应多种环境
  1. 环境配置。

    environments

    以上设置了两种环境,改变默认环境来访问不同的数据库或者其他切换。

  2. 事务管理器

    • 在 MyBatis 中有两种类型的事务管理器(也就是 type="[JDBC|MANAGED]"):
  3. 数据源
    datasource

**mybatis默认事务管理器时JDBC,连接池:POOLED.切换mybatis环境

3.属性

我们可以通过properties属性来实现引用配置文件

这些属性可以在外部进行配置,并可以进行动态替换。你既可以在典型的 Java 属性文件中配置这些属性,也可以在 properties 元素的子元素来传递.[dp.proeprties]

properties引入

编写一个配置文件:

driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/mybatis?useUnicode=true&characterEncoding=utf-8
username=root
password=1094148867g

在核心配置文件中引入。

<!--引入外部配置文件-->
    <properties resource="db.properties">
        <property name="password" value="1094148867g"/>
    </properties>
  • 可以引入外部文件
  • 可以在其中增加一些属性配置
  • 如果有两各文件有同一个字段,优先使用外部配置文件

5. 别名

类型别名可为 Java 类型设置一个缩写名字。 它仅用于 XML 配置,意在降低冗余的全限定类名书写。

<!--    方法一:可以给实体类起别名-->
    <typeAliases>
        <typeAlias type="com.peng.pojo.User" alias="User"/>
    </typeAliases>

    <!--    方法二:指定实体类的包名来指定搜索一下的类,则他的默认别名就是这个类的首字母小写-->
    <typeAliases>
        <package name="com.peng.pojo"/>
    </typeAliases>

  <select id="getUserList" resultType="User">
        select * from mybatis.user
    </select>
   <select id="getUserById" parameterType="int" resultType="com.peng.pojo.User">
        select *from mybatis.user where id=#{id}
    </select>

  • 在实体类比较少的时候使用第一种方式,如果实体类十分多的化建议使用第二种。
  • 第一种可以使用自定义别名,第二种一般为默认,但也可以用注解来更改第二种的默认别名如下:
@Alias("hello")
public class User {
    private int id;
    private String name;
    private String pwd;
    ...
}
    <insert id="addUser" parameterType="hello">
        insert into mybatis.user (id,name,pwd) values(#{id},#{name},#{pwd});
    </insert>

Java 类型内建的类型别名。它们都是不区分大小写的:


别名	映射的类型
_byte	byte
_long	long
_short	short
_int	int
_integer	int
_double	double
_float	float
_boolean	boolean
string	String
byte	Byte
long	Long
short	Short
int	Integer
integer	Integer
double	Double
float	Float
boolean	Boolean
date	Date
decimal	BigDecimal
bigdecimal	BigDecimal
object	Object
map	Map
hashmap	HashMap
list	List
arraylist	ArrayList
collection	Collection
iterator	Iterator

6.设置及其其他配置(了解)

这是 MyBatis 中极为重要的调整设置,它们会改变 MyBatis 的运行时行为。
具体查看官网

  • typeHandlers(类别处理器)
  • objectFactory(对象工厂)
  • plugins插件
    • mybatis-generator-core
    • mybatis-plus
    • 通用mapper

7. 映射器(mapper)

MapperRegistry:注册绑定我们的Mapper文件

方式一:推荐使用

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

方式二:

    <mappers>
<!--        <mapper resource="com/peng/dao/UserMapper.xml"/>-->
        <mapper class="com.peng.dao.UserMapper"></mapper>
    </mappers>

注意点:

  1. 接口和他的Mapper配置文件必须同名
  2. 接口和他的Mapper配置文件必须在同一个包下

方式三:

 <mappers>
<!--        <mapper resource="com/peng/dao/UserMapper.xml"/>-->
<!--        <mapper class="com.peng.dao.UserMapper"></mapper>-->
        <package name="com.peng.dao"/>
    </mappers>

使用扫描包进行注入绑定。

8.生命周期和作用域

Mybatis流程图

生命周期类别是至关重要的,因为错误的使用会导致非常严重的并发问题

SqlSessionFactoryBuilder:

  • 一旦创建SqlSessionFactory就不再需要他了
  • 局部变量

SqlSessionFactory:

  • 可以看作为数据库连接池
  • SqlSessionFactory一旦被创建就应该在应用的运行期间一直存在,没有任何理由丢弃它或重新创建另一个实例
  • 因此SqlSessionFactory的最佳作用作用域就是应用作用域
  • 最简单的就是使用单例模式或者静态单例模式。

SqlSession:

  • 连接池的一个请求!
  • SqlSession的实例不是线程安全的,因此是不能被共享的。所以他的最佳作用域时请求或者方法作用域
  • 用完之后需要关闭,否则资源被占用

如下图:
SqlSessionFactory就类似与线程池,里面可以调用很多的SqlSession。且他们之间时相互独立的。而一个SqlSession可以调用多个Mapper。
所以一般在结束增删改查操作后要关闭SqlSession避免浪费资源。
SqlSession作用实例图

这里面的每一个Mapper就代表一个具体的业务

9.解决属性名和字段名不一致的问题

mybitas数据库

新建一个项目拷贝之前的,测试实体类字段不一致的情况。

测试字段

测试出现问题

测试问题1

Mapper.xml中的sql语句:
 select *from mybatis.user where id=#{id}

解决方法:

  • 起别名

    select id ,name,pwd as password from mybatis.user where id=#{id}
    
  • resultMap(重点)

    结果集映射

    实力类字段:id name pwd
    java对应类:id name password
    
<!--    结果集映射 ,映射为User,其User又是别名,就相当于是映射com.peng.pojo.user-->
    <resultMap id="usermap" type="User">
        <!--column就是数据库中的字段,property就是实体类中的属性
        这样就完成了字段与实体类的不同名的映射情况-->
        <result column="pwd" property="password"></result>
    </resultMap>

    <select id="getUserById" parameterType="int" resultMap="usermap" >
        select *from mybatis.user where id=#{id}
    </select>

resultMap实例

  • resultMap 元素是 MyBatis 中最重要最强大的元素。
  • ResultMap 的设计思想是,对简单的语句做到零配置,对于复杂一点的语句,只需要描述语句之间的关系就行了。
    • ResultMap 的优秀之处——你完全可以不用显式地配置它们。

10.日志

10.1 日志工厂

如果一个数据操作出现了异常,我们需要排错,日志就是最好的助手

曾经:sout debug
现在:日志工厂

日志工厂

  • SLF4J
  • LOG4J(掌握)
  • LOG4J2
  • JDK_LOGGING
  • COMMONS_LOGGING
  • STDOUT_LOGGING(掌握)
  • NO_LOGGING

在myvbatis中具体使用哪一个日志,在设置中设定。

  • STDOUT_LOGGING
    <settings>
        <!--    日志设置-->
        <setting name="logImpl" value="STDOUT_LOGGING"/>
    </settings>

日志

10.2 Log4j

什么是log4j:

  • Log4j是Apache的一个开源项目,通过使用Log4j,我们可以控制日志信息输送的目的地是控制台、文件、GUI组件
  • 我们也可以控制每一条日志的输出格式
  • 通过定义每一条日志信息的级别,我们能够更加细致地控制日志的生成过程
  • 可以通过一个配置文件来灵活地进行配置,而不需要修改应用的代码。
  1. 先导入Log4j的包
    <!-- https://mvnrepository.com/artifact/log4j/log4j -->
    <dependency>
        <groupId>log4j</groupId>
        <artifactId>log4j</artifactId>
        <version>1.2.17</version>
    </dependency>
    

11.分页

  • 减少数据处理量

11.1 使用Limit分页

-- 每页显示2个从第0个开始查询
select * from user limit 0,2;

-- 只有一个参数就是从0开始到第二个参数
select * from user limit 3;

使用mybatis实现分页,核心SQL

  1. 接口
//分页
List<User> getUserBylimit(Map<String,Integer> map);
  1. Mapper.xml
    <select id="getUserBylimit" parameterType="map" resultMap="usermap">
    select * from mybatis.user limit #{startIndex},#{pageSize};
</select>
  1. 测试
    public void getUserBylimit(){
    SqlSession sqlSession = MybatisUtils.getSqlSession();

    UserMapper mapper = sqlSession.getMapper(UserMapper.class);

    Map<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();
}

11.2 RowBounds分页

不在使用SQL实现分页

  1. 接口
     //RowBounds分页
     List<User> getUserRowBounds();
    
  2. mapper.xml
       <select id="getUserRowBounds" resultMap="userMap">
         select * from mybatis.user;
     </select>
    
  3. 测试
     @Test
     public void getUserRowBounds(){
         SqlSession sqlSession = MybatisUtils.getSqlSession();
    
         //RowBounds实现
         RowBounds rowBounds = new RowBounds(1,2);
    
         //通过java代码层面实现分页
         List<User> userList = sqlSession.selectList("com.peng.dao.UserMapper.getUserRowBounds",null,rowBounds);
         for (User o : userList) {
             System.out.println(o);
         }
    
         sqlSession.close();
    
    
     }
    

11.3分页插件

分页插件

12.注解开发

12.1面向接口编程

  • 根本原因:解耦,可拓展,提高复用,分层开发中,上层不用管具体实现,大家都会遵守共同的标准,使得开发变得更容易,规范性更好。
  • 在一个面向对象的系统中,系统的各种功能是由许多的不同对象协作完成的。在这种情况下,各个对象内部是如何实现自己的。对系统设计人员来讲就不那么重要了。
  • 而各个对象之间的协作关系则成为系统设计的关键。小到不同类之间的通信,大到各种模块之间的交互,在系统设计之初都是要着重考虑的,这也是系统设计的主要工作内容,面向接口编程就是按照这种思想编程。

关于接口理解

  • 接口从更深层次的理解,应是定义(规范,约束)与实现(名实分离原则)的分离。
  • 接口的本身反映了系统设计人员对系统的抽象理解
  • 接口应有两类:
    • 第一类是对一个个体的抽象,它可对应为一个抽象体;
    • 第二类是对一个个体某一方面的抽象,即形成一个抽象面
    • 一个体有可能有多个抽象面,抽象体与抽象面是有区别的。

三个面向区别

  • 面向对象是指,我们考虑问题时,一对象为单位,考虑它的属性及方法;
  • 面向过程是指,我们考虑问题时,以一个具体的流程(事务过程)为单位,考虑它的实现;
  • 接口设计与非接口设计是2针对复用技术而言,与面向对象(过程)不是一个问题,更多的体现就是对系统整体的架构。

12.2使用注解开发

  1. 注解在接口上实现
    @Select("select * from user")
List<User> getUsers();
  1. 需要再核心配置文件(mybatis-config.xml)中绑定接口
<!--    绑定接口-->
    <mappers>
        <mapper class="com.peng.dao.UserMapper"/>
    </mappers>
  1. 测试
//    注解查询
    @Test
    public void test1(){
        SqlSession sqlSession = MybatisUtils.getSqlSession();

        //底层主要应用反射
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        List<User> users = mapper.getUsers();
        for (User user : users) {
            System.out.println(user);
        }
        sqlSession.close();
    }

本质:反射机制实现。
底层:动态代理
daili

mybatis详细流程

mybatis底层流程1
mybatis底层流程2
mybatis底层流程3

总结:resources再加载全局配置文件后,实例化SqlSessionFactoryBuilder构造器

     sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

build方法调用XMLConfigBuilder来解析配置文件
XML配置流解析
sqlSessionFactory配置
再将配置文件的信息加载到SqlSessionFactory对象。再创建executor执行器来创建SqlSession对象和事务管理
exector
再创建SqlSession,加载配置
SqlSession配置
再执行CRUD操作。

其他问题

我们可以在设置自动提交。
事务自动提交

多参情况:

 @Select("select * from user where id=#{id} and name=#{name}")
   List<User> getUsersByIDName(@Param("id") int id,@Param("name") String name);

注意点:

  • 当Mapper接口中的方法有两个或以上对象的时候,需要再每个参数前面加上@Param(参数名).
  • 再注解中的#{id}和用配置文件Mapper.xml中的sql语句中所要取的#{id}的元素都是从@Param{id}的元素里取的。应该保持一样,而非是方法中的参数名一样。

13 Lombok

  1. 安装lombok插件
  2. 再项目中导入lombok的jar包
@Getter and @Setter
@FieldNameConstants
@ToString
@EqualsAndHashCode
@AllArgsConstructor, @RequiredArgsConstructor and @NoArgsConstructor
@Log, @Log4j, @Log4j2, @Slf4j, @XSlf4j, @CommonsLog, @JBossLog, @Flogger, @CustomLog
@Data
@Builder
@SuperBuilder
@Singular
@Delegate
@Value
@Accessors
@Wither
@With
@SneakyThrows
@val
@var
experimental @var
@UtilityClass
Lombok config system
Code inspections
Refactoring actions (lombok and delombok)
@Data
@AllArgsConstructor
@Alias("hello")
public class User {
    private int id;
    private String name;
    private String password;

}

@Date:无参构造器,get,set,tostring,hashcode,equuals.

@AllArgsConstructor:有参构造

@NoArgsConstructor:显示无参构造

@ToString,@getter,@setter

14.多对一

多对一

建立表

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,
  PRIMARY KEY (`id`),
  KEY `fktid` (`tid`),
  CONSTRAINT `fktid` FOREIGN KEY (`tid`) REFERENCES `teacher` (`id`)
) ENGINE=INNODB DEFAULT CHARSET=utf8INSERT 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');

多对一实体类1
多对一实体类2

测试环境搭建

  1. 导入lombok
  2. 新建实体类Teacher,Student
  3. 建立Mapper接口
  4. 建立Mapper.XML文件
  5. 再核心配置文件中绑定注册我们的Mapper接口或者文件
  6. 测试查询是否成功

14.1 按照查询嵌套处理mysql中的子查询

对于同时查询两个表的信息。(或者说再java中所查询返回值的对象的中的属性是另外一个类的对象。)

再处理实体类属性与数据库对应表字段名不同名的情况下,我们往往采用resultMap来进行类中属性与表中字段的映射,但是当类中属性是作为一个对象存在时,这时用resultMap来映射往往就会失效。而需要采取别的方法处理

    <resultMap id="studentTeacher" type="com.peng.pojo.student">
<!--        对于数据库中字段名和实体类属性名不相同的情况,简单属性可以用result来进行
            简单映射,而对于以某个对象作为实体类属性的情况则不可用。需要单独处理

-->
        <result column="id" property="id"></result>
        <result column="name" property="name"></result>
        
<!--        复杂属性,单独处理-->
        <association property="t" column="tid" javaType="com.peng.pojo.Teacher" select="getTeacher"></association>

    </resultMap>

    <select id="getStudent" resultMap="studentTeacher">
        select * from student;
    </select>
    
    <select id="getTeacher" resultType="com.peng.pojo.Teacher">
        select * from teacher where id=#{id}
    </select>
    <!--       
        总结:以上理解孙旭可以先看做:使用select查询了student和teacher表的所有属性,然后再通过resultMap来映射了student表中的字段,而对于最后一个外键(老师表的编号),则是采用了
         <association property="t" column="tid" javaType="com.peng.pojo.Teacher" select="getTeacher"></association>
         该标签中可以看到:
         property对应的时student类中的属性名,
         column对饮的时student表中的外键tid.
         javaType:则是指定了该对象的实体类
         select:则是调用了以上的select标签的getTeacher方法的查询结构。
         从select元素获取到select标签中再Teacher表中的查询结果,再将返回结果作为了对应属性的返回值。(这个查询结果又是与该属性返回值的实体类是对应的)
    -->

14.2 按照结果嵌套处理。mysql中的连表查询

 <select id="getStudent2" resultMap="StudentTeacher2">
        select s.id sid,s.name sname,t.name tname
        from student s,teacher t where s.id=t.id;
    </select>

    <resultMap id="StudentTeacher2" type="com.peng.pojo.student">
        <result property="id" column="sid"></result>
        <result property="name" column="sname"></result>
        <association property="t" javaType="com.peng.pojo.Teacher">
            <result property="name" column="tname"></result>

        </association>
    </resultMap>
    
    <!-- 
        总结:显示select进行连表查询。
        然后用resultMap来对select中的字段名一一进行映射。而对于特殊的外键字段用association标签指定其实体类和对应的属性名
    -->

15.一对多

例:一个老师教授多个学生,对老师而言就是一对多的关系。

  1. 实体类

一对多实体类1
一对多实体类2

按照结果嵌套查询

<!--    按结果嵌套查询-->
    <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="com.peng.pojo.Teacher">
        <result column="tid" property="id"></result>
        <result column="tname" property="name"></result>
        
<!--        复杂的属性单独处理,对象:association  集合:collection
            javaType这是一个指定的属性的类型;
            集合中的泛型信息,我们使用ofType获取
-->
        <collection property="students" ofType="com.peng.pojo.Student">
            <result property="id" column="sid"></result>
            <result property="name" column="sname"></result>
            <result property="t" column="tid"></result>
        </collection>
    </resultMap>
   @Test
    public void test1(){
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        TeacherMapper mapper = sqlSession.getMapper(TeacherMapper.class);

        Teacher teacher = mapper.getTeacher(1);
        System.out.println(teacher);
        sqlSession.close();
    }

按照子查询处理

<!--    ===================方式二子查询================================-->
    <select id="getTeacher2" resultMap="TeacherStudent2">
        select *from mybatis.teacher where id=#{tid}
    </select>
    <resultMap id="TeacherStudent2" type="com.peng.pojo.Teacher">
<!--        <result property="id" column="id"></result>-->
        <collection property="students" column="id" javaType="ArrayList" ofType="com.peng.pojo.Student" select="getStudentByTeacherId" ></collection>

    </resultMap>
    <select id="getStudentByTeacherId" resultType="com.peng.pojo.Student">
        select * from mybatis.student where tid=#{tid}
    </select>
    @Test
    public void test2(){
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        TeacherMapper mapper = sqlSession.getMapper(TeacherMapper.class);

        Teacher teacher = mapper.getTeacher2(1);
        System.out.println(teacher);
        sqlSession.close();
    }

小结

  • 关联:association[多对一]
  • 集合:collection[一对多]
  • javaType & ofType
    • javaType:用来指定实体类中属性类型
    • oftype:用来指定映射到List或者集合中的pojo类型,泛型中的约束类型。

注意点:

  • 保证sql的可读性,尽量易懂
  • 注意一对多和多对一中,属性名和字段的问题
  • 如果问题不好排除。建议使用Log4j

面试高频率:

  • Mysql引擎
  • innoDB底层原理
  • 索引
  • 索引优化

13.动态Sql

什么是动态sql:动态Sql就是根据不同条件生成不同的sql语句

用jdbc拼接实现的动态sql
jdbc拼接

搭建环境

CREATE TABLE `blog`(
`id` VARCHAR(50) NOT NULL COMMENT '博客id',
`title` VARCHAR(100) NOT NULL COMMENT '博客标题',
`author` VARCHAR(30) NOT NULL COMMENT '博客作者',
`create_time` DATETIME NOT NULL COMMENT '创建时间',
`views` INT(30) NOT NULL COMMENT '浏览量'
)ENGINE=INNODB DEFAULT CHARSET=utf8;

创建一个工程:

  1. 导包

  2. 编写配置文件

  3. 编写实体类

    @Data
    public class Bolg {
    
        private String id;
        private String title;
        private String author;
        private Date createTime;
        private int Views;
    
    
    }
    
  4. 编写实体类对应的Mapper接口和Mapper.xml文件

    public interface BolgMapper {
    
    
     //插入数据
     int addBolg(Bolg bolg);
     }
    
    <?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.peng.dao.BolgMapper">
    
        <insert id="addBolg" parameterType="com.peng.pojo.Bolg">
            insert into mybatis.blog(id,title,author,create_time,views)
            values (#{id},#{title},#{author},#{createTime},#{views});
        </insert>
    
    </mapper>
    

IF

<!--    if 再sql语句中实现条件拼接 按照一下格式书写,if 参数test=后接字符串表示条件-->
    <select id="queryBolg" parameterType="map" resultType="com.peng.pojo.Bolg">
        select * from mybatis.blog where 1=1
        <if test="title != null">
            and title=#{title}
        </if>
        <if test="author != null">
            and author=#{author}
        </if>
    </select>
    @Test
    public void queryBolgTest2(){
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        BolgMapper mapper = sqlSession.getMapper(BolgMapper.class);

        Map<Object, Object> map = new HashMap<>();
        map.put("title","Java如此简单");
        map.put("author","狂神说");
        List<Bolg> bolgs = mapper.queryBolg(map);
        for (Bolg bolg : bolgs) {
            System.out.println(bolg);

        }
        sqlSession.close();
    }

choose(when,otherwise)

类似与java的swich

    <select id="queryBlogChoose" parameterType="map" resultType="com.peng.pojo.Bolg">
        select * from mybatis.blog
-- where标签,和sql中的where用法相似,唯一不同的是:where标签会自动去除后接的 and和or ,为的是防止select语句中
-- 的where后面再接拼接条件时 出现的错误。
        <where>
            <choose>
                <when test="title!=null">
                    and title=#{title}
                </when>
                <when test="author!=null">
                    and author=#{anthor}
                </when>
                <otherwise>
                    and views=#{views}
                </otherwise>
            </choose>

        </where>
    </select>
    @Test
    public void queryBolgTest3(){
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        BolgMapper mapper = sqlSession.getMapper(BolgMapper.class);

        Map<Object, Object> map = new HashMap<>();
//        map.put("title","Java如此简单");
//        map.put("author","狂神说");
        map.put("views","9999");
        List<Bolg> bolgs = mapper.queryBlogChoose(map);
        for (Bolg bolg : bolgs) {
            System.out.println(bolg);

        }
        sqlSession.close();
    }

trim(set where)


    <insert id="updateBlog" parameterType="map">
        update mybatis.blog
--             set标签与where创建的初衷类似,为了避免再拼接时后面出现的",",可以自动判断删除
        <set>
            <if test="title!=null">
                title=#{title},
            </if>
            <if test="author!=null">
                author=#{author},
            </if>
        </set>
        <where>
            id=#{id}
        </where>
    </insert>
    @Test
    public void updateBlog(){
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        BolgMapper mapper = sqlSession.getMapper(BolgMapper.class);

        Map<Object, Object> map = new HashMap<>();
        map.put("title","Java如此复杂");
//        map.put("author","狂神说");
        map.put("id","cbb86d626b5a444480e4b2023262cc55");
        mapper.updateBlog(map);

        sqlSession.close();
    }

你也可以通过自定义 trim 元素来定制 where 元素的功能

<trim prefix="WHERE" prefixOverrides="AND |OR ">
  ...
</trim>
<!-- prefixOverrides 属性会忽略通过管道符分隔的文本序列(注意此例中的空格是必要的)。上述例子会移除所有 prefixOverrides 属性中指定的内容,并且插入 prefix 属性中指定的内容。 -->

<trim prefix="SET" suffixOverrides=",">
  ...
</trim>

所谓的动态SQL,本质还是sql语句,只是我们可以再sql层面去执行一些逻辑代码。

SQL片段

有时候,我们可能会将一些公共的部分抽取出来方便复用。

  1. 使用sql标签抽取公共部分
  2. 再需要使用的地方使用include标签

<!--    sql片段抽出sql语法中相同的部分实现复用-->
    <sql id="if-title-author">
        <if test="title != null">
            and title=#{title}
        </if>
        <if test="author != null">
            and author=#{author}
        </if>
    </sql>

    <insert id="addBolg" parameterType="com.peng.pojo.Bolg">
        insert into mybatis.blog(id,title,author,create_time,views)
        values (#{id},#{title},#{author},#{createTime},#{views});
    </insert>

<!--    if 再sql语句中实现条件拼接 按照一下格式书写,if 参数test=后接字符串表示条件-->
    <select id="queryBolg" parameterType="map" resultType="com.peng.pojo.Bolg">
        select * from mybatis.blog where 1=1
        <include refid="if-title-author"></include>
--  引用所需要的sql标签中的语句,refid值为对应sql标签的id值
    </select>
  1. 注意事项
    • 最好基于单表来定义sql片段

foreach

一般对集合进行遍历,通常是在构建IN条件语句的时候。

<!--select * from mybatis.blog
        <where>
            id in ("1","2","3")
        </where>-->
    <select id="queryBlogForeach" parameterType="list" resultType="com.peng.pojo.Bolg">
        select * from mybatis.blog
        <where>id in
            <foreach collection="list" item="id"
                     open="(" separator="," close=")">
                 #{id}
            </foreach>
        </where>
    </select>

    @Test
    public void queryBolgForeach(){
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        BolgMapper mapper = sqlSession.getMapper(BolgMapper.class);

        List<String> list = new ArrayList<String>();
        list.add("1");
        list.add("2");
        list.add("3");
        List<Bolg> bolgs = mapper.queryBlogForeach(list);
        for (Bolg bolg : bolgs) {
            System.out.println(bolg);

        }

        sqlSession.close();
    }

14.缓存

  1. 什么是缓存[Chahe]?
    • 存在内存中的临时数据
    • 将用户经常查询的数据放在缓存(内存)中,用户去查询数据就不用从磁盘上(关系型数据库文件)查询,从缓存中查询,从而提高效率,解决了高并发系统的性能问题。
  2. 为什么使用缓存
    • 减少和数据的交互次数,减少系统开销,提高效率
  3. 什么样的数据可以使用缓存
    • 经常查询并且不经常改变的数据

Mybaits缓存

  • Mybatis包含一个非常强大的查询缓存特性,它可以非常方便的定制和配置缓存。缓存可以极大的提升查询效率
  • Mybatis系统中默认定义了两级缓存:一级缓存二级缓存
    • 默认情况下,只有一级缓存开启。SqlSession级别的缓存,也称为本地缓存)
    • 二级缓存需要手动启动和配置,基于namespace级别的缓存
    • 为了提高扩展性,MyBatis定义了缓存接口Cache。我们可以通过实现Cache接口来自定义二级缓存

一级缓存

  • 也叫本地缓存
    • 与数据库同义词会话期间查询到的数据会放到本地缓存中
    • 以后如果需要获取相同数据,直接从缓存中拿,没必须再去查询数据库
      一级缓存

测试步骤

  1. 开启日志
  2. 测试在一个sqlSession中

一级缓存失效情况:

  1. 查询不同结果
    缓存失效1
  2. 增删改操作,可能会改变原来的数据,所以必定会刷新

再连续查询相同用户的中间,进行一次其他用户的更新操作,可以看到其缓存失效。

        SqlSession sqlSession = MybatisUtils.getSqlSession();

        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        User user1 = mapper.queryUserById(1);
        System.out.println(user1);
        System.out.println("============================================");

        //增加修改语句
        mapper.updateUser(new User(2,"BBBB","141341"));

        User user2 = mapper.queryUserById(1);
        System.out.println(user2);

        System.out.println(user1==user2);
        sqlSession.close();

缓存失效2

  1. 在不同的Mapper中进行查询操作。
  2. 手动清除缓存
sqlSession.clearCache();

小结:一级缓存默认是开启的。只在一次sqlSession中有效,只在拿到连接和关闭连接之间有效。

二级缓存

  • 二级缓存也叫全局缓存
  • 基于namespace级别的缓存,一个名称空间对应一个二级缓存
  • 工作机制
    • 一个会话查询一条数据,这个数据就会被放在当前会话的一级缓存中
    • 如果当前会话关闭了,这个会话对应的一级缓存就没了。但是我们想要的是,会话关闭了,一级缓存中的数据就被保存再二级缓存中。
    • 新的会话查询信息,就可以从二级缓存中获取内容;

步骤

  1. 开启全局缓存
     <settings>
         <setting name="cacheEnabled" value="true"/>
     </settings>
    
  2. 在要使用二级缓存的Maopper中开启
    <!--    开启二级缓存,在当前Mapper.xml中-->
     <cache/>
    
    也可以自定义参数
    <!--    开启二级缓存,在当前Mapper.xml中-->
    <cache eviction="FIFO"
            flushInterval="60000"
            size="512"
            readOnly="true"/>
    

当然也可以设置对应方法是否使用二级缓存.默认是true


    <select id="queryUserById" resultType="user" useCache="true">

        select * from mybatis.user where id=#{id}

    </select>
  1. 测试
    1. 问题:我们需要将实体类序列化!
      否则就会报错
    Caused by: java.io.NotSerializableException: com.peng.pojo.User
    

小结:

  • 只要开启了二级缓存,在同一个Mapper下就有效
  • 所有的数据都会先放在一级缓存中
  • 只有当会话提交后,才会提交到二级缓存中。

原理流程图

缓存原理图

标签:缓存,name,战狂,神版,mybatis,sqlSession,Mybatis,id,select
From: https://www.cnblogs.com/tkyd/p/17165625.html

相关文章

  • day07-MyBatis的关联映射01
    MyBatis的关联映射Mybatis的关联映射实际的开发中,对数据库的操作常常会涉及到多张表,这在面向对象中就涉及到了对象与对象之间的关联关系。针对多表之间的操作,MyBatis提......
  • mybatis:自定义映射关系resultMap
    创建表t_emp定义实体类packageorg.example.entity;publicclassEmp{privateIntegerempId;privateStringempName;privateIntegerage;pr......
  • mybatis自增主键的获取
    实体类packageorg.example.entity;publicclassUser{privateIntegerid;privateStringname;privateintage;privateStringgender;p......
  • 【Mybatis】【配置文件解析】【四】Mybatis源码解析-mappers的解析四(绑定Mapper、处理
    1 前言我们上节把我们mapper里的sql节点以及我们的增删改查都解析了,那么最后回来就剩下两块没看了,一块是我们的mapper跟我们的接口绑定,一块就是我们在解析的过程中......
  • SSM框架-MyBatis学习日记6
    多对一的处理多对一的理解:多个学生对应一个老师如果对于学生这边,就是一个多对一的现象,即从学生这边关联一个老师!数据库设计CREATE TABLE `teacher`(`id` INT(10)......
  • mybatis入门
    一、MyBatis简介​ MyBatis本是apache的一个开源项目iBatis,2010年这个项目由apachesoftwarefoundation迁移到了googlecode,并且改名为MyBatis。2013年11月迁移到Github......
  • mybatis-plus主键生成策略(实体类配置,数据库插入数据自动生成id)
    转载:MyBatisplus--ActiveRecord(AR)_mybatisplusidtype.auto_憨憨浩浩的博客-CSDN博客0.auto:自动增长(mysql,sqlserver)1.none:没有主键2.input:手动输入3.id_worker:实体......
  • 【MyBatis】测试链接数据库查询用户
    配置文件访问:【MyBatis】配置Mybatis项目-小鼻涕孩-博客园(cnblogs.com)数据表数据:Test.java:1importorg.apache.ibatis.io.Resources;2importorg.apache.......
  • springboot+mybatis+redis+mysql项目搭建,含示例Demo
    转载自:https://blog.csdn.net/qq_40772342/article/details/105049322========== redis在web开发中使用的场景很多,其中缓存是其中一个很重要的使用场景,之所以用作缓存,......
  • SSM框架-MyBatis学习日记5
    使用limit实现分页在学习mybatis等持久层框架的时候,会经常对数据进行增删改查操作,使用最多的是对数据库进行查询操作,如果查询大量数据的时候,我们往往使用分页进行查询,也就......