hibernate 执行sql在哪个类_Hibernate框架入门

ORM全称是object/rational mapping,简单讲就是建立面向对象语言(OOP)和结构化查询语言(SQL)之间的关系。

这种关系其实并不是双向的。一般指的是使用面向对象语言来描述SQL,而不是使用SQL来支持面向对象语言。

这种情况主要是有两个原因导致的:

  1. OOP的结构要比SQL复杂的多,使用简单语言描述复杂语言本身就比较困难;

  2. ORM主要是方便开发人员构思软件体系结构和简化具体编码而发明的。实际项目中,大量的工作也集中在使用面向对象语言实现软件功能这部分。

所以,实际上的ORM框架,基本指的都是通过框架将OOP自动翻译成SQL并执行,开发人员的工作界面保持在面向对象语言上。

核心问题

ORM框架的核心问题其实也很简单,用一句话来概括就是:如何保证弱表现力的SQL能够支持OOP的全部特性?

典型场景比如:面向对象语言具有继承和多态的特性。可以使用面向对象语言定义父类,并通过子类实现这些接口,产生新的对象。

但SQL不行,SQL底层是关系型数据库。数据库表之间是不存在谁继承谁的概念的。数据库表之间的关系只有一种,即通过使用外键,让两张表之间产生关联关系。

我们在面向对象语言中经常见到比如父类是Animal,子类是Dog的这种设计和实现。但是我们在数据库中从来没有见过"父表"是Animal,而"子表"是Dog的这种场景。其实数据库中根本就不存在父表或子表之说……

Hibernate

若谈起ORM,则Hibernate是必然会被提及的框架。Hibernate的核心功能便是对应用封装数据库操作,让访问数据库的操作尽可能像操作本地对象一样简单。

JPA的全称叫Java Persistence API,是Java中定义的关于数据持久化的一套规范。

可以简单地认为Hibernate是JPA的一种实现。当然,Hibernate除实现了JPA规范定义的功能之外,还包含了很多其特有的特性。

ORM框架的核心抽象叫EntityManager,它用于处理所有对Entity对象的操作。

Entity是什么呢?Entity其实是沟通面向对象语言和结构化语言的桥梁。Entity本身是一个普通的Java对象。所以Java中所有操作对象的功能,在Entity上都能使用。同时,Entity上又定义了具体的SQL操作,对Entity的操作,最终会通过EntityManager转换成对数据库表的操作。

下面将基于Hibernate创建一个简单的ORM应用,初步体验ORM框架的魅力。

创建数据库

可以使用任意关系型数据库,本文中使用了MySQL的开源版本MariaDB。

在数据库中创建名为hibernate的schema:

CREATE SCHEMA `hibernate` DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci ;

创建应用

使用Maven工程,创建一个名为OrmTest的Maven工程,修改pom.xml文件,调整下Java编译器的版本为1.8:

<?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.0modelVersion>

    <groupId>org.examplegroupId>
    <artifactId>OrmTestartifactId>
    <version>1.0-SNAPSHOTversion>

    <properties>
        <java.version>1.8java.version>
        <maven.compiler.source>${java.version}maven.compiler.source>
        <maven.compiler.target>${java.version}maven.compiler.target>
        <project.build.sourceEncoding>UTF-8project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8project.reporting.outputEncoding>
    properties>
project>

引入依赖

修改pom文件,增加依赖包:

<?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.0modelVersion>

    <groupId>org.examplegroupId>
    <artifactId>OrmTestartifactId>
    <version>1.0-SNAPSHOTversion>

    <properties>
        <java.version>1.8java.version>
        <maven.compiler.source>${java.version}maven.compiler.source>
        <maven.compiler.target>${java.version}maven.compiler.target>
        <project.build.sourceEncoding>UTF-8project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8project.reporting.outputEncoding>
    properties>

    <dependencies>
        <dependency>
            <groupId>org.hibernategroupId>
            <artifactId>hibernate-entitymanagerartifactId>
            <version>5.4.22.Finalversion>
        dependency>
        <dependency>
            <groupId>mysqlgroupId>
            <artifactId>mysql-connector-javaartifactId>
            <version>8.0.21version>
        dependency>
        <dependency>
            <groupId>org.apache.logging.log4jgroupId>
            <artifactId>log4j-coreartifactId>
            <version>2.13.3version>
        dependency>
        <dependency>
            <groupId>org.junit.jupitergroupId>
            <artifactId>junit-jupiter-apiartifactId>
            <version>5.7.0version>
            <scope>testscope>
        dependency>
    dependencies>
project>

一共引入了四个依赖:

  • hibernate-entitymanager:Hibernate工程ORM功能的核心包;
  • mysql-connector-java:是MySQL的JDBC驱动,用于连接数据库;
  • log4j-core:主要目的是将Hibernate执行的SQL语句打印出来,便于调试;
  • junit-jupiter-api:Junit 5,用于编写测试用例;

配置log4j

在src/main/resources下面增加log4j2.xml文件,该文件是log4j的配置文件。在文件中增加如下配置,这样就能将Hibernate的相关日志打印在控台上了:

<?xml  version="1.0" encoding="UTF-8"?>
<Configuration status="WARN">
    <Appenders>
        <Console name="Console" target="SYSTEM_OUT">
            <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n" />
        Console>
    Appenders>
    <Loggers>
        
        <Logger name="org.hibernate" level="info" additivity="false">
            <AppenderRef ref="Console" />
        Logger>
        
        <Logger name="org.hibernate.SQL" level="debug" additivity="false">
            <AppenderRef ref="Console" />
        Logger>
        
        <Logger name="org.hibernate.type.descriptor.sql.BasicBinder" level="trace" additivity="false">
            <AppenderRef ref="Console" />
        Logger>
        
        <Logger name="org.hibernate.type.descriptor.sql.BasicExtractor" level="trace" additivity="false">
            <AppenderRef ref="Console" />
        Logger>

        <Root level="info">
            <AppenderRef ref="Console" />
        Root>
    Loggers>
Configuration>

创建Entity

创建org.example.hibernate.model package,在该package下创建Message类,代码如下:

package org.example.hibernate.model;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;

@Entity
public class Message {
    @Id @GeneratedValue
    private Long id;
    private String text;
    public String getText() {
        return this.text;
    }
    public void setText(String text) {
        this.text = text;
    }
}

@Entity表示这是一个Entity对象,它告诉Hibernate需要在数据库中创建一个名为MESSAGE的表。@Id@GeneratedValue是用于标注主键的,它告诉Hibernate需要在MESSAGE表中增加一个名为ID的列,且这个列是数据库的主键。

启用Hibernate

创建org.example.hibernate.config package,在该package下创建一个SessionFactorySingleton类。这个类我们准备用单例实现它,它维护一个静态的SessionFactory对象。

package org.example.hibernate.config;

import org.hibernate.SessionFactory;
import org.hibernate.boot.Metadata;
import org.hibernate.boot.MetadataBuilder;
import org.hibernate.boot.MetadataSources;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
import org.hibernate.service.ServiceRegistry;

public class SessionFactorySingleton {
    private static SessionFactory INSTANCE = null;
    public static SessionFactory getInstance() {
        if (INSTANCE == null) {
            synchronized (SessionFactorySingleton.class) {
                if (INSTANCE == null) {
                    init();
                }
            }
        }
        return INSTANCE;
    }

    private static void init() {
        StandardServiceRegistryBuilder serviceRegistryBuilder = new StandardServiceRegistryBuilder();
        serviceRegistryBuilder
                .applySetting("hibernate.connection.driver_class", "com.mysql.cj.jdbc.Driver")
                .applySetting("hibernate.connection.url", "jdbc:mysql://localhost:3306/hibernate?characterEncoding=utf8&useSSL=false")
                .applySetting("hibernate.connection.username", "root")
                .applySetting("hibernate.connection.password", "root1234")
                .applySetting("hibernate.format_sql", "true")
                .applySetting("hibernate.use_sql_comments", "true")
                .applySetting("hibernate.hbm2ddl.auto", "create-drop");
        ServiceRegistry serviceRegistry = serviceRegistryBuilder.build();

        MetadataSources metadataSources = new MetadataSources(serviceRegistry);
        metadataSources.addAnnotatedClass(org.example.hibernate.model.Message.class);
        MetadataBuilder metadataBuilder = metadataSources.getMetadataBuilder();

        Metadata metadata = metadataBuilder.build();
        INSTANCE = metadata.buildSessionFactory();
    }
}

这段代码的核心逻辑在init()方法中,主要是初始化Hibernate相关参数,并构造一个SessionFactory对象。

测试代码

在src/test/java目录下,创建org.example.hibernate.config package。在该package下创建SessionFactorySingletonTest测试类:

package org.example.hibernate.config;

import org.example.hibernate.model.Message;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.junit.jupiter.api.Test;

import java.util.List;

import static org.junit.jupiter.api.Assertions.assertEquals;

class SessionFactorySingletonTest {
    @Test
    public void testHibernate() {
        SessionFactory sessionFactory = SessionFactorySingleton.getInstance();
        Session session = sessionFactory.openSession();
        session.beginTransaction();

        Message message = new Message();
        message.setText("This is the text stored in DB");
        session.persist(message);

        session.getTransaction().commit();
        session.close();

        session = sessionFactory.openSession();
        session.beginTransaction();
        List messages = session.createCriteria(Message.class).list();
        assertEquals(messages.size(), 1);
        assertEquals(messages.get(0).getText(), "This is the text stored in DB");
        session.getTransaction().commit();
        session.close();
    }
}

这个测试类首先向数据库中提交了一个Message对象。然后它在另一个Session中查询了该对象。

测试

完整的工程结构如下图所示:

ba7921b6b1592bac6ca637f5187798d9.png

运行程序,在控制台上会输出如下信息:

/Library/Java/JavaVirtualMachines/jdk-14.0.2.jdk/Contents/Home/bin/java -ea -Didea.test.cyclic.buffer.size=1048576 -javaagent:/Applications/IntelliJ IDEA.app/Contents/lib/idea_rt.jar=60246:/Applications/IntelliJ IDEA.app/Contents/bin -Dfile.encoding=UTF-8 -classpath /Applications/IntelliJ IDEA.app/Contents/lib/idea_rt.jar:/Users/future/.m2/repository/org/junit/platform/junit-platform-launcher/1.7.0/junit-platform-launcher-1.7.0.jar:/Users/future/.m2/repository/org/junit/platform/junit-platform-engine/1.7.0/junit-platform-engine-1.7.0.jar:/Users/future/.m2/repository/org/junit/jupiter/junit-jupiter-engine/5.7.0/junit-jupiter-engine-5.7.0.jar:/Applications/IntelliJ IDEA.app/Contents/plugins/junit/lib/junit5-rt.jar:/Applications/IntelliJ IDEA.app/Contents/plugins/junit/lib/junit-rt.jar:/Volumes/data/idea/OrmTest/target/test-classes:/Volumes/data/idea/OrmTest/target/classes:/Users/future/.m2/repository/org/hibernate/hibernate-entitymanager/5.4.22.Final/hibernate-entitymanager-5.4.22.Final.jar:/Users/future/.m2/repository/org/jboss/logging/jboss-logging/3.3.2.Final/jboss-logging-3.3.2.Final.jar:/Users/future/.m2/repository/org/hibernate/hibernate-core/5.4.22.Final/hibernate-core-5.4.22.Final.jar:/Users/future/.m2/repository/org/javassist/javassist/3.24.0-GA/javassist-3.24.0-GA.jar:/Users/future/.m2/repository/antlr/antlr/2.7.7/antlr-2.7.7.jar:/Users/future/.m2/repository/org/jboss/jandex/2.1.3.Final/jandex-2.1.3.Final.jar:/Users/future/.m2/repository/com/fasterxml/classmate/1.5.1/classmate-1.5.1.jar:/Users/future/.m2/repository/javax/activation/javax.activation-api/1.2.0/javax.activation-api-1.2.0.jar:/Users/future/.m2/repository/javax/xml/bind/jaxb-api/2.3.1/jaxb-api-2.3.1.jar:/Users/future/.m2/repository/org/glassfish/jaxb/jaxb-runtime/2.3.1/jaxb-runtime-2.3.1.jar:/Users/future/.m2/repository/org/glassfish/jaxb/txw2/2.3.1/txw2-2.3.1.jar:/Users/future/.m2/repository/com/sun/istack/istack-commons-runtime/3.0.7/istack-commons-runtime-3.0.7.jar:/Users/future/.m2/repository/org/jvnet/staxex/stax-ex/1.8/stax-ex-1.8.jar:/Users/future/.m2/repository/com/sun/xml/fastinfoset/FastInfoset/1.2.15/FastInfoset-1.2.15.jar:/Users/future/.m2/repository/org/dom4j/dom4j/2.1.3/dom4j-2.1.3.jar:/Users/future/.m2/repository/org/hibernate/common/hibernate-commons-annotations/5.1.0.Final/hibernate-commons-annotations-5.1.0.Final.jar:/Users/future/.m2/repository/javax/persistence/javax.persistence-api/2.2/javax.persistence-api-2.2.jar:/Users/future/.m2/repository/net/bytebuddy/byte-buddy/1.10.10/byte-buddy-1.10.10.jar:/Users/future/.m2/repository/org/jboss/spec/javax/transaction/jboss-transaction-api_1.2_spec/1.1.1.Final/jboss-transaction-api_1.2_spec-1.1.1.Final.jar:/Users/future/.m2/repository/mysql/mysql-connector-java/8.0.21/mysql-connector-java-8.0.21.jar:/Users/future/.m2/repository/com/google/protobuf/protobuf-java/3.11.4/protobuf-java-3.11.4.jar:/Users/future/.m2/repository/org/apache/logging/log4j/log4j-core/2.13.3/log4j-core-2.13.3.jar:/Users/future/.m2/repository/org/apache/logging/log4j/log4j-api/2.13.3/log4j-api-2.13.3.jar:/Users/future/.m2/repository/org/junit/jupiter/junit-jupiter-api/5.7.0/junit-jupiter-api-5.7.0.jar:/Users/future/.m2/repository/org/apiguardian/apiguardian-api/1.1.0/apiguardian-api-1.1.0.jar:/Users/future/.m2/repository/org/opentest4j/opentest4j/1.2.0/opentest4j-1.2.0.jar:/Users/future/.m2/repository/org/junit/platform/junit-platform-commons/1.7.0/junit-platform-commons-1.7.0.jar com.intellij.rt.junit.JUnitStarter -ideVersion5 -junit5 org.example.hibernate.config.SessionFactorySingletonTest,testHibernate

17:12:30.519 [main] INFO  org.hibernate.Version - HHH000412: Hibernate ORM core version 5.4.22.Final
17:12:30.784 [main] INFO  org.hibernate.annotations.common.Version - HCANN000001: Hibernate Commons Annotations {5.1.0.Final}
17:12:30.974 [main] WARN  org.hibernate.orm.connections.pooling - HHH10001002: Using Hibernate built-in connection pool (not for production use!)
17:12:30.984 [main] INFO  org.hibernate.orm.connections.pooling - HHH10001005: using driver [com.mysql.cj.jdbc.Driver] at URL [jdbc:mysql://localhost:3306/hibernate?characterEncoding=utf8&useSSL=false]
17:12:30.985 [main] INFO  org.hibernate.orm.connections.pooling - HHH10001001: Connection properties: {password=****, user=root}
17:12:30.985 [main] INFO  org.hibernate.orm.connections.pooling - HHH10001003: Autocommit mode: false
17:12:30.989 [main] INFO  org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl - HHH000115: Hibernate connection pool size: 20 (min=1)
17:12:31.450 [main] INFO  org.hibernate.dialect.Dialect - HHH000400: Using dialect: org.hibernate.dialect.MySQL55Dialect
17:12:32.703 [main] DEBUG org.hibernate.SQL - 
    
    drop table if exists hibernate_sequence
17:12:32.707 [main] INFO  org.hibernate.orm.connections.access - HHH10001501: Connection obtained from JdbcConnectionAccess [org.hibernate.engine.jdbc.env.internal.JdbcEnvironmentInitiator$ConnectionProviderJdbcConnectionAccess@465b38e6] for (non-JTA) DDL execution was not in auto-commit mode; the Connection 'local transaction' will be committed and the Connection will be set into auto-commit mode.
17:12:32.757 [main] DEBUG org.hibernate.SQL - 
    
    drop table if exists Message
17:12:32.773 [main] DEBUG org.hibernate.SQL - 
    
    create table hibernate_sequence (
       next_val bigint
    ) engine=InnoDB
17:12:32.776 [main] INFO  org.hibernate.orm.connections.access - HHH10001501: Connection obtained from JdbcConnectionAccess [org.hibernate.engine.jdbc.env.internal.JdbcEnvironmentInitiator$ConnectionProviderJdbcConnectionAccess@4ace284d] for (non-JTA) DDL execution was not in auto-commit mode; the Connection 'local transaction' will be committed and the Connection will be set into auto-commit mode.
17:12:32.828 [main] DEBUG org.hibernate.SQL - 
    
    insert into hibernate_sequence values ( 1 )
17:12:32.845 [main] DEBUG org.hibernate.SQL - 
    
    create table Message (
       id bigint not null,
        text varchar(255),
        primary key (id)
    ) engine=InnoDB
17:12:32.915 [main] INFO  org.hibernate.engine.transaction.jta.platform.internal.JtaPlatformInitiator - HHH000490: Using JtaPlatform implementation: [org.hibernate.engine.transaction.jta.platform.internal.NoJtaPlatform]
17:12:33.065 [main] DEBUG org.hibernate.SQL - 
    select
        next_val as id_val 
    from
        hibernate_sequence for update
            
17:12:33.084 [main] DEBUG org.hibernate.SQL - 
    update
        hibernate_sequence 
    set
        next_val= ? 
    where
        next_val=?
17:12:33.125 [main] DEBUG org.hibernate.SQL - 
    /* insert org.example.hibernate.model.Message
        */ insert 
        into
            Message
            (text, id) 
        values
            (?, ?)
17:12:33.129 [main] TRACE org.hibernate.type.descriptor.sql.BasicBinder - binding parameter [1] as [VARCHAR] - [This is the text stored in DB]
17:12:33.130 [main] TRACE org.hibernate.type.descriptor.sql.BasicBinder - binding parameter [2] as [BIGINT] - [1]
17:12:33.164 [main] WARN  org.hibernate.orm.deprecation - HHH90000022: Hibernate's legacy org.hibernate.Criteria API is deprecated; use the JPA javax.persistence.criteria.CriteriaQuery instead
17:12:33.183 [main] DEBUG org.hibernate.SQL - 
    /* criteria query */ select
        this_.id as id1_0_0_,
        this_.text as text2_0_0_ 
    from
        Message this_
17:12:33.189 [main] TRACE org.hibernate.type.descriptor.sql.BasicExtractor - extracted value ([id1_0_0_] : [BIGINT]) - [1]
17:12:33.193 [main] TRACE org.hibernate.type.descriptor.sql.BasicExtractor - extracted value ([text2_0_0_] : [VARCHAR]) - [This is the text stored in DB]
Process finished with exit code 0

MariaDB

在数据库中,可以看到新创建了一个名为MESSAGE的表:

178241be064be8b4f664fb48acb62b47.png

查看MESSAGE表数据,可以看到测试用例插入的那条记录:

5d21769a6e036825a3f237e5f2fa5f2e.png
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值