Mybatis框架(1)--注解

前言:

自学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新建数据库和表:
在这里插入图片描述

0.总体结构:
在这里插入图片描述
其中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文件里配置数据源信息以便修改:
在这里插入图片描述
在SqlMapConfig.xml中就可以直接用 ${变量名} 来指代具体信息。妙!
在这里插入图片描述
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 在这里插入图片描述
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注解下的代码 相当于 如下代码,没学注解时是这样实现的:
在这里插入图片描述


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

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

  2. 实践1中预留了一个悬念:mybatis中如果实体类属性和db表中列明命名不一致怎么解决?
    为测试这一点,改动原本domain中的User类:
    在这里插入图片描述
    对比数据库表User表列名:
    在这里插入图片描述
    此时运行,打印结果将会是:
    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接口配置:(两个接口在同一个包下)
在这里插入图片描述

  1. 现在讲刚刚遗留的两个小点:
    回到@Result注解,ctr+左击查看源码,发现还有One和Many:
    在这里插入图片描述
    再点进去看One的底层实现,有select和fetchtype两个属性:在这里插入图片描述
    点进Many也是一样的:
    在这里插入图片描述
    进一步:看FetchType底层,发现有三个属性。

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

在这里插入图片描述
—>>注解可以帮助实现多对一查询,不需要写在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))

这样一来就可以通过注解达到多表查询的效果啦!
【画图大师上线:代码怎么运作的】
在这里插入图片描述


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

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

不试一下怎么知道我不行?

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值