持久化框架

一、JDBC – J2EE的一部分

无论是mybatis还是hibernate,都要去想他们是怎么替代JDBC的,每一步为啥这么替代
这里写图片描述

之前:要写某一个数据库的, 就要了解这个数据库专门的API,JDBC是JAVA访问数据库平台的统一接口,统一了这个接口之后就会非常的方便。
但是缺点:JDBC这个层次的抽象还不是特别完美,JDBC必须要把sql语句写到程序里面去(但是不同的数据库之间的sql会存在微笑的差别,那移植JDBC程序会变的比较麻烦),这就是为什么hibernate流行原因(因为hibernate完全屏蔽了底层的东西,)。
这里写图片描述
解读上图:不同的JDBC需要不同的JDBC类库,连mysql就要用mysql的JDBC类库,对于编程来说接口是一样的,但是类库不一样,由不同的数据库厂商去做。

这里写图片描述
解读上图: JDBC编程步骤都很相似
1、driver实例化 ---- 用JDBC的时候首先给这个驱动(Driver类)new一个实例出来,驱动也就是这个类会自动地往DriverManager注册。
注册好之后的好处:可以通过DriverManager拿到数据库的一根连接。这个连接有个专门的接口表示它,就是connection。
找到相应数据库的 JDBC类库(就是jar包)。。找到类库之后需要找到Driver(就是驱动),这个驱动就是专门的类提供给JDBC,连接到自己的数据库上。
DriverManager:管理数据库的连接。连哪个库首先得向DriverMagager注册一下。
总之:包–类库,类(Driver.java)–驱动
怎么new实例:Class.forName();
Class—类装载器。
2、得到连接 — 接下来DriverManager拿到一个connection,并填上url,用户名密码,就得到connection了。。
3、执行sql语句的话,要先创一个语句对象,Statement
接下来执行sql即可,statement.executeQuery(“SELECT * FROM girl”);
4、返回结果集。
rs.getString() — 把某一个字段的内容当作字符串拿出来
5、遍历
6、关闭资源

这里写图片描述

1、CallableStatement(接口)是PreparedStatement的孩子,是Statement的孙子。
CallableStatement:用来调用数据库里的存储过程。

2、批处理:addBatch()

3、事务(重要) ,回滚

4、可滚动的结果集

5、可更新的结果集

这里写图片描述

二、J2EE编程基础概念,servlet,session等

Tomcat
1、 更改用户名密码,tomcat7为例子。
tomcat-users.xml 加一行即可:

2、tomcat启动报错信息一闪而过,看不到怎么办?
tomcat是java写的,出了错会报一些exception
tomcat7>bin>catalina debug
3、 tomcat的目录结构:
bin/ 放可执行的脚本程序,启动,停止等
common/ tomcat本身用的jar包以及自己要用的jar包(catalina本身和web应用可加载的类目录)
conf/ 配置文件目录
logs/ 日志目录
server/ 服务器所需的类库目录
shared/ web app共享的类库
webapps/ web应用所存放的目录 applications(webapps = web applications)
work/ tomcat的工作目录(存放jsp产生的class文件)
tmp/ 存放临时产生的文件
4、 conf/server.xml
服务器的主配置文件(改端口) connector
conf/web.xml

conf/tomcat-users.xml

xml文件:一个一个的标签,和HTML很像。这个配置文件是给tomcat用的,在启动的时候会读这个配置文件。

5、403—不允许访问(forbidden)
500 – 服务器内部错误

HTTP
这个小程序new一个socket,socket连接到tomcat server的首页。然后拿一个输出流去输出,输出的内容肯定是给tomcat server了。。 原始的tomcat就是基于BIO的撒,它的基本原理就是new一个socketserver去等待监听、、我擦
那输出什么内容呢?
HTTP要求我们要想拿一个页面的话,通过get或者post方式去拿(当然还有其它几种方式,一共有八种方式,不过最常用的就是这两种),协议规定了固定的写法。网上的东西只要我们能访问,就一定可以down的下来。
下面拿GET做实验,去拿这个页面的内容。
HTTP协议要求,你要拿页面,必须得这么写。 见下面的代码。
GET表示请求方式、/ 表示请求的URL地址(表示直接去访问127.0.0.1:8888/这个效果)、也就是这么去请求的话,会把127.0.0.1/8888/ 这个页面给返回回来。
空行表示,我请求给你的东西已经结束了。接下来就是HTTP返回的东西。

然后把返回的东西打印出来就完了。

这里写图片描述
上图解读:
“GET / HTTP1.1” – 写请求的URL地址
Content-Type:text/html — 表示请求之后,反馈回来的内容的类型应该是html类型
请求的最后是一个空行

HTTP协议比较简单(一系列的字符形式的协议)写一串字符过去,它就返回一串字符回来。

post提交方式:url地址看不到
下图解读:HTTP返回状态码
常见错误码:404找不到,403禁止访问,500服务器内部出错
这里写图片描述

三、hibernate

先save(),搞清流程

这里写图片描述

ssh
spring贯穿struts和hibernate之中(spring提倡的是面向接口编程,好处就是将来换实现的时候会非常容易),
action会调用service,会调用哪个service呢,spring会把service注入到action把DAO注入到service,spring还会针对service进行声明式的管理,这是它的作用。

两种注解,讲hibernate重点讲注解的JPA。,后面会说JPA和hibernate的关系
这里写图片描述
hibernate做日志的时候,用到了其他的日志框架,叫做slf4j
简单日志门面for java,相当于把各种各样的log的实现做了一个综合:想用log4J它可以匹配过去,想用java自带的JDK logging,它也可以匹配,想用Apache的commons logging也可以匹配。
这里写图片描述
hibernate框架的原理比较简单:把Object映射成relationship相关的SQL语句,把SQL从数据库里拿出来之后再组装成一个对象。

DDL – 数据定义语言,— DDL就是建表语句。

<property name="hbm2ddl.auto">create</property>   ddl --- 数据定义语言data definition language

在hibernate里面,操作都应该放到一个事务里面,

@Before
    public void init() {
        //创建配置对象
        Configuration config = new Configuration().configure();
        //创建服务注册对象
        ServiceRegistry serviceRegistry = new ServiceRegistryBuilder().applySettings(config.getProperties()).buildServiceRegistry();
        //创建会话工厂对象
        sessionFactory = config.buildSessionFactory(serviceRegistry);
        //会话对象
        session = sessionFactory.openSession();
        //开启事务
        transaction = session.beginTransaction();   ----  事务
    }

JDBC就是一个标准,mysql的Driver,oracle的Driver就是它的实现。
JPA也是一个标准,hibernate是它的实现。
一些术语:这里的“最佳实践”要这么做,所谓最佳实践就是最好的做法。

自己实现

ORM好处:可以用面向对象的方式去编程,不用考虑SQL语句了。。
为什么需要ORM:因为1 2,所以3。。
ORM的好处:4 和 5 。。怎么跨数据库平台? 把配置的方言改一下即可,其他不用变。
这里写图片描述
下面模拟下ORM的原理:
思路:写一配置文件,再写一个session读进来,session调用save就会根据配置文件读出来这个类相应内容再拼成SQL语句并且执行。
测试驱动开发:假设什么已经实现了,先调用,后面再实现即可。假设配置文件的东西已经读出来了,先用假数据模拟,后面实现了再替换即可。。。
下面是hibernate的基本功能的实现原理:

/**
 * 注意看这思路
 */
public class Session {
    //假设读到表名字了
    String tableName = "_Student";
    //假设读到 对应关系了,一对一可以用Map来存
    Map<String,String> cfs = new HashMap<>();
    String[] methodNames;  //方法名拿到放到这个数组里去

    public Session() {
        cfs.put("_id","id");
        cfs.put("_name","name");
        cfs.put("_age","age");
        methodNames = new String[cfs.size()];
    }
    //1、假设已经读到配置文件内容了,然后在这里写死一些值。实际上后续这些值是从配置文件中读到的。
    public void save(Student s) throws Exception{
        //2、save之前要拼一个SQL语句
        String sql = createSQL();
        //3、连上数据库,把sql的?替换掉,就可以了。。
        //难点 ps.setXXX (index,s.getXXX);  怎么拿到s.getXXX这个不好搞,方法名能拼出来,但是方法没法儿执行。所以要用反射执行,再拿值即可
        //如何先拼方法名? 如何反射拿getXXX的值?
        Class.forName("com.mysql.jdbc.Driver");
        Connection connection = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/micro_message","root","admin");
        PreparedStatement ps = connection.prepareStatement(sql);
        for (int i=0;i<methodNames.length;i++) {
            Class studentClass = s.getClass();
            Method method = studentClass.getMethod(methodNames[i]);
            Class methodTypeClass = method.getReturnType();

            if(methodTypeClass.getName().equals("java.lang.String")){
                String returnValue = (String) method.invoke(s);
                ps.setString(i+1,returnValue);
            }
            if(methodTypeClass.getName().equals("int")){
                Integer returnValue = (Integer) method.invoke(s);
                ps.setInt(i+1,returnValue);
            }
        }
        ps.executeUpdate();
        ps.close();
        connection.close();
    }

    /**
     * 拼SQL 无非就是拼字符串
     * @return
     */
    private String createSQL() {
        String column = "";
        int index = 0;

        for (String s : cfs.keySet()) {     //keyset()就是 一个set里全部是key,因为set不可重复
            String value = cfs.get(s);
            value = Character.toUpperCase(value.charAt(0)) + value.substring(1);
            methodNames[index] = "get" + value;
            column += s + ",";
            index++;
        }
        column =  column.substring(0,column.length()-1);
        System.out.println(column);

        String wenhao = "";
        for(int i=0;i<cfs.size();i++){
            wenhao += "?,";
        }
        wenhao = wenhao.substring(0,wenhao.length()-1);
        System.out.println(wenhao);
        String sql = "insert into " + tableName + " (" + column + ")" + " values ( "+ wenhao +")";
        System.out.println(sql);
        return sql;
    }
}

public class StudentTest {

    public static void main(String[] args) throws Exception{
        Student student = new Student(1,"s1",1);

        Session session = new Session();   -- 自己的session

        session.save(student);
    }

}

阶段总结

常见ORM框架:
这里写图片描述
JPA:JPA是一个接口,很多框架实现了这个接口。 JPA很类似于JDBC,它有很多个实现。但是JPA有一些细节还是没有统一起来,这时候就让这些子框架慢去优化。

hibernate常用配置

<property name="hbm2ddl.auto">create</property>
create -- 自动创表
update -- 如果表结构修改了,它也会去修改表结构。比如增加或者减少一个字段
create-drop --- 在显示关闭sessionFactory的时候,会drop掉这张表
validate --- 操作数据之前,它会检查数据库的表是不是和配置文件对应的上

先表后类 还是先类后表??? 纯理论的说,是先类后表,面向对象,先类,可以跨数据库。。但是实际中,是先表后类。

slf是一个日志的框架,具体的日志的实现可以有很多种,hibernate具体怎么实现就看你怎么配了。现在可以用slf的接口用log4j的实现。。把slf的接口换成log4j的这种做法,用到了适配器设计模式。。。如下图:
这里写图片描述
引入包,加入log4j的配置文件,就自动加载了。。

hibernate 夹在数据库驱动,获取sessionFactory可以用单例,然后用static静态语句块把它初始化了,类一加载,static块就执行了。
用注解的形式的话,就要配

<mapping class="com.entity.Students"/>

当实体类名和数据库表名不一致的时候,就用@Table:

//@Table(name = "t_students",schema = "hibernate")

实体属性名和数据库列名不一致的时候,就用@Column:

@Column(name = "_sname",length = 8)
    private String sname;//姓名

注解比配置文件方便的多,配置文件要每个属性都配,注解只需要@Id,其余默认适配。


四、hibernate简单(慕课网)

1、什么是ORM – hibernate就是一种可行的ORM框架技术

这里写图片描述
这里写图片描述
不同数据库sql语法的细微差别:同样一套SQL脚本在oracle上能运行,不一定能保证能在mysql中正常运行
分页:oracle分页用row number,mysql分页用关键字limit。
这里写图片描述
这里写图片描述

2、hibernate简介

是对JDBC轻量级的封装:hibernate最终还是通过JDBC来实现对数据库的操作。
这里写图片描述
hibernate在应用程序当中充当的角色:业务逻辑层和数据库层之间的角色,叫做持久化层。。
作用就是把程序当中生成的对象持久化到数据库当中,换句话说,就是把对象通过hibernate对象关系映射最终存到库中
这里写图片描述

其他的一些ORM框架技术:
mybatis EJB 等

3、例子程序

这里写图片描述

javabeans要遵循的设计原则:
这里写图片描述

这里写图片描述

这里写图片描述

4、hibernate进阶

主要内容:
这里写图片描述

一个个的看:

1.hibernate.cfg.xml常用配置

[外链图片转存失败(img-sJpla7lb-1568801439189)(https://img-blog.csdn.net/2018030814454059?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvdTAxMjUzMjA5Mg==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70)]

2.session简介

先看看hibernate执行流程:

这里写图片描述
大体执行流程:首先创建一个Configuration(配置)对象,它的作用就是用来读取配置文档。获取Configuration对象的目的是为了用它创建SessionFactory对象,创建SessionFactory对象的时候就会读取相应的对象关系映射文件。 然后就可以用sf对象创建session对象了,session类似于JDBC中的connection (也就是一个数据库连接对象),然后就可以执行session对象相应的方法(增删改查)了。这里要特别注意的是:在执行session的某一个方法的时候,必须要开启一个事务,那就是这些方法都要封装到事务当中,执行完这些方法之后还要提交事务,然后再关闭session。

关于session的说明:
1.不建议直接使用jdbc的connection操作数据库,而是通过使用session操作数据库;
2.session可以理解为操作数据库的对象 (它表示数据库的一个连接)
3.session与connection,是多对一的关系,每个session都有一个与之对应的connection,一个connection不同时刻可以供多个session使用;
4.把对象保存到关系型数据库中需要调用session的各种方法,如:save(),update(),delete(),createQuery()等。
1.Configuration对象:配置文件对象,读取hibernate配置文件xxx.cfg.xml
2.SessionFactory对象:读取对象/关系映射文件 xxx.hbm.xml
3.session对象:数据库链接对象,获得之后可以操作数据库。可以理解为操作数据库的对象
4.Transaction:使用session操作数据库需要开启的事务

3.transaction 简介:

不开启事务就不能将对象保存到数据库中! hibernate默认不是自动提交事务。

4.session详解

(session表示数据库的一个连接)
这里写图片描述

简单的来理解:getCurrentSession有点像是单例模式,用的都是同一个对象,事务提交之后自动关闭session
记一个:open是多个,要手动关闭session
这里写图片描述

5.hbm配置文件常用设置

scheme – 设置模式的名字,catalog – 设置目录的名称
这里写图片描述

batch-- 抓取策略:一次可以抓取多少条数
entity-name – 同一个实体类可以映射成很多表。 一般用的不多
下面看对象关系映射文件,后面就要被注解的方式替代了。
这里写图片描述

id-- 表示表的主键

这里写图片描述
generator class表示主键生成策略:
这里写图片描述

5、hibernate单表操作

主要由以下内容:
这里写图片描述
单一主键:
如果设置成native,那是自动增长的,这时候程序中手动设置主键的值是不生效的。
这里写图片描述

基本类型
日期类型和java类型的映射关系:User.hbm.xml中修改
这里写图片描述

对象类型
这里写图片描述

组件属性:(举例)
这里写图片描述

这里写图片描述

@Test
    public void testGetStudents(){
        //参数一:查哪个表对应的哪个类,获得类对象可以用反射来获得
        //参数二:标识符,也就是库的主键
        Students s = (Students) session.get(Students.class,1);
        System.out.println(s.getClass().getName());
        System.out.println(s);
    }

    @Test
    public void testLoadStudents(){
        Students s = (Students) session.load(Students.class,1);
        System.out.println(s.getClass().getName());
        System.out.println(s);
    }

    @Test
    public void testUpdateStudents(){
        Students s = (Students) session.get(Students.class,1);
        s.setGender("女");
        session.update(s);
        System.out.println(s);
    }

    @Test
    public void testDeleteStudents(){
        Students s = (Students) session.get(Students.class,1);
        session.delete(s);
    }

Get和Load的区别:
这里写图片描述
这里写图片描述

总结:
这里写图片描述
一、ORM(对象关系映射),好处是在用面向对象的思想编程的时候,可以少些和底层相关的SQL语句,方面程序的维护扩展和跨平台型 。hibernate就是一个稳定成熟的ORM框架。
二、四步:1.hibernate.cfg.xml 2.实体类和数据库表对应 3.User.hbm.xml 4.写业务测试
三、session 一个数据库连接
四、openSession()— 每次都创一新对象
五六、见内文

五 hibernate注解

1、类级别注解,前言一些概念

这里写图片描述

1、
注解干的事情就是(映射文件)*.hbm干的一切,只是换个形式。 – 就这么简单
这里写图片描述

2、
JPA – java持久化API接口,JPA注解是JAVA EE的规范和标准。 他们的关系如下图:
这里写图片描述
这里写图片描述
这里写图片描述

3、注解的分类,主要有类级别注解、属性级别注解、映射关系注解(下面分三大章节)

2、类级别注解讲解

类级别注解,分这三个一一讲解:
@Entity:实体类,代表着数据库中的一张表
@Table:
@Embeddable: 嵌入类

看回顾下hibernate配置文档 — hibernate.cfg.xml
这里写图片描述

主键:保证一张表中不出现重复记录,这里必须要指定
这里写图片描述

@Table
catalog 表示为目录的意思,schema表示为模式的意思。默认值都是为空。一般和Entity配合使用
这里写图片描述

第三个类注解:@Embeddable — 它不是一个实体类,所以它不会摄影成一张表,它是嵌入到另一个类当中作为属性而存在
这里写图片描述

3、属性级别注解

先掌握几个常用注解,标红
这里写图片描述
这里写图片描述

@Id 联合主键 ?
主键是string类型的时候,长度不能过长,@Column(length = 8) 可以指定长度
@Column表示字段,后面设置长度

//添加了这个注解就可以保证这个类可以映射成关系型数据库中的一张表、是标准的JPA注解
//要生成的表名和实体类名 不同的时候,需要指定name属性,比如:@Entity(name = "t_students")
@Entity(name = "t_students")
//@Entity
//@Table(name = "t_students",schema = "hibernate")
public class Students implements Serializable{  //这是一个声明式接口,不需要实现任何的方法
    //为了符合javabean的编程规范
    //1. 必须为公有的类
    //2. 必须提供公有的不带参数的默认的构造方法
    //3. 属性私有
    //4. 属性setter/getter封装

    @Id
    private int sid; //学号
    @Id
    @Column(length = 8)
    private String sname;//姓名
    private String gender;//性别
    private Date birthday;//生日
    private String major;//专业
//    private String address;//地址
    private Address address;

    public Students() {
    }

    public Students(int sid, String sname, String gender, Date birthday, String major, Address address) {
        this.sid = sid;
        this.sname = sname;
        this.gender = gender;
        this.birthday = birthday;
        this.major = major;
        this.address = address;
    }
 }

[外链图片转存失败(img-M0c6LWTT-1568801439202)(https://img-blog.csdn.net/20180311135413477?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvdTAxMjUzMjA5Mg==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70)]

执行主键生成策略的注解。
oracle主键没有自动增长类型,它是用序列来实现的。
@TableGenerator name表示用主键生成的表名。。?allocationSize表示给它分配空间的大小
@GeneratedValue:表示这个主键生成策略是借助一张表来实现的。
这里写图片描述
这里写图片描述

@Id
    //指定主键为自增类型。默认就是AUTO类型,也就是根据底层数据库来自动选择的,mysql默认主键生成策略就是autoincrement
    //也就是说这里     @GeneratedValue和@GeneratedValue(strategy = GenerationType.AUTO) 是等价的
    //另外,AutoIncreament的时候类型要是int类型,不能是string类型
//    @GeneratedValue
//    private int sid; //学号
    
    
    //如果id是string的话,就要用生成器generator,再@GenericGenerator构建一个生成器。二者名字要一致
    @GeneratedValue(generator = "sid")
    @GenericGenerator(name = "sid",strategy = "assigned") //assigned是手工赋值的主键生成策略
    @Column(length = 8)
    private string sid;

@Column – 表示数据库字段 。。
这里写图片描述

注意这里要两个注解一起配合使用
这里写图片描述
这里写图片描述

@Transient
这里写图片描述

4、关系映射级别注解

主要分为以下几种:
这里写图片描述

回顾下数据库实体之间的映射关系:
这里写图片描述

一对一单向外键 关联 最典型的,(父表的主键 对应 子表的外键,子 ----> 父):
这里写图片描述

5、看总结

这里写图片描述
这里写图片描述

mybatis

复习下JDBC JSP 分层的东西

sql不要写select * 。。数据库引擎会去解析一遍* ,解析出列名,

踩过的坑:jstl的包要引入jstl-1.2.jar 否则会jstl标签会报类找不到。。。

下面正式mybatis – (代替JDBC,全程想每一步怎么用mybatis替代)

mybatis大部分工作在配置文件里,首先是核心配置文件
核心配置文件:把JDBC那段东西写到配置文件里面去了,读这个配置文件自然就去加载驱动,连接

JDBC:
这里写图片描述
但是DAO层需要关心的东西只是:
这里写图片描述
怎么代替JDBC之前先看看mybatis相关知识:
DAO层的需求:DAO需要1、对象能与数据库交互 2、能执行SQL语句。mybatis就为DAO层提供了这样一个对象,该对象叫做SqlSession(核心对象)

1是什么意思:JDBC中如果要执行一条预编译的SQL语句,那就要通过一个对象为SQL语句设置预编译的参数。而SqlSession就具备这样一个能力,
2、3、4事务:JDBC是具备的,那SqlSession也具备
那如何去规划呢? 就需要一个类提供这样的功能:专门去获取SqlSession。 (注意这里面的核心配置文件可以去配置其他文件)
这里写图片描述

获得SqlSession之后就要去执行sql了,我们说mybatis大部分工作其实在配置文件里的,那SQL语句也是一样的,也要写在配置文件里面。
Message.xml这个配置文件什么目的? 配置一条SQL语句提供给SqlSession,让它读到并执行,这就是xml的目的。
SqlSession怎么知道调用哪个SQL呢,通过每一个sql标签的id,namespace。。所以这个id必须要唯一。多个文件怎么保证id不冲突呢,每个人写的有可能一样,这个时候就通过namespace区分。

jdbcType 需要填的就是 java.sql.Types这个类下的常量名字,注意不是常量值。

动态SQL拼接:参数的问题?
1、传递。。前端参数怎么传递给DAO,sql语句怎么接收?
sql.xml只能传递一个string变量。拼多个就传对象,并且sql.xml中要写变量的类所在的包名。String不用写,因为是lang包下的
2、拼接SQL。。
sql.xml中没有if语句咋办??它可以用if标签去替代。那JSP中识别是EL表达式,那mybatis的sql.xml识别什么表达式呢? 就是OGNL.
mybaits帮你拼接sql不需要留空格什么的,它自动识别,你还可以在代码中搞下缩进什么的。
这里写图片描述
这里写图片描述

引出问题:sql.xml中如果哪个sql写错了,调试不方便。之前的JDBC可以打断点,那配置文件sql.xml不能打断点,而且sql.xml的加载过程也不是我们写的,那要如何调试sql语句呢??用日志处理,最常用的日志就是log4j,mybatis也是支持它的。
使用log4j,引入jar包,加载配置文件log4j.properties。
关于properties文件:它都是key=value的形式,需要用到这个文件的时候程序会通过key去取到value

log4j.rootLogger=DEBUG,Console 跟路径下日志输出的级别和位置。级别由低到高 debug,info,warn,error
log4j.appender.Console=org.apache.log4j.ConsoleAppender
log4j.appender.Console.layout=org.apache.log4j.PatternLayout
%d表示输出日志的时间,%t表示产生日志所处的线程的名称,%p表示输出日志的级别(-5表示不足5位字符用空格补齐,负号表示补齐的空格在右边,对齐的效果),%c表示产生日志所处的类的全名包括包名,%m表示输出日志附加的信息,%n表示换行,否组会连成一片。
log4j.appender.Console.layout.ConversionPattern=%d [%t] %-5p [%c] - %m%n  --- 输出格式
log4j.logger.org.apache=INFO --- 为org.apache这个包下的日志配置 级别。

那mybatis怎么就能识别到log4j呢?它的源码已经定义好了各种各样的日志接口,项目加入哪个它就用哪个

编程技巧:逻辑代码不要混在业务代码中间, 要抽取出来。。refactor重构

mybatis常用标签:
where、sql
所以:where和条件查询配合使用,解决逗号和and多余的问题,那set和update配合使用,也是解决逗号多余的问题
这里写图片描述
标签总结:
这里写图片描述

mybatis进阶

发现问题:

messageList = sqlSession.selectList("Message.queryMessageList", message);
//namespace、与sql关联的id、传入的参数、返回值
这四个地方在编译的时候一般很难发现问题(传的是Object啊,泛型是Object啊),执行的时候就容易出错
这四种风险可以通过一种规范去约束, mybatis提供了一种手段,那就是面向接口式编程。。

所谓面向接口式编程,就是Message.xml这个SQL配置文件找到一个接口 作为它的代言人。这个配置文件又告诉其他java代码以后就不要用messageList = sqlSession.selectList(“Message.queryMessageList”, message);了,有事就调用我的接口。。 也就是意味着接口和配置文件之间的Message,queryMessageList,message和 返回值是统一的。
实现也就变成这样了:

// 通过sqlSession执行SQL语句
//            messageList = sqlSession.selectList("Message.queryMessageList", message);
            IMessage iMessage = sqlSession.getMapper(IMessage.class);  -- 这里不是用iMessage实现类的对象是接,因为没有实现类,而是一个代理实例。。。。这里获得是一个代理实例。。。。
            messageList = iMessage.queryMessageList(message); //不会去执行这个方法,而是去触发invoke()方法
 //通过以上动态代理的知识,就能解释,没有实现类的接口的 方法也可以被执行。。
/**
 * 与Message配置文件相对应的接口
 *
 * 解决四处可能存在的风险:1、namespace、2、与sql关联的id、3、传入的参数、4、返回值
 * 看看接口式编程是如何规避的
 *
 * 那接口式编程除了规避上面四个风险,还有什么作用呢?那就是和spring整合。
 * mybatis遇到spring会发生的事:
 * 1、首先核心配置文件里面 配的数据源 会交给spring去管理,也就意味着这个DB层会消失
 * 2、dao层的SqlSession会给spring托管,组织对象的代码会在service层写好再传过来。 然后接口式的代码通通由spring去实现。
 * 这样的话现在的DAO层会消失,IMessage这个接口将会变成真正的DAO层
 */
public interface IMessage {   //1、接口(也就是代言人)的名称作为namespace,类名字不可随意修改吧。而且不可能出现完全相同名字的两个接口。。

    //代言人想代言一个SQL语句提供给外面,  代言哪个sql,就写一个和id相对应的方法。
    //2、id 和 方法名 一样 .. 这里是方法名,不再是写一个id,这里方法不可以重载。。
    //3、参数 一致 。 接口定义什么就得传什么,传的不对的话会编译报错,这个问题也解决了
    //4、返回值一致。  方法规定返回什么就必须得用什么类型去接,否则编译报错。 这个问题也解决了。
    //做到这四点,这个接口就成功代言了这个SQL语句messageList = sqlSession.selectList("Message.queryMessageList", message);
    //以后就可以找这个接口了。。mybatis看到这个接口就知道你要调用哪一条SQL语句了。
    public List<Message> queryMessageList(Message message);
    
}

解释接口式编程的原理 要搞懂的几个问题:
1、这个没有实现类的接口为什么能执行这个方法?? 是通过动态代理实现的
注意:这里和最开始学的动态代理有点不同,mybatis中没有接口的实现类,所以mybatis的源码实现当中就不能调用method.invoke()方法, 那mybatis是怎么调用接口的方法的呢??见下:
下面源码可能看的也不是很明白,这里补充下:mybatis在加载配置文件的时候,已经为接口这个class创建好了一个代理工厂。如何做到??因为配置文件的namespace就是接口的全名,所以可以用Class.forName()来加载这个接口class,(这个方法传入的string 也可以是接口)。
引用博客中的话:意思明显每执行一次XxxMapper(例如:笔者例子里面的IProductMapper接口)的方法都会创建一个MapperProxy类。方法执行之前都会先去调用相应MapperProxy类里面的invoke方法

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        try {
            if (Object.class.equals(method.getDeclaringClass())) {
                return method.invoke(this, args);   -----   mybatis不执行这个
            }

            if (this.isDefaultMethod(method)) {
                return this.invokeDefaultMethod(proxy, method, args);
            }
        } catch (Throwable var5) {
            throw ExceptionUtil.unwrapThrowable(var5);
        }

        MapperMethod mapperMethod = this.cachedMapperMethod(method);   ----  执行的是这个。
        return mapperMethod.execute(this.sqlSession, args);
    }

    private MapperMethod cachedMapperMethod(Method method) { //它由代理工厂生成代理类
        MapperMethod mapperMethod = (MapperMethod)this.methodCache.get(method);
        if (mapperMethod == null) {
            mapperMethod = new MapperMethod(this.mapperInterface, method, this.sqlSession.getConfiguration());
            this.methodCache.put(method, mapperMethod);
        }

        return mapperMethod;
    }

这里写图片描述
动态代理的过程:
这里写图片描述

2、invoke方法怎么就知道 我们调用的SQL语句是哪个呢。。
这里写图片描述
解释:
我们的SQL是在配置文件里,我们在获得sqlsession之前,调用了mybatis加载配置文件的方法,加载总的配置文件的时候也加载了其他的SQL配置文件,获得的这些配置信息mybatis会存到configuration对象中。
有了配置信息就好说了,当代理实例调用接口方法的时候,如果接口方法和配置信息能对应上就可以了。(上面的接口式编程的知识,他们是可以对应上的。)
接口的全名称和调用的方法名和invoke方法里是可以拿到的,这是动态代理的知识。有了这些信息也就能拿到SQL配置文件信息了。然后在invoke方法里面就能代替 sqlSession.selectList这种写法了。
这里写图片描述

3、结论
说了这么多,就是为了证明 下面二者 效果是一样的。
这里写图片描述

分页

标签中

注意点:resultMap是需要配结果集的列 和 java对象属性 之前的对应关系。resultType是直接返回的java类型。

基本报错:就是程序抛异然后终止了,debug就是找出抛异常的那行代码,然后解决错误。

bug:Type interface IMessage is not known to the MapperRegistry.
接口没有被找到,说明获取代理实例失败,debug思路:1、xml与接口没有联系起来 2、要不是没注册xml文件

拦截器

每个列表的分页都是一样的,重复性工作太多。先看看DAO层的封装。
通过log4j可以把SQL打印出来,是不是可以通过其他什么办法拿到mybatis的SQL语句,然后做处理呢。那就是 拦截器。。。
mybatis的拦截器提供了这样一种代理的能力,可以代替别人做一些事情,可以利用拦截器代替所有开发人员实现分页功能(类似于所有的代购公司,帮大家买火车票)
站在代购公司的角度想:首先要拦住想买票的人,拦下之后替别人做的事是明确的(只能帮别人买票),最后把票交回去。
mybatis拦截器也是一样的,如下图:
把列表查询拦截下来,然后代替它去完成分页,怎么代替完成呢?把原有查询改进成分页查询再交回主角即可。
这里写图片描述

------ 1、要买票的人 2、买票才拦截 3、 过早或过迟的拦截都不合适
这里写图片描述

怎么做呢?
拦截器可以在不改变源码的情况下,改变mybatis的一些原本的行为。 mybatis源码拿到SQL语句执行之前,拦截下,再偷梁换柱把要执行的SQL语句换成自己的分页语句就OK了。
具体实现?
根据实现功能的不同可以实现多种拦截器(提供各个服务的代购公司)

小插曲:protected int id; 不是同包或者子类,这个属性就拿不到。。

一对多查询

主表–command (一对多) 子表command_content

--- 子表的创建方式
CREATE TABLE `command_content` (
	`ID` INT(11) NOT NULL,
	`CONTENT` VARCHAR(2048) NULL DEFAULT NULL,
	`COMMAD_ID` INT(11) NULL DEFAULT NULL,
	PRIMARY KEY (`ID`),
	INDEX `FK_command_content_command` (`COMMAD_ID`),
	CONSTRAINT `FK_command_content_command` FOREIGN KEY (`COMMAD_ID`) REFERENCES `command` (`ID`)
)
ENGINE=InnoDB
;

查回来想要的效果:一对多想要展现的就是这种列表形式(查询页,修改页都要有这种需求)
这里写图片描述
这里写图片描述

注意点:
主表和子表,都需要java中 对应两个实体类,还得有两个数据库配置文件。Command.xml和CommandContext.xml。 但是这里是查询主表的主体,所以SQL要写在主表的xml文件中,然后将查回来的数据填充到主表的各个属性,同时填充子表的列表就可以了。
left join – 无论子表有没有内容,主要都应该出来。。
那xml中怎么表示一对多的关系呢? java类中是 把子表的一个集合作为主表的一个属性 来反应这种关系的,那xml配置文件同样如此。。。用的是标签

注意:这里的column不是数据库表中的列名,而是查回来的结果集的列名,结果集的列名可能用的别名,用了别名的话,那column就要和别名一致。而mybatis的resultMap中是不识别a.id b.id 前面的a和b的,如果这么写就会报错,mybatis不知道id是a的还是b的。如何解决? 两个id至少要有一个要起别名。。

<?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="Command">
    <resultMap type="com.imooc.bean.Command" id="Command">
        <id column="C_ID" jdbcType="INTEGER" property="id"/>
        <result column="NAME" jdbcType="VARCHAR" property="name"/>
        <result column="DESCRIPTION" jdbcType="VARCHAR" property="description"/>
        --- 这里property是java类中的属性,这里就没有column列了,resultMap是跨文件的引用(引用的是子表xml的namespace.id)
        <collection property="contentList"  resultMap="CommandContent.Content"/>
    </resultMap>

    <select id="queryCommandList" parameterType="com.imooc.bean.Command" resultMap="Command">
        select a.ID C_ID,a.NAME,a.DESCRIPTION,bID,b.CONTENT,b.CONTENT_ID
        from command a LEFT JOIN command_context b
        on a.ID=b.COMMAND_ID
        <where>
            <if test="name != null and !&quot;&quot;.equals(name.trim())">
                and a.name=#{name}
            </if>
            <if test="message.description != null and !&quot;&quot;.equals(message.description.trim())">
                and a.DESCRIPTION like '%' #{description} '%'
            </if>
        </where>
        order by ID limit #{page.dbIndex},#{page.dbNumber}
    </select>
</mapper>

通过sql查回来其实是下面这样的,主表的信息是反复出现的:
这里写图片描述
但是通过mybatis 通过配置文件转换之后,通过一对多展现出来的其实这样的:
这里写图片描述

批量新增

mysql的SQL语法是 ,再后面加就行,用逗号隔开。

DAO层的循环写到CommandContext.xml来了
<insert id="insertBatch" parameterType="java.util.List">
        insert into COMMAND_CONTENT(CONTENT,COMMAND_ID) values
        <foreach collection="list" item="item" separator=",">
            (#{item.content},#{item.commandId})
        </foreach>
    </insert>
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值