Spring数据访问和数据访问层与业务或服务层之间的交互

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spring数据访问和数据访问层与业务或服务层之间的交互相关的知识,希望对你有一定的参考价值。

Spring数据访问和数据访问层与业务或服务层之间的交互(二)_sql

2. DAO 支持

Spring 中的数据访问对象 (DAO) 支持旨在使其易于使用 以一致的方式访问数据访问技术(如 JDBC、Hibernate 或 JPA)。这 让您相当轻松地在上述持久性技术之间切换, 它还允许您编码,而不必担心捕获异常 特定于每种技术。

2.1. 一致的异常层次结构

Spring 提供了从特定于技术的异常的便捷转换,例如到它自己的异常类层次结构,它有 根异常。这些异常包装原始异常,因此永远不会 您可能会丢失有关可能出错的任何信息的任何风险。​​SQLException​​​​DataAccessException​

除了 JDBC 异常之外,Spring 还可以包装特定于 JPA 和 Hibernate 的异常, 将它们转换为一组集中的运行时异常。这使您可以处理大多数 仅在适当的层中出现不可恢复的持久性异常,而没有 DAO 中烦人的样板捕获和抛出块和异常声明。 (不过,您仍然可以在需要的任何位置捕获和处理异常。如上所述, JDBC 异常(包括特定于数据库的方言)也会转换为相同的 层次结构,这意味着您可以在一致的 JDBC 中执行一些操作 编程模型。

前面的讨论适用于 Spring 支持中的各种模板类 适用于各种 ORM 框架。如果使用基于侦听器的类,则应用程序必须 关心处理和本身,最好由 分别委托给理论方法。这些方法转换异常 与异常层次结构中的异常兼容的异常。未经检查,它们也可能被扔掉 (不过,在例外方面牺牲了通用的DAO抽象)。​​HibernateExceptions​​​​PersistenceExceptions​​​​convertHibernateAccessException(..)​​​​convertJpaAccessException(..)​​​​SessionFactoryUtils​​​​org.springframework.dao​​​​PersistenceExceptions​

下图显示了 Spring 提供的异常层次结构。 (请注意,图中详述的类层次结构仅显示整个层次结构的子集。​​DataAccessException​

Spring数据访问和数据访问层与业务或服务层之间的交互(二)_sql_02

2.2. 用于配置 DAO 或存储库类的注释

保证数据访问对象 (DAO) 或存储库提供的最佳方法 例外翻译是使用注释。此注释还 让组件扫描支持查找和配置您的 DAO 和存储库 而不必为它们提供 XML 配置条目。以下示例显示 如何使用注释:​​@Repository​​​​@Repository​

@Repository 
public class SomeMovieFinder implements MovieFinder
// ...

注释。​​@Repository​

任何 DAO 或存储库实现都需要访问持久性资源, 取决于所使用的持久性技术。例如,基于 JDBC 的存储库 需要访问 JDBC,基于 JPA 的存储库需要访问 。实现此目的的最简单方法是具有此资源依赖关系 通过使用其中一个,,oran注释注入。以下示例适用于 JPA 存储库:​​DataSource​​​​EntityManager​​​​@Autowired​​​​@Inject​​​​@Resource​​​​@PersistenceContext​

@Repository
public class JpaMovieFinder implements MovieFinder

@PersistenceContext
private EntityManager entityManager;

// ...

如果您使用经典的 Hibernate API,则可以注入,如下所示 示例显示:​​SessionFactory​

@Repository
public class HibernateMovieFinder implements MovieFinder

private SessionFactory sessionFactory;

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


// ...

我们在这里展示的最后一个示例是典型的 JDBC 支持。您可以注入到初始化方法或构造函数中,您将在其中使用 这。以下示例自动连线 a:​​DataSource​​​​JdbcTemplate​​​​SimpleJdbcCall​​​​DataSource​​​​DataSource​

@Repository
public class JdbcMovieFinder implements MovieFinder

private JdbcTemplate jdbcTemplate;

@Autowired
public void init(DataSource dataSource)
this.jdbcTemplate = new JdbcTemplate(dataSource);


// ...

3. 使用 JDBC 访问数据

Spring Framework JDBC 抽象提供的价值也许最好地表现为 下表中概述的操作顺序。该表显示了哪些操作 Spring 照顾和哪些行动是你的责任。

表 4.春季JDBC - 谁做什么?

行动

春天


定义连接参数。

X

打开连接。

X

指定 SQL 语句。

X

声明参数并提供参数值

X

准备并运行语句。

X

设置循环以循环访问结果(如果有)。

X

完成每次迭代的工作。

X

处理任何异常。

X

处理事务。

X

关闭连接、语句和结果集。

X

Spring 框架负责所有可以使 JDBC 成为 繁琐的 API。

3.1. 选择 JDBC 数据库访问方法

您可以在多种方法中进行选择,以构成 JDBC 数据库访问的基础。 除了三种风格之外,newand方法还优化了数据库元数据,而RDBMS对象样式采用了 更面向对象的方法类似于JDO查询设计。开始使用后 其中一种方法,您仍然可以混合和匹配以包含来自 不同的方法。所有方法都需要符合 JDBC 2.0 的驱动程序,并且某些 高级功能需要 JDBC 3.0 驱动程序。​​JdbcTemplate​​​​SimpleJdbcInsert​​​​SimpleJdbcCall​

  • ​JdbcTemplate​​是经典和最流行的春季JDBC方法。这 “最低级别”方法和所有其他方法都在幕后使用 Jdbc 模板。
  • ​NamedParameterJdbcTemplate​​包装 ATO 提供命名参数 而不是传统的 JDBC 占位符。这种方法提供更好的 文档和 SQL 语句有多个参数时的易用性。JdbcTemplate?
  • ​SimpleJdbcInsert​​并优化数据库元数据以限制数量 必要的配置。此方法简化了编码,因此您需要 仅提供表或过程的名称,并提供参数匹配的映射 列名称。仅当数据库提供足够的元数据时,此操作才有效。如果 数据库不提供此元数据,您必须提供显式 参数的配置。SimpleJdbcCall
  • RDBMS 对象 — 包括、和— 要求您在初始化期间创建可重用和线程安全的对象 数据访问层。此方法以 JDO 查询为模型,其中您可以定义您的 查询字符串,声明参数,然后编译查询。一旦你这样做了,,,方法就可以被称为多个 具有各种参数值的时间。MappingSqlQuerySqlUpdateStoredProcedureexecute(…​)update(…​)findObject(…​)

3.2. 软件包层次结构

Spring 框架的 JDBC 抽象框架由四个不同的包组成:

  • ​core​​:包包含类及其 各种回调接口,以及各种相关类。名为 theandclasses 的子包包含 theandclass。另一个名为 的子包包含类和相关的支持类。请参阅使用 JDBC 核心类控制基本的 JDBC 处理和错误处理、JDBC 批处理操作和使用SimpleJdbc类简化 JDBC 操作。org.springframework.jdbc.coreJdbcTemplateorg.springframework.jdbc.core.simpleSimpleJdbcInsertSimpleJdbcCallorg.springframework.jdbc.core.namedparamNamedParameterJdbcTemplate
  • ​datasource​​:该软件包包含一个易于访问的实用程序类和各种可用于的简单实现 在 Jakarta EE 容器之外测试和运行未修改的 JDBC 代码。一个子包 named提供对创建的支持 使用 Java 数据库引擎(如 HSQL、H2 和 Derby)嵌入数据库。请参阅控制数据库连接和嵌入式数据库支持。org.springframework.jdbc.datasourceDataSourceDataSourceorg.springfamework.jdbc.datasource.embedded
  • ​object​​:包包含表示 RDBMS 的类 查询、更新和存储过程作为线程安全、可重用的对象。请参阅将 JDBC 操作建模为 Java 对象。此方法由 JDO 建模,尽管对象由查询返回 自然与数据库断开连接。这种更高级别的 JDBC 抽象 取决于包中的较低级别抽象。org.springframework.jdbc.objectorg.springframework.jdbc.core
  • ​support​​:包提供翻译 功能和一些实用程序类。JDBC 处理期间引发的异常是 转换为包中定义的异常。这意味着 使用 Spring JDBC 抽象层的代码不需要实现 JDBC 或 特定于 RDBMS 的错误处理。所有翻译的异常均未选中,这为您提供了 捕获异常的选项,您可以从中恢复,同时让其他 异常将传播到调用方。请参见使用SQLExceptionTranslator。org.springframework.jdbc.supportSQLExceptionorg.springframework.dao

3.3. 使用 JDBC 核心类来控制基本的 JDBC 处理和错误处理

本节介绍如何使用 JDBC 核心类来控制基本的 JDBC 处理。 包括错误处理。它包括以下主题:

  • 使用Jdbc 模板
  • 使用NamedParameterJdbcTemplate
  • 使用SQLExceptionTranslator
  • 运行语句
  • 运行查询
  • 更新数据库
  • 检索自动生成的密钥

3.3.1. 使用​​JdbcTemplate​

​JdbcTemplate​​是 JDBC 核心包中的中心类。它处理 创建和释放资源,这有助于避免常见错误,例如 忘记关闭连接。它执行核心 JDBC 的基本任务 工作流(例如语句创建和执行),保留应用程序代码以提供 SQL 并提取结果。班级:​​JdbcTemplate​

  • 运行 SQL 查询
  • 更新语句和存储过程调用
  • 执行迭代覆盖实例和返回参数值的提取。ResultSet
  • 捕获 JDBC 异常并将其转换为通用的、信息量更大的异常 包中定义的层次结构。(请参阅一致的异常层次结构。org.springframework.dao

当你使用for你的代码时,你只需要实现回调 接口,为它们提供明确定义的协定。给定由类提供,回调接口创建一个准备好的 语句,提供 SQL 和任何必要的参数。接口也是如此,它创建可调用的语句。接口从 a 的每一行中提取值。​​JdbcTemplate​​​​Connection​​​​JdbcTemplate​​​​PreparedStatementCreator​​​​CallableStatementCreator​​​​RowCallbackHandler​​​​ResultSet​

您可以通过直接实例化在 DAO 实现中使用 或者您可以在Spring IoC容器中配置它并将其提供给 DAO 作为豆子参考。​​JdbcTemplate​​​​DataSource​

此类发出的所有 SQL 都记录在类别下的级别 对应于模板实例的完全限定类名(通常,但如果使用 TheClass 的自定义子类,则可能会有所不同)。​​DEBUG​​​​JdbcTemplate​​​​JdbcTemplate​

以下各节提供了一些用法示例。这些例子 不是公开的所有功能的详尽列表。 请参阅随之而来的javadoc了解这一点。​​JdbcTemplate​​​​JdbcTemplate​

查询 (​​SELECT​​)

以下查询获取关系中的行数:

int rowCount = this.jdbcTemplate.queryForObject("select count(*) from t_actor", Integer.class);

以下查询使用绑定变量

int countOfActorsNamedJoe = this.jdbcTemplate.queryForObject(
"select count(*) from t_actor where first_name = ?", Integer.class, "Joe");

以下查询查找:​​String​

String lastName = this.jdbcTemplate.queryForObject(
"select last_name from t_actor where id = ?",
String.class, 1212L);

以下查询查找并填充单个域对象:

Actor actor = jdbcTemplate.queryForObject(
"select first_name, last_name from t_actor where id = ?",
(resultSet, rowNum) ->
Actor newActor = new Actor();
newActor.setFirstName(resultSet.getString("first_name"));
newActor.setLastName(resultSet.getString("last_name"));
return newActor;
,
1212L);

以下查询查找并填充域对象列表:

List<Actor> actors = this.jdbcTemplate.query(
"select first_name, last_name from t_actor",
(resultSet, rowNum) ->
Actor actor = new Actor();
actor.setFirstName(resultSet.getString("first_name"));
actor.setLastName(resultSet.getString("last_name"));
return actor;
);

如果最后两个代码片段实际上存在于同一个应用程序中,它将使 删除 twolambda 表达式中存在的重复项,以及 将它们提取到单个字段中,然后可以根据需要由 DAO 方法引用。 例如,最好按如下方式编写前面的代码片段:​​RowMapper​

private final RowMapper<Actor> actorRowMapper = (resultSet, rowNum) -> 
Actor actor = new Actor();
actor.setFirstName(resultSet.getString("first_name"));
actor.setLastName(resultSet.getString("last_name"));
return actor;
;

public List<Actor> findAllActors()
return this.jdbcTemplate.query("select first_name, last_name from t_actor", actorRowMapper);
更新(,和)与​​INSERT​​​​UPDATE​​​​DELETE​​​​JdbcTemplate​

可以使用该方法执行插入、更新和删除操作。 参数值通常作为变量参数提供,或者作为对象数组提供。​​update(..)​

下面的示例插入一个新条目:

this.jdbcTemplate.update(
"insert into t_actor (first_name, last_name) values (?, ?)",
"Leonor", "Watling");

以下示例更新现有条目:

this.jdbcTemplate.update(
"update t_actor set last_name = ? where id = ?",
"Banjo", 5276L);

下面的示例删除一个条目:

this.jdbcTemplate.update(
"delete from t_actor where id = ?",
Long.valueOf(actorId));
其他操作​​JdbcTemplate​

您可以使用该方法运行任意 SQL。因此, 方法通常用于 DDL 语句。它被大量超载的变体 回调接口、绑定变量数组等。以下示例创建一个 桌子:​​execute(..)​

this.jdbcTemplate.execute("create table mytable (id integer, name varchar(100))");

下面的示例调用存储过程:

this.jdbcTemplate.update(
"call SUPPORT.REFRESH_ACTORS_SUMMARY(?)",
Long.valueOf(unionId));

稍后将介绍更复杂的存储过程支持。

​JdbcTemplate​​最佳实践

一旦配置,类的实例是线程安全的。这是 很重要,因为这意味着您可以配置 aand 的单个实例,然后将此共享引用安全地注入多个 DAO(或存储库)。 Theis 有状态的,因为它保持对 a 的引用,但是 此状态不是会话状态。​​JdbcTemplate​​​​JdbcTemplate​​​​JdbcTemplate​​​​DataSource​

使用类(以及关联的NamedParameterJdbcTemplate类)时的常见做法是 在 Spring 配置文件中配置,然后进行依赖注入 将豆子共享到您的 DAO 类中。泰斯创建于 二传手。这会导致类似于以下内容的 DAO:​​JdbcTemplate​​​​DataSource​​​​DataSource​​​​JdbcTemplate​​​​DataSource​

public class JdbcCorporateEventDao implements CorporateEventDao 

private JdbcTemplate jdbcTemplate;

public void setDataSource(DataSource dataSource)
this.jdbcTemplate = new JdbcTemplate(dataSource);


// JDBC-backed implementations of the methods on the CorporateEventDao follow...

用注释类。​​@Repository​

注释方法。​​DataSource​​​​@Autowired​

创建一个新的。​​JdbcTemplate​​​​DataSource​

以下示例显示了相应的 XML 配置:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">

<bean id="corporateEventDao" class="com.example.JdbcCorporateEventDao">
<property name="dataSource" ref="dataSource"/>
</bean>

<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="$jdbc.driverClassName"/>
<property name="url" value="$jdbc.url"/>
<property name="username" value="$jdbc.username"/>
<property name="password" value="$jdbc.password"/>
</bean>

<context:property-placeholder location="jdbc.properties"/>

</beans>

显式配置的替代方法是使用组件扫描和注释 支持依赖注入。在这种情况下,您可以使用(这使其成为组件扫描的候选者)对类进行注释并注释这些内容。 方法。以下示例演示如何执行此操作:​​@Repository​​​​DataSource​​​​@Autowired​

@Repository 
public class JdbcCorporateEventDao implements CorporateEventDao

private JdbcTemplate jdbcTemplate;

@Autowired
public void setDataSource(DataSource dataSource)
this.jdbcTemplate = new JdbcTemplate(dataSource);


// JDBC-backed implementations of the methods on the CorporateEventDao follow...

用注释类。​​@Repository​

构造函数注入的。​​DataSource​

创建一个新的。​​JdbcTemplate​​​​DataSource​

以下示例显示了相应的 XML 配置:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">

<!-- Scans within the base package of the application for @Component classes to configure as beans -->
<context:component-scan base-package="org.springframework.docs.test" />

<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="$jdbc.driverClassName"/>
<property name="url" value="$jdbc.url"/>
<property name="username" value="$jdbc.username"/>
<property name="password" value="$jdbc.password"/>
</bean>

<context:property-placeholder location="jdbc.properties"/>

</beans>

如果你使用 Spring 的类和你的各种 JDBC 支持的 DAO 类 从它扩展,你的子类从类继承amethod。您可以选择是否从此类继承。该课程仅为方便起见而提供。​​JdbcDaoSupport​​​​setDataSource(..)​​​​JdbcDaoSupport​​​​JdbcDaoSupport​

无论您选择使用上述哪种模板初始化样式(或 not),很少需要为每个类创建一个新的实例 您想要运行 SQL 的时间。配置后,实例是线程安全的。 如果应用程序访问多个 数据库,您可能需要多个实例,这需要多个实例,随后需要多个不同的实例 已配置实例。​​JdbcTemplate​​​​JdbcTemplate​​​​JdbcTemplate​​​​DataSources​​​​JdbcTemplate​

3.3.2. 使用​​NamedParameterJdbcTemplate​

该类增加了对 JDBC 语句编程的支持 通过使用命名参数,而不是仅使用经典参数对 JDBC 语句进行编程 占位符 () 参数。该类包装和委托给包装以完成其大部分工作。这 部分仅介绍类中那些不同的区域 从自身 — 即通过使用命名对 JDBC 语句进行编程 参数。以下示例演示如何使用:​​NamedParameterJdbcTemplate​​​​?​​​​NamedParameterJdbcTemplate​​​​JdbcTemplate​​​​JdbcTemplate​​​​NamedParameterJdbcTemplate​​​​JdbcTemplate​​​​NamedParameterJdbcTemplate​

// some JDBC-backed DAO class...
private NamedParameterJdbcTemplate namedParameterJdbcTemplate;

public void setDataSource(DataSource dataSource)
this.namedParameterJdbcTemplate = new NamedParameterJdbcTemplate(dataSource);


public int countOfActorsByFirstName(String firstName)

String sql = "select count(*) from T_ACTOR where first_name = :first_name";

SqlParameterSource namedParameters = new MapSqlParameterSource("first_name", firstName);

return this.namedParameterJdbcTemplate.queryForObject(sql, namedParameters, Integer.class);

请注意,在分配给变量的值和插入到变量(类型)中的相应值中使用了命名参数表示法。​​sql​​​​namedParameters​​​​MapSqlParameterSource​

或者,您可以使用基于样式将命名参数及其相应的值传递给实例。其余的 由 TheAnd Class实现的方法遵循类似的模式,此处不涉及。​​NamedParameterJdbcTemplate​​​​Map​​​​NamedParameterJdbcOperations​​​​NamedParameterJdbcTemplate​

以下示例显示了基于样式的用法:​​Map​

// some JDBC-backed DAO class...
private NamedParameterJdbcTemplate namedParameterJdbcTemplate;

public void setDataSource(DataSource dataSource)
this.namedParameterJdbcTemplate = new NamedParameterJdbcTemplate(dataSource);


public int countOfActorsByFirstName(String firstName)

String sql = "select count(*) from T_ACTOR where first_name = :first_name";

Map<String, String> namedParameters = Collections.singletonMap("first_name", firstName);

return this.namedParameterJdbcTemplate.queryForObject(sql, namedParameters, Integer.class);

一个与(并且存在于相同的 Java包)是接口。你已经看过一个例子 此接口在前面的代码段之一 (Theclass) 中的实现。Anis 命名参数的来源 值为 a。该类是一个 简单的实现,即围绕 a 的适配器,其中键 是参数名称,值是参数值。​​NamedParameterJdbcTemplate​​​​SqlParameterSource​​​​MapSqlParameterSource​​​​SqlParameterSource​​​​NamedParameterJdbcTemplate​​​​MapSqlParameterSource​​​​java.util.Map​

另一个实现是类。此类包装一个任意 JavaBean(即,一个类的实例 坚持 JavaBean 约定),并使用包装的 JavaBean 的属性作为源代码 的命名参数值。​​SqlParameterSource​​​​BeanPropertySqlParameterSource​

以下示例显示了一个典型的 JavaBean:

public class Actor 

private Long id;
private String firstName;
private String lastName;

public String getFirstName()
return this.firstName;


public String getLastName()
return this.lastName;


public Long getId()
return this.id;


// setters omitted...

以下示例使用 ato 返回 前面示例中所示的类的成员:​​NamedParameterJdbcTemplate​

// some JDBC-backed DAO class...
private NamedParameterJdbcTemplate namedParameterJdbcTemplate;

public void setDataSource(DataSource dataSource)
this.namedParameterJdbcTemplate = new NamedParameterJdbcTemplate(dataSource);


public int countOfActors(Actor exampleActor)

// notice how the named parameters match the properties of the above Actor class
String sql = "select count(*) from T_ACTOR where first_name = :firstName and last_name = :lastName";

SqlParameterSource namedParameters = new BeanPropertySqlParameterSource(exampleActor);

return this.namedParameterJdbcTemplate.queryForObject(sql, namedParameters, Integer.class);

请记住,该类包装了一个经典模板。如果您需要访问包装实例才能访问 仅在类中存在的功能,您可以使用该方法访问包装的接口。​​NamedParameterJdbcTemplate​​​​JdbcTemplate​​​​JdbcTemplate​​​​JdbcTemplate​​​​getJdbcOperations()​​​​JdbcTemplate​​​​JdbcOperations​

另请参阅JdbcTemplate最佳实践,了解有关在应用程序上下文中使用类的指南。​​NamedParameterJdbcTemplate​

3.3.3. 使用​​SQLExceptionTranslator​

​SQLExceptionTranslator​​是由可以翻译的类实现的接口 在斯普林斯之间, 这在数据访问策略方面是不可知的。实现可以是通用的(对于 例如,对 JDBC 使用 SQLState 代码)或专有代码(例如,使用 Oracle 错误) 代码),以提高精度。​​SQLException​​​​org.springframework.dao.DataAccessException​

​SQLErrorCodeSQLExceptionTranslator​​是默认使用的实现。此实现使用特定的供应商代码。它更多 比实施精确。错误代码翻译基于 代码保存在调用的 JavaBean 类型类中。此类已创建并 由 an 填充,它(顾名思义)是 创建基于命名的配置文件的内容。此文件填充有供应商代码,并基于取自。实际代码 使用您正在使用的数据库。​​SQLExceptionTranslator​​​​SQLState​​​​SQLErrorCodes​​​​SQLErrorCodesFactory​​​​SQLErrorCodes​​​​sql-error-codes.xml​​​​DatabaseProductName​​​​DatabaseMetaData​

按以下顺序应用匹配规则:​​SQLErrorCodeSQLExceptionTranslator​

  1. 由子类实现的任何自定义翻译。通常,使用所提供的混凝土,因此此规则不适用。它 仅当您实际提供了子类实现时才适用。SQLErrorCodeSQLExceptionTranslator
  2. 提供的接口的任何自定义实现 作为类的属性。SQLExceptionTranslatorcustomSqlExceptionTranslatorSQLErrorCodes
  3. 搜索类的实例列表(为类的属性提供)以查找匹配项。CustomSQLErrorCodesTranslationcustomTranslationsSpring数据访问和数据访问层与业务或服务层之间的交互

    将数据访问层与服务层分开是否很好[关闭]

    JDBC的架构设计

    微服务定义

    Dao层Dao层实现类和Service层Service实现类的关系

    用于业务逻辑或数据访问层的 Web 服务