提交 8070244b 编写于 作者: W wizardforcel

2021-10-01 23:45:26

上级 42556a37
......@@ -332,7 +332,7 @@ Consumer<Double> failure = new Consumer<Double>() {
```
我们现在可以调用 calculate 方法,如下所示:
我们现在可以调用`calculate`方法,如下所示:
```java
calculate(source, process, condition, success, failure);
......@@ -764,7 +764,7 @@ public String m1(double x, double y){
# 还有更多。。。
与在匿名类中一样,在 Lambda 表达式外部创建但在内部使用的变量实际上是 final,不能修改。您可以编写以下代码:
与在匿名类中一样,在 Lambda 表达式外部创建但在内部使用的变量实际上是`final`,不能修改。您可以编写以下代码:
```java
double v = 10d;
......@@ -826,7 +826,7 @@ class Demo{
![](img/324ceaa7-1714-4153-bd7a-43ae67258283.png)
Lambda 表达式不是内部类,不能被`this`引用。Lambda 表达式没有字段或属性。它是无国籍的。这就是为什么在 Lambda 表达式中,`this`关键字引用周围的上下文。这也是要求 Lambda 表达式使用的周围上下文的所有变量必须是 final 或有效 final 的另一个原因。
Lambda 表达式不是内部类,不能被`this`引用。Lambda 表达式没有字段或属性。它是无国籍的。这就是为什么在 Lambda 表达式中,`this`关键字引用周围的上下文。这也是要求 Lambda 表达式使用的周围上下文的所有变量必须是`final`或有效`final`的另一个原因。
# 使用 Lambda 表达式
......@@ -1343,7 +1343,7 @@ System.out.println("new Food(Carrot, Broccoli)
.sayFavorite() => " + func.apply(food3));
```
如您所见,参数(CallReceiver 对象)与任何参数一样,仅来自当前上下文。无论函数传递到哪里,它都不携带上下文。它的接收者未绑定到用于创建函数的上下文。这就是为什么这个方法引用被称为*未绑定*
如您所见,参数(`CallReceiver`对象)与任何参数一样,仅来自当前上下文。无论函数传递到哪里,它都不携带上下文。它的接收者未绑定到用于创建函数的上下文。这就是为什么这个方法引用被称为*未绑定*
上述代码的输出如下所示:
......
# 流和管道
在 Java8 和 Java9 中,CollectionsAPI 通过利用 Lambda 表达式引入流和内部迭代进行了重大的改头换面。在 Java10(JDK18.3)中,添加了新方法-`List.copyOf``Set.copyOf``Map.copyOf`,允许我们从现有实例创建新的不可变集合。此外,新方法`toUnmodifiableList``toUnmodifiableSet``toUnmodifiableMap`被添加到`java.util.stream`包中的`Collectors`类中,允许将`Stream`中的元素收集到一个不可变的集合中。本章介绍如何使用流和链多个操作来创建管道。此外,读者还将了解如何并行完成这些操作。配方列表包括以下内容:
在 Java8 和 Java9 中,集合 API 通过利用 Lambda 表达式引入流和内部迭代进行了重大的改头换面。在 Java10(JDK18.3)中,添加了新方法-`List.copyOf``Set.copyOf``Map.copyOf`,允许我们从现有实例创建新的不可变集合。此外,新方法`toUnmodifiableList``toUnmodifiableSet``toUnmodifiableMap`被添加到`java.util.stream`包中的`Collectors`类中,允许将`Stream`中的元素收集到一个不可变的集合中。本章介绍如何使用流和链多个操作来创建管道。此外,读者还将了解如何并行完成这些操作。配方列表包括以下内容:
* 使用`of()``copyOf()`工厂方法创建不可变集合
* 创建和操作流
......@@ -124,9 +124,9 @@ static <E> List<E> of(E... elements)
[JEP 269](http://openjdk.java.net/jeps/269) 所述,针对性能优化了具有固定数量元素的 10 种重载工厂方法,这些方法
*避免**varargs 调用所产生的阵列分配、初始化和垃圾收集开销。*
*避免可变参数调用所产生的阵列分配、初始化和垃圾收集开销。*
*使用`of()`工厂方法使代码更加紧凑:
使用`of()`工厂方法使代码更加紧凑:
```java
List.of("This ", "is ", "created ", "by ", "List.of()")
......@@ -250,7 +250,7 @@ setA = new HashSet<>(listB);
# 还有更多。。。
在 Lambda 表达式和流被引入后不久,非空值和不变性被强制执行,这不是偶然的。正如您将在后续配方中看到的,函数式编程和流管道鼓励流畅的编码风格(使用方法链接,以及在本配方示例中使用`forEach()`方法)。Fluent 样式提供了更紧凑、可读性更强的代码。不需要检查`null`值有助于保持紧凑,并专注于主要处理过程。
在 Lambda 表达式和流被引入后不久,非空值和不变性被强制执行,这不是偶然的。正如您将在后续配方中看到的,函数式编程和流管道鼓励流畅的编码风格(使用方法链接,以及在本配方示例中使用`forEach()`方法)。流畅样式提供了更紧凑、可读性更强的代码。不需要检查`null`值有助于保持紧凑,并专注于主要处理过程。
不变性特性反过来与 Lambda 表达式使用的变量的`finale`概念非常一致。例如,可变集合允许我们绕过此限制:
......@@ -283,7 +283,7 @@ list.forEach(System.out::print); //prints: 12545
* `java.nio.file.Files`类的`Stream<Path> list()``Stream<String> lines()``Stream<Path> find()`方法
* `java.io.BufferedReader`类的`Stream<String> lines()`方法
创建流后,可以对其元素应用各种方法(称为操作)。流本身不存储数据。相反,它根据需要从源获取数据(并向操作提供或发送数据)。这些操作可以使用 fluent 样式形成管道,因为许多中间操作也可以返回流。这种操作称为*中间*操作。中间操作的示例包括:
创建流后,可以对其元素应用各种方法(称为操作)。流本身不存储数据。相反,它根据需要从源获取数据(并向操作提供或发送数据)。这些操作可以使用流畅样式形成管道,因为许多中间操作也可以返回流。这种操作称为*中间*操作。中间操作的示例包括:
* `map()`:根据函数变换元素
* `flatMap()`:根据函数将每个元素转换成流
......@@ -325,7 +325,7 @@ Map.of(1, "This ", 2, "is ", 3, "built ", 4, "by ", 5,
.entrySet().stream().forEach(System.out::print);
```
我们使用 fluent 样式使代码更加紧凑和插入`System.out.println()`,以便在输出中开始一行新代码。
我们使用流畅样式使代码更加紧凑和插入`System.out.println()`,以便在输出中开始一行新代码。
2. 运行前面的示例,您将看到以下结果:
......@@ -424,13 +424,13 @@ try(Stream<Path> stream = Files.list(dir)) {
“必须在`try-with-resources`语句或类似的控制结构中使用此方法,以确保在流的操作完成后立即关闭流的打开目录。”
这就是我们所做的;我们使用了 try with resources 语句。或者,我们可以使用 try-catch-finally 构造,关闭 finally 块中的流,结果不会改变。
这就是我们所做的;我们使用了资源试用语句。或者,我们可以使用`try-catch-finally`构造,关闭`finally`块中的流,结果不会改变。
12. 运行前面的示例并观察输出:
![](img/9faf4c8d-cc86-43ce-9bcc-d116b5953269.png)
并非所有流都必须显式关闭,尽管`Stream`接口扩展了`AutoCloseable`,并且人们希望所有流都必须使用 try with resources 语句自动关闭。但事实并非如此。[`Stream`接口的 Javadoc](https://docs.oracle.com/javase/8/docs/api/java/util/stream/Stream.html) 表示,
并非所有流都必须显式关闭,尽管`Stream`接口扩展了`AutoCloseable`,并且人们希望所有流都必须使用资源试用语句自动关闭。但事实并非如此。[`Stream`接口的 Javadoc](https://docs.oracle.com/javase/8/docs/api/java/util/stream/Stream.html) 表示,
“流有一个`BaseStream.close()`方法并实现`AutoCloseable`。大多数流实例在使用后实际上不需要关闭,因为它们由集合、数组或生成函数支持,不需要特殊的资源管理。通常,只有源为 I/O 通道的流,例如`Files.lines(Path)`返回的流。”,将需要关闭。”
......@@ -713,7 +713,7 @@ Stream.of("3","2","1").parallel().forEachOrdered(System.out::print);
![](img/062c4c7d-21e1-4ad9-8e69-c21ec4476bb5.png)
现在,让我们谈谈下一组终端操作,称为`reduce()`。在处理所有流元素后,三个重载方法中的每一个都返回一个值。其中最简单的例子是查找流元素的总和,如果它们是数字,或者是 max、min 和类似的值。但对于任何类型的对象流,都可以构造更复杂的结果。
现在,让我们谈谈下一组终端操作,称为`reduce()`。在处理所有流元素后,三个重载方法中的每一个都返回一个值。其中最简单的例子是查找流元素的总和,如果它们是数字,或者是最大、最小和类似的值。但对于任何类型的对象流,都可以构造更复杂的结果。
第一个方法`Optional<T> reduce(BinaryOperator<T> accumulator)`返回`Optional<T>`对象,因为计算结果是提供的累加器函数的责任,JDK 实现的作者不能保证它始终包含非空值:
......@@ -1116,8 +1116,8 @@ LongStream.range(1, 3).asDoubleStream()
`IntSummaryStatistics`类有以下两种方法:
* void accept(int 值):将新值包含到统计摘要中
* void combine(`IntSummaryStatistics`other):将提供的`other`对象收集到的统计信息添加到当前统计信息中
* `void accept(int value)`:将新值包含到统计摘要中
* `void combine(IntSummaryStatistics other)`:将提供的`other`对象收集到的统计信息添加到当前统计信息中
这些方法允许我们在任何`Stream`对象上使用`R collect(Supplier<R> supplier, BiConsumer<R,? super T> accumulator, BiConsumer<R,R> combiner)`操作的重载版本,如下所示:
......@@ -2071,7 +2071,7 @@ System.out.println(map);
```
如您所见,`Collectors.groupingBy(Person::getName)`采集器生成的 map `List<Person>`值后来(下游)被`Collectors.toSet()`采集器更改为一个集合。
如您所见,`Collectors.groupingBy(Person::getName)`采集器生成的映射`List<Person>`值后来(下游)被`Collectors.toSet()`采集器更改为一个集合。
或者,每个`List<Person>`值可以仅转换为列表元素的计数,如下所示:
......@@ -2082,7 +2082,7 @@ Map<String, Long> map = getStreamPerson()
System.out.println(map); //prints: {John=2, Jill=1}
```
要计算流中有多少相同的`Person`对象(根据`equals()`方法相等的对象),我们可以使用 identity 函数,该函数定义为返回未更改的输入。例如:
要计算流中有多少相同的`Person`对象(根据`equals()`方法相等的对象),我们可以使用恒等函数,该函数定义为返回未更改的输入。例如:
```java
Stream.of("a","b","c")
......
......@@ -201,7 +201,7 @@ CREATE TABLE traffic_unit (
);
```
`size`参数是可选的。如果未设置,如前面的示例代码中所示,则表示该列可以存储任意长度的值。在本例中,`integer`类型允许您存储从`Integer.MIN_VALUE`(即-2147483648)到`Integer.MAX_VALUE`(即+2147483647)的数字。添加了`NOT NULL`类型,因为默认情况下,该列可以为 null,并且我们希望确保填充所有列。
`size`参数是可选的。如果未设置,如前面的示例代码中所示,则表示该列可以存储任意长度的值。在本例中,`integer`类型允许您存储从`Integer.MIN_VALUE`(即`-2147483648`)到`Integer.MAX_VALUE`(即`+2147483647`)的数字。添加了`NOT NULL`类型,因为默认情况下,该列可以为`null`,并且我们希望确保填充所有列。
我们还将`id`列标识为`PRIMARY KEY`,这表示该列(或列的组合)唯一地标识了该记录。例如,如果有一个表包含所有国家的所有人的信息,那么唯一的组合可能是他们的全名、地址和出生日期。好吧,我们可以想象,在一些家庭中,双胞胎出生并有相同的名字,所以我们说*可能*。如果出现这种情况的可能性很高,我们需要在主键组合中添加另一列,这是一个出生顺序,默认值为`1`。以下是我们如何在 PostgreSQL 中做到这一点:
......@@ -534,7 +534,7 @@ void traverseRS(String sql){
HikariCP 框架由居住在日本的 Brett Wooldridge 创建。*Hikari*在日语中的意思是*光*。它是一个轻量级且相对较小的 API,经过高度优化,允许通过许多属性进行调优,其中一些属性在其他池中不可用。除了标准用户、密码、最大池大小、各种超时设置和缓存配置属性外,它还公开了诸如`allowPoolSuspension``connectionInitSql``connectionTestQuery`等属性,甚至包括处理未及时关闭的连接的属性`leakDetectionThreshold`
要使用 Hikari pool 的最新版本(在编写本书时),请向项目添加以下依赖项:
要使用 Hikari 池子的最新版本(在编写本书时),请向项目添加以下依赖项:
```java
<dependency>
......@@ -866,7 +866,7 @@ org.postgresql.util.PSQLException: ERROR: syntax error at or near "inst"
![](img/e419c1af-ae1e-4f52-942c-a47dacd0d441.png)
未插入第二行。如果在第一个`INSERT INTO`语句之后没有`conn.commit()`,则也不会应用第一个 insert。这是编程事务控制的优点,在许多独立数据更改的情况下,如果一个失败,我们可以跳过它并继续应用其他更改。
未插入第二行。如果在第一个`INSERT INTO`语句之后没有`conn.commit()`,则也不会应用第一个插入。这是编程事务控制的优点,在许多独立数据更改的情况下,如果一个失败,我们可以跳过它并继续应用其他更改。
现在,让我们尝试在第二行插入三行有错误的内容(将字母而不是数字设置为`id`值):
......@@ -981,7 +981,7 @@ traverseRS("select * from test");
`Clob`允许您存储字符数据。`NClob`存储 Unicode 字符数据以支持国际化。它扩展了`Clob`接口并提供了相同的方法。这两个接口都允许您查找 LOB 的长度,并在值中获取子字符串。
`ResultSet``CallableStatement`(我们将在下一个配方中讨论)和`PreparedStatement`接口中的方法允许应用程序以各种方式存储和访问存储的值,其中一些方式是通过相应对象的设置器和 getter,而另一些方式是`bytes[]`或二进制、字符或 ASCII 流。
`ResultSet``CallableStatement`(我们将在下一个配方中讨论)和`PreparedStatement`接口中的方法允许应用程序以各种方式存储和访问存储的值,其中一些方式是通过相应对象的设置器和获取器,而另一些方式是`bytes[]`或二进制、字符或 ASCII 流。
# 怎么做。。。
......@@ -1007,7 +1007,7 @@ execute("create table lobs (id integer, lob oid)");
execute("create table texts (id integer, text text)");
```
查看 JDBC 接口`PreparedStatement``ResultSet`,您会注意到对象的设置器和 getter`get/setBlob()``get/setClob()``get/setNClob()``get/setBytes()`——以及使用`InputStream``Reader``get/setBinaryStream()``get/setAsciiStream()``get/setCharacterStream()`的方法。流式方法的最大优点是,它们在数据库和源之间移动数据,而无需将整个 LOB 存储在内存中。
查看 JDBC 接口`PreparedStatement``ResultSet`,您会注意到对象的设置器和获取器`get/setBlob()``get/setClob()``get/setNClob()``get/setBytes()`——以及使用`InputStream``Reader``get/setBinaryStream()``get/setAsciiStream()``get/setCharacterStream()`的方法。流式方法的最大优点是,它们在数据库和源之间移动数据,而无需将整个 LOB 存储在内存中。
然而,对象的设置器和获取器更接近我们的心,与面向对象的编码保持一致。因此,我们将从它们开始,使用不太大的对象进行演示。我们希望以下代码能够正常工作:
......@@ -1355,7 +1355,7 @@ try (PreparedStatement st = conn.prepareStatement(sql)) {
[这里是 PostgreSQL 文档中的另一条建议](https://jdbc.postgresql.org/documentation/80/binary-data.html)
BYTEA 数据类型不太适合存储非常大量的二进制数据。虽然 BYTEA 类型的列最多可以容纳 1 GB 的二进制数据,但它需要大量内存来处理如此大的值。
`BYTEA`数据类型不太适合存储非常大量的二进制数据。虽然`BYTEA`类型的列最多可以容纳 1 GB 的二进制数据,但它需要大量内存来处理如此大的值。
用于存储二进制数据的大对象方法更适合存储非常大的值,但它有其自身的局限性。 专门删除包含大对象引用的行不会删除大对象。 删除大对象是需要执行的单独操作。 大对象也有一些安全问题,因为连接到数据库的任何人都可以查看和/或修改任何大对象,即使他们没有查看/更新包含大对象引用的行的权限。”
......@@ -1599,7 +1599,7 @@ try (CallableStatement st = conn.prepareCall(sql)) {
我们试过了,但没有成功。以下是 PostgreSQL 文档对此的说明:
以集合形式返回数据的函数不应通过 CallableStatement 接口调用,而应使用普通语句或 PreparedStatement 接口
以集合形式返回数据的函数不应通过`CallableStatement`接口调用,而应使用普通语句或`PreparedStatement`接口
不过,有一种方法可以绕过这一限制。相同的数据库文档描述了如何检索`refcursor`(PostgreSQL 特定功能)值,然后将其转换为`ResultSet`
......@@ -2097,9 +2097,9 @@ create table person1 (
如您所见,`<mapper>`标记有一个`namespace`属性,用于解析不同位置具有相同名称的文件。它可能与映射器文件位置匹配,也可能不匹配。映射器文件位置在配置文件`mb-config1.xml`中指定为标签`<mapper>`的属性资源(参见上一步)。
标签`<insert>``<select>``<update>``<delete>`的属性在很大程度上是不言自明的。添加属性`keyProperty``keyColumn``useGeneratedKeys`(在配置`<settings>`中)以使用数据库生成的值填充插入的对象。如果您在全局范围内不需要该属性,则可以从配置中的设置中删除该属性`useGeneratedKeys`,并仅将其添加到希望利用自动生成某些值的 insert 语句中。我们这样做是因为我们希望获得生成的 ID,并在稍后的代码中使用它来演示如何通过 ID 检索记录。
标签`<insert>``<select>``<update>``<delete>`的属性在很大程度上是不言自明的。添加属性`keyProperty``keyColumn``useGeneratedKeys`(在配置`<settings>`中)以使用数据库生成的值填充插入的对象。如果您在全局范围内不需要该属性,则可以从配置中的设置中删除该属性`useGeneratedKeys`,并仅将其添加到希望利用自动生成某些值的插入语句中。我们这样做是因为我们希望获得生成的 ID,并在稍后的代码中使用它来演示如何通过 ID 检索记录。
使用`<select>`的 ID 属性和类似的标记来调用它们,以及映射器名称空间值。我们将很快向您展示这是如何完成的。构造`#{id}`是指作为参数传入的值,如果该值是基元类型。否则,传入的对象应该有这样一个字段。不需要在对象上具有 getter。如果存在 getter,它必须符合 JavaBean 方法格式。
使用`<select>`的 ID 属性和类似的标记来调用它们,以及映射器名称空间值。我们将很快向您展示这是如何完成的。构造`#{id}`是指作为参数传入的值,如果该值是基元类型。否则,传入的对象应该有这样一个字段。不需要在对象上具有获取器。如果存在获取器,它必须符合 JavaBean 方法格式。
对于返回值,默认情况下,列的名称与对象字段或设置器的名称匹配(必须符合 JavaBean 方法格式)。如果字段(或设置器名称)和列名不同,可以使用标记`<resultMap>`提供映射。例如,如果表`person``person_id``person_name`列,而域对象`Person``id``name`字段,我们可以创建一个映射:
......@@ -2119,7 +2119,7 @@ create table person1 (
</select>
```
或者,也可以使用标准 select 子句别名:
或者,也可以使用标准`select`子句别名:
```java
<select id="selectPersonById" parameterType="int"
......@@ -2162,7 +2162,7 @@ By id 1: Person1{id=1, age=10, name='John'}
实用程序`Resources`有十种重载方法用于读取配置文件。我们已经描述了如何确保 Maven 将配置和映射器文件放在类路径上。
`SqlSession`对象实现了`AutoCloseable`接口,因此我们可以使用 try with resources 块,而不用担心资源泄漏。`SqlSession`接口提供了很多执行方法,包括重载方法`insert()``select()``selectList()``selectMap()``selectOne()``update()``delete()`,这些方法是最常用、最简单的方法。我们还使用了`insert()``selectOne()`。后者确保只返回一个结果。否则,它将抛出一个异常。当用于通过值标识单个记录的列没有唯一约束时,它还会引发异常。这就是为什么我们将`PRIMARY KEY`限定添加到列 ID 中。或者,我们可以只添加一个唯一的约束(将其标记为`PRIMARY KEY`隐式地执行此操作)。
`SqlSession`对象实现了`AutoCloseable`接口,因此我们可以使用资源试用块,而不用担心资源泄漏。`SqlSession`接口提供了很多执行方法,包括重载方法`insert()``select()``selectList()``selectMap()``selectOne()``update()``delete()`,这些方法是最常用、最简单的方法。我们还使用了`insert()``selectOne()`。后者确保只返回一个结果。否则,它将抛出一个异常。当用于通过值标识单个记录的列没有唯一约束时,它还会引发异常。这就是为什么我们将`PRIMARY KEY`限定添加到列 ID 中。或者,我们可以只添加一个唯一的约束(将其标记为`PRIMARY KEY`隐式地执行此操作)。
另一方面,`selectList()`方法生成`List`对象,即使只返回一个结果。我们现在将演示这一点。
......@@ -2328,7 +2328,7 @@ class Person2 {
</configuration>
```
请注意,我们现在有两个别名和两个 mapper`.xml`文件。
请注意,我们现在有两个别名和两个映射器`.xml`文件。
9. `Person2Mapper.xml`文件的内容比我们以前使用的`Person1Mapper.xml`文件的内容小得多:
......@@ -2401,7 +2401,7 @@ class Person2 {
```
但是我们想在`Person2`对象中设置相应的`Family`对象值,所以我们使用`ResultMap``personMap`只描述默认情况下无法完成的映射,我们使用`<association>`标记使用查询`selectFamilyById`将字段`family`与列`family_id`a 相关联。最后一个查询将不会填充`Family`对象的`members`字段,但我们认为我们的演示不需要它。
但是我们想在`Person2`对象中设置相应的`Family`对象值,所以我们使用`ResultMap``personMap`只描述默认情况下无法完成的映射,我们使用`<association>`标记使用查询`selectFamilyById`将字段`family`与列`family_id`相关联。最后一个查询将不会填充`Family`对象的`members`字段,但我们认为我们的演示不需要它。
我们在查询`selectFamilies`中重用了查询`selectMembersOfFamily`。为了填充`Family`对象的字段`members`,我们创建了一个`ResultMap``familyMap`,它使用`selectMembersOfFamily`来完成此操作。
......@@ -2548,7 +2548,7 @@ public class Person1 {
}
```
我们不添加 getter、setter 或任何其他方法;这是为了使我们的代码示例保持简短。根据 JPA 规范,要将该类转换为实体,我们需要将注释`@Entity`添加到类声明中(需要导入`java.persistence.Entity`。这意味着我们希望这个类表示名为`person`的数据库表中的一条记录。默认情况下,实体类的名称与表的名称匹配。但是可以使用注释`@Table(name="<another table name>")`将类映射到另一个名称的表。类似地,每个类属性都映射到具有相同名称的列,并且可以使用注释`@Column (name="<another column name>")`更改默认名称。
我们不添加获取器、设置器或任何其他方法;这是为了使我们的代码示例保持简短。根据 JPA 规范,要将该类转换为实体,我们需要将注释`@Entity`添加到类声明中(需要导入`java.persistence.Entity`。这意味着我们希望这个类表示名为`person`的数据库表中的一条记录。默认情况下,实体类的名称与表的名称匹配。但是可以使用注释`@Table(name="<another table name>")`将类映射到另一个名称的表。类似地,每个类属性都映射到具有相同名称的列,并且可以使用注释`@Column (name="<another column name>")`更改默认名称。
此外,实体类必须有一个主键,即注释`@Id`描述的字段。组合多个字段的复合键也可以使用注释`@IdClass`来定义(在我们的示例中没有使用)。如果数据库中的主键是自动生成的,`@GeneratedValue`注释可以放在该字段的前面。
......@@ -2575,9 +2575,9 @@ public class Person1 {
}
```
该类及其任何持久实例变量都不能声明为 final。通过这种方式,实现框架可以扩展实体类并实现所需的功能。
该类及其任何持久实例变量都不能声明为`final`。通过这种方式,实现框架可以扩展实体类并实现所需的功能。
或者,可以将持久性注释添加到获取器和 setter,而不是实例字段(如果方法名称遵循 JavaBean 约定)。但不允许混淆字段和方法注释,这可能会导致意外的后果。
或者,可以将持久性注释添加到获取器和设置器,而不是实例字段(如果方法名称遵循 JavaBean 约定)。但不允许混淆字段和方法注释,这可能会导致意外的后果。
也可以使用 XML 文件而不是注释来定义 Java 类与数据库表和列之间的映射,但我们将继续使用字段级注释,以便提供最紧凑和清晰的方法来表达意图。
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册