JavaRush /Java 博客 /Random-ZH /Maven、Spring、MySQL、Hibernate 和第一个 CRUD 应用程序简介(第 3 部分)
Макс
第 41 级

Maven、Spring、MySQL、Hibernate 和第一个 CRUD 应用程序简介(第 3 部分)

已在 Random-ZH 群组中发布
下午好。在这篇文章中,我想分享我在创建一个简单的 CRUD 应用程序的过程中第一次接触到 Maven、Spring、Hibernate、MySQL 和 Tomcat 等东西。这是 4 的第三部分。本文主要面向那些已经完成 30-40 级,但尚未超越纯 Java 并刚刚开始(或即将开始)进入开放世界的人所有这些技术、框架和其他陌生的词语。 Maven、Spring、MySQL、Hibernate 和第一个 CRUD 应用程序简介(第 3 部分)- 1这是文章《Maven、Spring、MySQL、Hibernate 简介和第一个 CRUD 应用程序》的第三部分。前面的部分可以通过以下链接查看:

内容:

创建并连接数据库

好吧,是时候开始处理数据库了。在连接Hibernate并思考它应该如何在那里工作之前,首先让我们看看数据库本身,即 让我们创建它、连接它、制作它并填写标志。我们将使用DBMS(数据库管理系统)MySQL(当然,您必须首先下载并安装)。 SQL(结构化查询语言)是一种声明性编程语言,用于创建、修改和操作关系数据库中的数据。在此类数据库中,数据以表的形式存储。应用程序如何与数据库通信(将SQL查询传输到数据库并返回结果)。为此,Java 提供了JDBC(Java 数据库连接)这样的东西,简单地说,它是一组用于处理数据库的接口和类。要与数据库交互,您需要创建一个连接;为此,包java.sql有一个类Connection。建立连接的方法有多种,例如可以使用getConnection类方法DriverManager。但是与数据库的交互并不是直接进行的,因为数据库有很多,而且各不相同。因此,它们每个都有自己的 JDBC 驱动程序。使用该驱动程序,可以建立与数据库的连接。所以,首先,为了以后不被这个分心,我们先安装MySQL驱动。让我们添加pom.xml以下依赖项:
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>8.0.11</version>
</dependency>
现在让我们创建数据库。 查看 -> 工具窗口 -> 数据库- 将打开数据库面板。 新建(绿色+)->数据源->MySQL - 将打开一个窗口,您需要在其中指定用户名和密码,我们在安装MySQL时设置它们(例如,我使用root和root)。端口(MySQL 默认为 3306)、名称等。保持原样。您可以使用“测试连接”按钮测试连接。 Maven、Spring、MySQL、Hibernate 和第一个 CRUD 应用程序简介(第 3 部分)- 2单击“确定”,现在我们已连接到 MySQL 服务器。接下来,让我们创建一个数据库。为此,您可以在打开的控制台中编写一个脚本:
CREATE DATABASE test
单击“执行”,数据库已准备就绪,现在您可以连接它。为此,请返回“数据源属性”并在“数据库”字段中输入数据库名称(test),然后再次输入用户名和密码,然后单击“确定”。现在我们需要制作一张桌子。您可以使用图形工具,但第一次,可能值得手动编写脚本来看看它是什么样子:
USE test;

CREATE TABLE films
(
  id int(10) PRIMARY KEY AUTO_INCREMENT,
  title VARCHAR(100) NOT NULL,
  year int(4),
  genre VARCHAR(20),
  watched BIT DEFAULT false  NOT NULL
)
COLLATE='utf8_general_ci';
CREATE UNIQUE INDEX films_title_uindex ON films (title);

INSERT INTO `films` (`title`,`year`,`genre`, watched)
VALUES
  ("Inception", 2010, "sci-fi", 1),
  ("The Lord of the Rings: The Fellowship of the Ring", 2001, "fantasy", 1),
  ("Tag", 2018, "comedy", 0),
  ("Gunfight at the O.K. Corral", 1957, "western", 0),
  ("Die Hard", 1988, "action", 1);
创建一个表,其名称films为 columns等idtitle对于每一列,都会指示类型(括号中是最大输出大小)。
  • PRIMARY KEY- 这是主键,用于唯一标识表中的记录(意味着唯一性)
  • AUTO_INCREMENT— 该值将自动生成(当然它将是非零的,因此您不必指定它)
  • NOT NULL- 这里一切也很明显,它不能为空
  • DEFAULT— 设置默认值
  • COLLATE- 编码
  • CREATE UNIQUE INDEX— 使该领域独一无二
  • INSERT INTO— 添加一条记录到表中
结果是这样的标志: 也许值得尝试连接到它,只是暂时,与我们的 Web 应用程序分开。如果出现问题怎么办,我们会立即解决。否则,稍后我们将连接Hibernate,做一些事情,配置,修补,如果我们在某个地方搞砸了,那么至少我们会知道问题不在这里。好吧,为了检查连接,我们main暂时创建一个方法。原则上,你可以把它放在任何地方,甚至在控制器类中,甚至在模型或配置中,都没关系,你只需要使用它来确保连接一切正常,然后就可以删除它。但为了更加小心,让我们为它创建一个单独的类Main
package testgroup.filmography;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;

public class Main {
    public static void main(String[] args) {
        String url = "jdbc:mysql://localhost:3306/test";
        String username = "root";
        String password = "root";
        System.out.println("Connecting...");

        try (Connection connection = DriverManager.getConnection(url, username, password)) {
            System.out.println("Connection successful!");
        } catch (SQLException e) {
            System.out.println("Connection failed!");
            e.printStackTrace();
        }
    }
}
这里一切都很简单,我们设置数据库的连接参数并尝试创建连接。让我们启动这个main并看一下。因此,我遇到了异常、一些时区问题以及一些有关 SSL 的其他警告。浏览互联网后,您可以发现这是一个相当普遍的问题,并且当使用不同版本的驱动程序(mysql-connector-java)时,它可以发誓不同。例如,我通过实验发现,使用5.1.47版本时,没有因为时区的原因出现异常,连接正常创建,但仍然弹出SSL警告。对于其他一些版本,似乎存在有关 SSL 的例外,而不仅仅是警告。好吧,这不是重点。你可以尝试单独处理这个问题,但我们现在不讨论它。解决这个问题的方法非常简单,您需要在url中指定其他参数,即serverTimezone问题是否与时区有关,以及useSSL是否与 SSL 有关:
String url = "jdbc:mysql://localhost:3306/test?serverTimezone=Europe/Minsk&useSSL=false";
现在我们已经设置了时区并禁用了 SSL。我们再次启动它main,瞧 - 连接成功!好吧,太好了,我们找到了如何创建连接。该类Main已基本完成其任务,可以将其删除。

ORM 和 JPA

好的方面,为了更好的理解,最好是按顺序开始熟悉数据库,从一开始,没有任何hibernate之类的东西。因此,最好找到一些指南并首先尝试使用 JDBC 类、手动编写 SQL 查询等。好吧,让我们立即转向ORM模型。这是什么意思?当然,再次建议单独阅读这一点,但我将尝试简要描述它。ORM(Object-Relational Mapping 或对象关系映射)是一种将对象映射到关系数据库结构的技术,即 将我们的 Java 对象表示为表行。感谢 ORM,您不必担心编写 SQL 脚本,而可以专注于处理对象。如何使用它。Java还有一个伟大的东西,JPA(Java Persistence API),它实现了ORM概念。JPA 就是这样一个规范;它描述了对象的需求,它定义了使用数据库的各种接口和注释。JPA本质上是一种描述,一种标准。因此,具体的实现有很多,其中之一(也是最流行的之一)就是Hibernate,它是这个框架的精髓。Hibernate 是 JPA 规范的实现,旨在解决对象关系映射 (ORM) 问题。我们需要将这整个事情连接到我们的项目中。另外,为了让我们的 Spring 不袖手旁观,也参与到数据库的所有运动中,我们需要连接更多的模块,因为 我们从spring-webmvc依赖项中获得的所有内容都不足以满足此目的。我们还需要spring-jdbc来处理数据库,spring-tx来支持事务,以及spring-orm来处理 Hibernate。让我们添加依赖项pom.xml
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-orm</artifactId>
    <version>5.1.1.RELEASE</version>
</dependency>

<dependency>
    <groupId>org.hibernate</groupId>
    <artifactId>hibernate-core</artifactId>
    <version>5.3.7.Final</version>
</dependency>
这两个依赖就足够了。javax.persistence-api将与hibernate-core一起到达,spring-jdbcspring-txspring-orm一起到达。

实体

所以我们希望类对象能够Film存储在数据库中。为此,类必须满足许多条件。在 JPA 中有一个实体( Entity)。实体类是一个普通的POJO类,具有私有字段以及它们的 getter 和 setter。它必须有一个不带参数的非私有构造函数(或默认构造函数),并且必须有一个主键,即 唯一标识数据库中此类的每个记录的东西。您还可以单独阅读此类课程的所有要求。让我们Film使用 JPA 注释使我们的类成为一个实体:
package testgroup.filmography.model;

import javax.persistence.*;

@Entity
@Table(name = "films")
public class Film {

    @Id
    @Column(name = "id")
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private int id;

    @Column(name = "title")
    private String title;

    @Column(name = "year")
    private int year;

    @Column(name = "genre")
    private String genre;

    @Column(name = "watched")
    private boolean watched;

    // + getters and setters
}
  • @Entity- 表明该类是一个实体。
  • @Table- 指向一个特定的表来显示该实体。
  • @Id— 表示该字段是主键,即 该属性将用于标识每个唯一的条目。
  • @Column— 将字段连接到表列。如果字段名和表列名相同,则可以省略。
  • @GeneratedValue— 该属性将自动生成;您可以在括号中指定如何生成。我们现在无法理解不同策略到底是如何运作的。知道在这种情况下每个新值都会比前一个值增加 1 就足够了。
对于每个属性,您还可以指定许多其他内容,例如,什么应该是非零或唯一的、指定默认值、最大大小等。如果您需要基于此类生成表,这将很有用;Hibernate 有此选项。但我们已经自己创建了表并配置了所有属性,所以我们可以不用它。 一个小笔记。Hibernate 文档建议不要在字段上使用注释,而是在 getter 上使用注释。然而,这些方法之间的差异非常微妙,在我们的简单应用程序中这不会产生任何影响。另外,大多数人无论如何都会在字段上方添加注释。因此,就这样吧,看起来更整洁。

休眠属性

好吧,让我们开始设置 Hibernate。首先,让我们将一些信息(例如用户名和密码、网址和其他内容)放入一个单独的文件中。当然,您可以直接在类中将它们指定为常规行,就像我们检查连接时所做的那样(String username = "root";然后将其传递给创建连接的方法)。但将此类静态数据存储在某个文件中仍然更正确property。例如,如果您需要更改数据库,那么您不必遍历所有类并查找使用它的位置;更改此文件中的值一次就足够了。让我们在资源目录中创建一个db.properties文件:
jdbc.driverClassName=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/test?serverTimezone=Europe/Minsk&useSSL=false
jdbc.username=root
jdbc.password=root

hibernate.dialect=org.hibernate.dialect.MySQL8Dialect
hibernate.show_sql=true
好吧,从上面一切都清楚了,连接数据库的参数,即 驱动程序类名、url、用户名和密码。 hibernate.dialect- 需要此属性来向 Hibernate 指示使用的是 SQL 语言的特定版本。事实上,在每个 DBMS 中,为了扩展功能、添加一些功能或优化某些内容,它们通常会对语言进行稍微现代化。结果是,每个 DBMS 都有自己的 SQL 方言。就像英语一样,看起来语言是一样的,但在澳大利亚、美国或英国会略有不同,有些单词可能有不同的含义。为了避免任何理解问题,您需要直接告诉 Hibernate 它到底要处理什么。 hibernate.show_sql— 由于此属性,对数据库的查询将显示在控制台中。这不是必要的,但是有了这个东西你至少可以看看正在发生什么,否则看起来 Hibernate 正在做某种魔法。好吧,当然,它不会完全清晰地显示;最好使用某种记录器来实现这一点,但那是另一次的事情了,现在就可以了。

休眠配置

让我们继续设置配置。让我们在包中创建config一个类HibernateConfig
package testgroup.filmography.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.core.env.Environment;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
import org.springframework.orm.hibernate5.HibernateTransactionManager;
import org.springframework.orm.hibernate5.LocalSessionFactoryBean;
import org.springframework.transaction.annotation.EnableTransactionManagement;

import javax.sql.DataSource;
import java.util.Properties;

@Configuration
@ComponentScan(basePackages = " testgroup.filmography")
@EnableTransactionManagement
@PropertySource(value = "classpath:db.properties")
public class HibernateConfig {
    private Environment environment;

    @Autowired
    public void setEnvironment(Environment environment) {
        this.environment = environment;
    }

    private Properties hibernateProperties() {
        Properties properties = new Properties();
        properties.put("hibernate.dialect", environment.getRequiredProperty("hibernate.dialect"));
        properties.put("hibernate.show_sql", environment.getRequiredProperty("hibernate.show_sql"));
        return properties;
    }

    @Bean
    public DataSource dataSource() {
        DriverManagerDataSource dataSource = new DriverManagerDataSource();
        dataSource.setDriverClassName(environment.getRequiredProperty("jdbc.driverClassName"));
        dataSource.setUrl(environment.getRequiredProperty("jdbc.url"));
        dataSource.setUsername(environment.getRequiredProperty("jdbc.username"));
        dataSource.setPassword(environment.getRequiredProperty("jdbc.password"));
        return dataSource;
    }

    @Bean
    public LocalSessionFactoryBean sessionFactory() {
        LocalSessionFactoryBean sessionFactory = new LocalSessionFactoryBean();
        sessionFactory.setDataSource(dataSource());
        sessionFactory.setPackagesToScan("testgroup.filmography.model");
        sessionFactory.setHibernateProperties(hibernateProperties());
        return sessionFactory;
    }

    @Bean
    public HibernateTransactionManager transactionManager() {
        HibernateTransactionManager transactionManager = new HibernateTransactionManager();
        transactionManager.setSessionFactory(sessionFactory().getObject());
        return transactionManager;
    }
}
这里有很多新东西,所以最好在不同的来源中额外查找每个项目的信息。我们在这里简单回顾一下。
  • 我们上课的时候@Configuration就已经想好了。@ComponentScanWebConfig
  • @EnableTransactionManagement— 允许您使用它TransactionManager来管理交易。Hibernate 使用事务与数据库一起工作;它们需要作为一个整体来执行一组特定的操作,即 如果该方法中的任何一项操作出现问题,那么所有其他操作都不会被执行,这样就不会像经典的转账例子那样,当从一个账户提款的操作完成时,而是发生这种情况。写信给另一个人的操作不起作用,结果钱消失了。
  • @PropertySource— 连接我们最近创建的属性文件。
  • Environment- 为了从property文件中获取属性。
  • hibernateProperties- 需要此方法将 Hibernate 属性表示为 Properties 对象
  • DataSource— 用于创建与数据库的连接。这是DriverManager的替代方案,我们之前在创建main. 文档说DataSource最好使用。当然,这就是我们要做的,不要忘记在互联网上阅读差异和优势是什么。一项特别的好处是能够创建数据库连接池 (DBCP)。
  • sessionFactory— 创建会话,借助该会话执行实体对象的操作。这里我们设置数据源、Hibernate 属性以及我们需要在哪个包中查找实体类。
  • transactionManager— 配置事务管理器。
关于 的一个小注释DataSource。文档说不DriverManagerDataSource建议使用标准实现,即 ,因为 它只是普通连接池的替代品,通常只适用于测试等。对于普通应用程序,最好使用某种 DBCP 库。好吧,对于我们的应用程序来说,当然,我们拥有的已经足够了,但为了完成图片,也许我们仍然会按照建议使用另一个实现。让我们添加pom.xml以下依赖项:
<dependency>
            <groupId>org.apache.tomcat</groupId>
            <artifactId>tomcat-dbcp</artifactId>
            <version>9.0.10</version>
</dependency>
dataSource类方法中HibernateConfig,我们将其替换DriverManagerDataSourceBasicDataSource包中的方法org.apache.tomcat.dbcp.dbcp2
BasicDataSource dataSource = new BasicDataSource();
好吧,一切似乎都准备好了,配置也准备好了,剩下的就是将其添加到我们的AppInitializer中:
protected Class<?>[] getRootConfigClasses() {
        return new Class[]{HibernateConfig.class};
    }

数据访问层

终于到了开始使用我们的 DAO 的时候了。我们去上课FilmDAOImpl,首先我们从那里删除试用列表,我们不再需要它。让我们添加一个会话工厂并完成它。
private SessionFactory sessionFactory;

    @Autowired
    public void setSessionFactory(SessionFactory sessionFactory) {
        this.sessionFactory = sessionFactory;
    }
首先,我们将创建一个方法来显示包含电影列表的页面,在其中我们将接收一个会话并向数据库发出请求(提取所有记录并形成一个列表):
public List<Film> allFilms() {
        Session session = sessionFactory.getCurrentSession();
        return session.createQuery("from Film").list();
    }
这里有2点。首先,显示警告。这是因为我们想要接收一个参数化的List<Film>,但该方法返回只是List因为在编译时不知道请求将返回什么类型。所以这个想法警告我们,我们正在进行不安全的转换,这可能会导致麻烦。有几种更正确的方法可以做到这一点,这样就不会出现这样的问题。您可以在互联网上搜索信息。但现在我们不用担心这个。事实上,我们确切地知道将返回什么类型,因此这里不会出现问题,您可以简单地忽略该警告。但是,为了不碍眼,您可以在方法上方添加注释@SupressWarning("unchecked")。通过这样做,我们有点告诉编译器,谢谢你,伙计,你的关心,但我知道我在做什么,一切都在掌控之中,所以你可以放松,不用担心这个方法。其次,这个想法用红色下划线“ from Film”表示。这只是一个 HQL(Hibernate Query Language)查询,并且这个想法不知道是否一切都正确或有错误。您可以转到创意设置并手动调整所有内容(如果有兴趣,请在互联网上查找)。或者,您可以简单地添加对 Hibernate 框架的支持,为此,右键单击该项目,选择“添加框架支持” ,选中Hibernate复选框,然后单击“确定”。此后,很可能在实体类(Film)中很多东西也会用红色下划线表示,例如注释中@Table(name = "films")会发出警告Cannot parse table 'films'。再说一遍,这里没有任何问题,这不是设计错误,一切都会编译并工作。这个想法之所以被强调,是因为它对我们的基地一无所知。为了解决这个问题,让我们将这个想法与数据库集成起来。查看->工具窗口-> Persistense(将打开一个选项卡) ->鼠标右键,选择分配数据源->在数据源中,指定与数据库的连接,然后单击确定Maven、Spring、MySQL、Hibernate 和第一个 CRUD 应用程序简介(第 3 部分) - 3当这一切都解决之后,还剩下一些东西。让我们走向更高的层次,走向服务。在类中,FilmServiceImpl我们allFilms用注释标记 spring 方法@Transactional,这将表明该方法应该在事务中执行(没有这个,Hibernate 将拒绝工作):
@Transactional
public List<Film> allFilms() {
    return filmDAO.allFilms();
}
所以,这里一切都准备好了,你不需要碰控制器中的任何东西。好吧,看来关键时刻已经到来,单击“运行”,看看会发生什么。 Maven、Spring、MySQL、Hibernate 和第一个 CRUD 应用程序简介(第 3 部分)- 4这就是我们的标志,这次它不是从我们自己在课堂上列出的列表中获得的,而是从数据库中获得的。太好了,一切似乎都正常。现在我们使用会话方法以相同的方式执行所有其他 CRUD 操作。最终的类如下所示:
package testgroup.filmography.dao;

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import testgroup.filmography.model.Film;

import java.util.List;

@Repository
public class FilmDAOImpl implements FilmDAO {
    private SessionFactory sessionFactory;

    @Autowired
    public void setSessionFactory(SessionFactory sessionFactory) {
        this.sessionFactory = sessionFactory;
    }

    @Override
    @SuppressWarnings("unchecked")
    public List<Film> allFilms() {
        Session session = sessionFactory.getCurrentSession();
        return session.createQuery("from Film").list();
    }

    @Override
    public void add(Film film) {
        Session session = sessionFactory.getCurrentSession();
        session.persist(film);
    }

    @Override
    public void delete(Film film) {
        Session session = sessionFactory.getCurrentSession();
        session.delete(film);
    }

    @Override
    public void edit(Film film) {
        Session session = sessionFactory.getCurrentSession();
        session.update(film);
    }

    @Override
    public Film getById(int id) {
        Session session = sessionFactory.getCurrentSession();
        return session.get(Film.class, id);
    }
}
现在剩下的就是记住转到服务并向方法添加注释@Transactional。就这样,准备好了。您现在可以运行并检查。单击链接和按钮,尝试添加/删除/编辑条目。如果一切都正确完成,它应该可以工作。现在,这是一个使用 Hibernate、Spring、MySQL 的成熟 CRUD 应用程序。待续... Maven、Spring、MySQL、Hibernate 简介和第一个 CRUD 应用程序(第 1 部分) Maven、Spring、MySQL、Hibernate 简介和第一个 CRUD 应用程序(第 2 部分) Maven、Spring、MySQL、Hibernate 简介和第一个 CRUD 应用程序(第 3 部分) Maven、Spring、MySQL、Hibernate 简介和第一个 CRUD 应用程序(第 4 部分)
评论
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION