欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

Mybatis框架(1)--注解

程序员文章站 2022-06-19 15:15:55
前言:自学ssm已经一段时间了,深刻感受到跟着视频学必须集中一整块时间。如果断断续续地看真的会忘光前面的知识,从而后面的也连不上,整个感受就会很不系统不扎实。今天刚结束Spring框架的学习,准备复习一下Mybatis框架。以下为个人跟着视频内容整理,有错误请一定指出!勿喷,谢谢!视频:B站黑马程序Mybatis框架学习作用:Mybatis是持久层框架 -> 内部封装jdbc(省去手动加载驱动、创建连接、创建statement等繁杂过程),使开发者只需要关注sql本身。持久层技术...

前言:

自学ssm已经一段时间了,深刻感受到跟着视频学必须集中一整块时间。如果断断续续地看真的会忘光前面的知识,从而后面的也连不上,整个感受就会很不系统不扎实。

今天刚结束Spring框架的学习,准备复习一下Mybatis框架。以下为个人跟着视频内容整理,有错误请一定指出!勿喷,谢谢!

视频来源:B站:BV1mE411X7yp
黑马程序出品不会错!!!强推~(黑马快打钱 x)


Mybatis概述

  1. 作用:Mybatis是持久层框架 -> 内部封装jdbc(省去手动加载驱动、创建连接、创建statement等繁杂过程),使开发者只需要关注sql本身。

  2. 持久层技术解决方案:

    · JDBC技术:Connection PreparedStatement ResultSet

    ·Spring的JdbcTemplate:Spring中对jdbc的简单封装

    ·Apache的DBUtils:类似JdbcTemplate

    ·后两者都是工具类。

  3. 方式:通过xml或注解的方式将要执行的各种statement配置起来,并通过java对象和statement中sql的动态参数进行映射生成最终执行的sql语句,最后由mybatis框架执行sql并将结果映射为java对象并返回。

  4. ORM思想:Object Relational Mapping(对象关系映射),即把数据库表和实体类及实体类属性对应起来,通过操作实体类来操作db。Mybatis框架利用ORM思想实现结果集的封装。

  5. 在Mybatis中CRUD一共有四个注解:@Select @Insert @Update @Delete

xml v.s. 注解

xml:集中、好维护,但较为繁琐
注解:简便(写得少),但因其较为分散所以不好维护
e.g.:@Select这一个注解就可以代替了原本xml配置方法中需要在resources文件夹底下新建com.demo.dao.IUserDao.xml创建映射关系这一步。


实战1(基于注解的单表CRUD操作)

Mysql新建数据库和表:
Mybatis框架(1)--注解

0.总体结构:
Mybatis框架(1)--注解
其中domain中的User.java就是实体类,此处实体类内部的属性名与db表中列名一一对应(实际上会有命名不统一的情况,处理方法将在实践2中提到)。

1.pom.xml 导入依赖

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.example</groupId>
    <artifactId>mybatis_day04_annotation</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>jar</packaging>

<dependencies>
    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis</artifactId>
        <version>3.5.4</version>
    </dependency>

    <!--驱动-->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>8.0.20</version>
    </dependency>

    <!--日志-->
    <dependency>
        <groupId>log4j</groupId>
        <artifactId>log4j</artifactId>
        <version>1.2.12</version>
    </dependency>

    <!--单元测试-->
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.13</version>
        <scope>test</scope>
    </dependency>
</dependencies>
</project>

2.主配置文件:SqlMapConfig.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>
    <!-- 引入外部配置文件-->
    <properties resource="jdbcConfig.properties"></properties>

    <!--配置别名-->
    <typeAliases>
        <package name="com.itheima.domain"></package>
    </typeAliases>

    <!-- 配置环境-->
    <environments default="mysql">
        <environment id="mysql">
            <transactionManager type="JDBC"></transactionManager>
            <dataSource type="POOLED">
                <property name="driver" value="${jdbc.driver}"></property>
                <property name="url" value="${jdbc.url}"></property>
                <property name="username" value="${jdbc.username}"></property>
                <property name="password" value="${jdbc.password}"></property>
            </dataSource>
        </environment>
    </environments>

    <!-- 指定带有注解的dao接口所在位置 -->
    <mappers>
        <mapper class="com.itheima.dao.IUserDao"></mapper>
    </mappers>
</configuration>

这里导入外部配置文件就是在另一个专门的.properties文件里配置数据源信息以便修改:
Mybatis框架(1)--注解
在SqlMapConfig.xml中就可以直接用 ${变量名} 来指代具体信息。妙!
Mybatis框架(1)--注解
3.导入配置文件:log4j.properties

log4j.rootLogger=debug, stdout, R

log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout

# Pattern to output the caller's file name and line number.
log4j.appender.stdout.layout.ConversionPattern=%5p [%t] (%F:%L) - %m%n

log4j.appender.R=org.apache.log4j.RollingFileAppender
log4j.appender.R.File=example.log

log4j.appender.R.MaxFileSize=100KB
# Keep one backup file
log4j.appender.R.MaxBackupIndex=5

log4j.appender.R.layout=org.apache.log4j.PatternLayout
log4j.appender.R.layout.ConversionPattern=%p %t %c - %m%n

4. 持久层接口: IUserDao.java Mybatis框架(1)--注解
5.测试类:AnnotationCRUDTest

package com.itheima.test;

import com.itheima.dao.IUserDao;
import com.itheima.domain.User;
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 org.junit.After;
import org.junit.Before;
import org.junit.Test;
import java.io.InputStream;
import java.util.Date;
import java.util.List;

public class AnnotationCRUDTest {
    private InputStream in;
    private SqlSessionFactory factory;
    private SqlSession session;
    private IUserDao userDao;

    @Before
    public  void init()throws Exception{
        in = Resources.getResourceAsStream("SqlMapConfig.xml");
        factory = new SqlSessionFactoryBuilder().build(in);
        session = factory.openSession();
        userDao = session.getMapper(IUserDao.class);
    }

    @After
    public  void destroy()throws  Exception{
        session.commit();
        session.close();
        in.close();
    }
    
    @Test
    public void testSave(){
        User user = new User();
        user.setUsername("mybatis annotation");
        user.setAddress("北京市昌平区");
        userDao.saveUser(user);
    }

    @Test
    public void testUpdate(){
        User user = new User();
        user.setId(4);
        user.setUsername("mybatis annotation update");
        user.setAddress("北京市海淀区");
        user.setSex("男");
        user.setBirthday(new Date());

        userDao.updateUser(user);
    }
    
    @Test
    public void testDelete(){
        userDao.deleteUser(4);
    }

    @Test
    public void testFindOne(){
        User user = userDao.findById(1);
        System.out.println(user);
    }
    
    @Test
    public  void testFindByName(){
//        List<User> users = userDao.findUserByName("%mybatis%");
        List<User> users = userDao.findUserByName("mybatis");
        for(User user : users){
            System.out.println(user);
        }
    }

    @Test
    public  void testFindTotal(){
        int total = userDao.findTotalUser();
        System.out.println(total);
    }
}

注解@Before @After @Test:
@Before:初始化方法 对于每一个测试方法都要执行一次(注意与BeforeClass区别,后者是对于所有方法执行一次
@After:释放资源 对于每一个测试方法都要执行一次(注意与AfterClass区别,后者是对于所有方法执行一次
@Test:测试方法,在这里可以测试期望异常和超时时间

@Before和@After注解下的代码 相当于 如下代码,没学注解时是这样实现的:
Mybatis框架(1)--注解


实战2(基于注解的多表一对多查询)

  1. 新加实体类:Account.java,实现两表:User&Account多对一(即一个账户只能属于一个用户,一个用户可拥有多个账户,在mybatis中称为一对一)的查询
    数据库新表Account:
    Mybatis框架(1)--注解
    domain包中新加新的实体类Account:
    Mybatis框架(1)--注解
    为了实现多对一关系,需要在Account实体类中创建User对象~
    Mybatis框架(1)--注解
    同时在User类中也要补充Account对象作为属性!
    Mybatis框架(1)--注解

  2. 实践1中预留了一个悬念:mybatis中如果实体类属性和db表中列明命名不一致怎么解决?
    为测试这一点,改动原本domain中的User类:
    Mybatis框架(1)--注解
    对比数据库表User表列名:
    Mybatis框架(1)--注解
    此时运行,打印结果将会是:
    User{userId=null, userName=‘xxx’, userBirthday=null, userSex=‘null’, userAddress=‘null’}
    User{userId=null, userName=‘xxxxx’, userBirthday=null, userSex=‘null’, userAddress=‘null’}
    只有username这一列有数据,其他列数据都是null!----->说明命名不一致会映射失败,同时也说明Mybatis大小写不区分。
    解决思路:通过注解添加映射!
    解决方法:在@Select等后添加注解@Results及二级注解@Result

@Results(id="这一组映射的名称",value = {
        @Result(id=true,column = "db表中列名",property = "实体类中属性名"),//id=true 主键(此id代表是是否是主键,默认值是 false,非主键可省略)
        @Result(property = "accounts",column = "id",many=@Many(select = "com.demo.dao.IAccountDao.findByUid",fetchType = FetchType.LAZY))
}
  • 等会说以下这行哈~
@Result(property = "accounts",column = "id",many=@Many(select = "com.demo.dao.IAccountDao.findByUid",fetchType = FetchType.LAZY))

并且:不需要每次@Select都写一遍!!只需要 @ResultMap(value={“要用到的那组映射的名称”})即可!!

具体见下述代码:

持久层接口:IUserDao.java

package com.demo.dao;
import com.demo.domain.Account;
import com.demo.domain.User;
import org.apache.ibatis.annotations.*;
import org.apache.ibatis.mapping.FetchType;
import java.util.List;

public interface IUserDao {
    //查询所有
    @Select("select * from user")
    @Results(id="userMap",value = {
            @Result(id=true,column = "id",property = "userId"), //id=true 主键
            @Result(column = "username",property = "userName"),
            @Result(column = "birthday",property = "userBirthday"),
            @Result(column = "sex",property = "userSex"),
            @Result(column = "address",property = "userAddress"),
            @Result(property = "accounts",column = "id",many=@Many(select = "com.demo.dao.IAccountDao.findByUid",fetchType = FetchType.LAZY))
    })
    List<User> findall();

    //查询部分
    @Select("select * from user where id=#{id}")
    @ResultMap(value={"userMap"})
    User findById(Integer userId);
    /*@ResultMap相当于复制了一边上面的@Results
      若此处无@ResultMap 查询打印的结果同上,部分属性未能被封装
      上一句也可直接写为: @ResultMap("userMap")
      value在注解中如果是唯一的一个属性则可以省略 若还有别的则要加上(参照上满@Result)
      若集合元素中只有一个元素 则可以省略{}
    */

    //根据名字模糊查询
    @Select("select * from user where username like '%${username}%'")
    @ResultMap(value={"userMap"})
    List<User> findUserByName(String username);

    @Select("select * from account where uid = #{userId}")
    @ResultMap(value={"userMap"})
    List<Account> findAccountByUid(Integer userId);
}

相应地去写持久层接口:IAccountDao.java和Account测试类:AccountCRUDTest,上代码:

IAccountDao.java中也用到@Results和@Result,

  • 同样的,这行一会儿和上面那行一起解释~
@Result(property = "user",column = "uid",one=@One(select="com.demo.dao.IUserDao.findById",fetchType =FetchType.EAGER))
package com.demo.dao;

import com.demo.domain.Account;
import org.apache.ibatis.annotations.*;
import org.apache.ibatis.mapping.FetchType;
import java.util.List;

public interface IAccountDao {
    @Select("select * from account")
    @Results(id="accountMap",value = {
            @Result(id=true,column = "id",property = "id"), //id=true 主键
            @Result(column = "uid",property = "uid"),
            @Result(column = "money",property = "money"),
            @Result(property = "user",column = "uid",one=@One(select="com.demo.dao.IUserDao.findById",fetchType =FetchType.EAGER))
    })
    List<Account> findall();
    /*One2Many:一个人可以有多个账号 但一个账号只对应一个人 这里账号对人是一对一() 人对账号是一对多
    fetchtype:延迟查询LAZY:查询结果为多条 立即查询EAGER:查询结果为1条*/

    @Select("select * from account where uid = #{userId}")
    @ResultMap(value={"accountMap"})
    List<Account> findByUid(Integer userId);
}

AccountCRUDTest:

package com.demo.test;

import com.demo.dao.IAccountDao;
import com.demo.domain.Account;
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 org.junit.After;
import org.junit.Before;
import org.junit.Test;
import java.io.InputStream;
import java.util.List;

public class AccountCRUDTest {
    private InputStream in;
    private SqlSessionFactory factory;
    private SqlSession session;
    private IAccountDao accountDao;
    
    @Before
    public void init() throws Exception{
        in = Resources.getResourceAsStream("SqlMapConfig.xml");
        factory= new SqlSessionFactoryBuilder().build(in);
        session = factory.openSession();
        accountDao = session.getMapper(IAccountDao.class);
    }

    @After
    public void destroy() throws Exception{
        session.commit();
        session.close();
        in.close();
    }

    @Test
    public void testfindAll(){
        List<Account> accounts = accountDao.findall();
        for(Account account:accounts){
            System.out.println("----每个账户的信息----");
            System.out.println(account);
            System.out.println(account.getUser());
        }
    }
}

此时运行测试方法会报错:

org.apache.ibatis.binding.BindingException: Type interface com.demo.dao.IAccountDao is not known to the MapperRegistry.

MapperRegistry->映射注册,新加的IAccountDao没有在配置文件里映射注册!!!->很简单,在主配置文件SqlMapConfig.xml中修改dao接口配置:(两个接口在同一个包下)
Mybatis框架(1)--注解

  1. 现在讲刚刚遗留的两个小点:
    回到@Result注解,ctr+左击查看源码,发现还有One和Many:
    Mybatis框架(1)--注解
    再点进去看One的底层实现,有select和fetchtype两个属性:Mybatis框架(1)--注解
    点进Many也是一样的:
    Mybatis框架(1)--注解
    进一步:看FetchType底层,发现有三个属性。

LAZY: 延迟加载(对多)
EAGER: 立即加载(对一)
DEFAULT:从上面两者选一个

Mybatis框架(1)--注解
—>>注解可以帮助实现多对一查询,不需要写在sql里实现!!!
在Account的@Results中,在一一对应好列名和属性名后需要加一行:

@Result(property = "user",column = "uid",one=@One(select="com.demo.dao.IUserDao.findById",fetchType =FetchType.EAGER))

property:对应封装的user属性 column:用Account的uid属性查询
select:填写能实现该查询功能的方法的全限定类名
fetchType:EAGER 因为是Account在这是“一“,若是User(多)则用 LAZY

同时,在User的@Results中也需要加一行:

@Result(property = "accounts",column = "id",many=@Many(select = "com.demo.dao.IAccountDao.findByUid",fetchType = FetchType.LAZY))

这样一来就可以通过注解达到多表查询的效果啦!
【画图大师上线:代码怎么运作的】
Mybatis框架(1)--注解


打开弹幕一看,企业大多都用xml…因为更清晰明了。懂了,下一期:Mybatis框架(2)–xml

本文地址:https://blog.csdn.net/weixin_42700882/article/details/110957537