column lengths and LOBs

This commit is contained in:
Gavin 2023-05-11 09:38:04 +02:00 committed by Gavin King
parent e1160fec91
commit 6edefe6f4d
3 changed files with 108 additions and 13 deletions

View File

@ -226,6 +226,7 @@ With rare exception, the default behavior of every one of these settings was car
The properties you really do need to get started are these three:
.JDBC connection settings
[cols=",2"]
|===
| Configuration property name | Purpose
@ -255,6 +256,7 @@ This pool is not meant for use in production, and later, when we discuss perform
Alternatively, in a container environment, you'll need at least one of these properties:
.Transaction management settings
[cols=",2"]
|===
| Configuration property name | Purpose
@ -276,7 +278,7 @@ initialization time by specifying one or more of the following configuration
properties:
.Schema management settings
[cols="1,1"]
[cols=",2"]
|===
| Configuration property name | Purpose
@ -339,6 +341,7 @@ logger.hibernate.level = debug
You can make the logged SQL more readable by enabling one or both of the following settings:
.Setting for SQL logging to the console
[cols=",2"]
|===
| Configuration property name | Purpose
@ -353,6 +356,7 @@ These settings can really help when troubleshooting SQL.
The following properties are very useful for minimizing the amount of information you'll need to explicitly specify in `@Table` and `@Column` annotations, which we'll discuss below in <<mapping-entity-classes>>:
.Settings for minimizing explicit mapping information
[cols=",2"]
|===
| Configuration property name | Purpose
@ -376,7 +380,8 @@ Please refer to the Javadoc for these interfaces for more information about the
_By default,_ SQL Server's `char` and `varchar` types don't accommodate Unicode data. So, if you're working with SQL Server, you might need to force Hibernate to use the `nchar` and `nvarchar` types.
.Setting the use of nationalized charcter data
.Setting the use of nationalized character data
[cols=",2"]
|===
| Configuration property name | Purpose

View File

@ -208,6 +208,7 @@ On the other hand, if, as is more common, you're working with a pre-existing dat
JPA defines the following strategies for generating ids, which are enumerated by `GenerationType`:
.Standard id generation strategies
[cols="3,2,6"]
|===
| Strategy | Java type | Implementation
@ -215,7 +216,7 @@ JPA defines the following strategies for generating ids, which are enumerated by
| `GenerationType.IDENTITY` | `Long` or `Integer` | An identity or autoincrement column.
| `GenerationType.SEQUENCE` | `Long` or `Integer` | A database sequence.
| `GenerationType.TABLE` | `Long` or `Integer` | A database table.
| `GenerationType.AUTO` | `Long` or `Integer` | Selects `SEQUENCE` `TABLE`, or `UUID` based on the identifier type and capabilities of the database.
| `GenerationType.AUTO` | `Long` or `Integer` | Selects `SEQUENCE`, `TABLE`, or `UUID` based on the identifier type and capabilities of the database.
|===
For example, the following id maps to a SQL `identity`, `auto_increment`, or `bigserial` column:
@ -447,6 +448,7 @@ A _basic_ attribute of an entity is a field or property which maps to a single c
The JPA specification defines a quite limited set of basic types:
.JPA-standard basic attribute types
[cols="3,2,6"]
|====
| Classification | Package | Types
@ -476,6 +478,17 @@ Serializing a Java object and storing its binary representation in the database
As we'll soon see in <<embeddable-objects>>, Hibernate has much better ways to handle complex Java objects.
====
Hibernate slightly extends this list with the following types:
.Additional basic attribute types in Hibernate
[cols="3,2,6"]
|====
| Classification | Package | Types
| Date/time types | `java.time` | `Duration`, `ZoneId`, `ZoneOffset`, `ZonedDateTime`, `Year`
| Miscellaneous | `java.util` | `Currency`, `URL`
|====
The `@Basic` annotation explicitly specifies that an attribute is basic, but it's often not needed, since attributes are assumed basic by default.
On the other hand, if an attribute cannot be null, use of `@Basic(optional=false)` is highly recommended.
@ -1203,18 +1216,18 @@ Let's pause to remember the annotations we've met so far.
|===
.Declaring basic attributes
[cols="2,4,1"]
[cols="2,4,2,1"]
|===
| Annotation | Purpose | JPA-standard
| Annotation | Purpose | Required | JPA-standard
| `@Id` | Declare a basic-typed identifier attribute | &#10003;
| `@Version` | Declare a version attribute | &#10003;
| `@Basic` | (Optional) Declare a basic attribute | &#10003;
| `@EmbeddedId` | Declare an embeddable-typed identifier attribute | &#10003;
| `@Embedded` | (Optional) Declare an embeddable-typed attribute | &#10003;
| `@Enumerated` | (Optional) Declare an `enum`-typed attribute and specify how it is encoded | &#10003;
| `@Array` | (Optional) Declare that an attribute maps to a SQL `ARRAY`, and specify the length | &#10007;
| `@ElementCollection` | Declare that a collection is mapped to a dedicated table | &#10003;
| `@Id` | Declare a basic-typed identifier attribute | Yes | &#10003;
| `@Version` | Declare a version attribute | Yes | &#10003;
| `@Basic` | Declare a basic attribute | No | &#10003;
| `@EmbeddedId` | Declare an embeddable-typed identifier attribute | Yes | &#10003;
| `@Embedded` | Declare an embeddable-typed attribute | No | &#10003;
| `@Enumerated` | Declare an `enum`-typed attribute and specify how it is encoded | No | &#10003;
| `@Array` | Declare that an attribute maps to a SQL `ARRAY`, and specify the length | No | &#10007;
| `@ElementCollection` | Declare that a collection is mapped to a dedicated table | Yes | &#10003;
|===
.Converters and compositional basic types

View File

@ -156,6 +156,7 @@ is a bad idea, since it's impossible to create a foreign key constraint that tar
The following annotations specify exactly how elements of the domain model map to tables of the relational model:
.Annotations for mapping tables
[cols=",5"]
|===
| Annotation | Purpose
@ -187,6 +188,7 @@ class Book { ... }
The `@Table` annotation can do more than just specify a name:
.`@Table` annotation members
[cols=",8"]
|===
| Annotation member | Purpose
@ -208,6 +210,7 @@ It's usually better to set the configuration properties `hibernate.default_schem
The `@SecondaryTable` annotation is even more interesting:
.`@SecondaryTable` annotation members
[cols=",8"]
|===
| Annotation member | Purpose
@ -228,6 +231,7 @@ To understand this annotation better, we must first discuss column mappings in g
These annotations specify how elements of the domain model map to columns of tables in the relational model:
.Annotations for mapping columns
[cols=",5"]
|===
| Annotation | Purpose
@ -246,6 +250,7 @@ We use the `@Column` annotation to map basic attributes.
The `@Column` annotation is not only useful for specifying the column name.
.`@Column` annotation members
[cols=",8"]
|===
| Annotation member | Purpose
@ -299,6 +304,7 @@ We don't use `@Column` to map associations.
The `@JoinColumn` annotation is used to customize a foreign key column.
.`@JoinColumn` annotation members
[cols=",8"]
|===
| Annotation member | Purpose
@ -346,6 +352,7 @@ The `@PrimaryKeyJoinColumn` is a special-purpose annotation for mapping:
- the primary key column of the primary table mapped by a subclass in a `JOINED` inheritance hierarchy&mdash;which is also a foreign key referencing the primary table mapped by the root entity.
.`@PrimaryKeyJoinColumn` annotation members
[cols=",8"]
|===
| Annotation member | Purpose
@ -387,3 +394,73 @@ class Book {
}
----
[[column-lengths]]
=== Column lengths and adaptive column types
Hibernate automatically adjusts the column type used in generated DDL based on the column length specified by the `@Column` annotation.
So we don't usually need to explicitly specify that a column should be of type `TEXT` or `CLOB`&mdash;or worry about the parade of `TINYTEXT`, `MEDIUMTEXT`, `TEXT`, `LONGTEXT` types on MySQL&mdash;because Hibernate will automatically select one of those types if required to accommodate a string of the `length` we specify.
The constant values defined in the class `org.hibernate.Length` are very helpful here:
.Predefined column lengths
[cols=",,8"]
|===
| Constant | Value | Description
| `DEFAULT` | 255 | The default length of a `VARCHAR` or `VARBINARY` column when none is explicitly specified
| `LONG` | 32600 | The largest column length for a `VARCHAR` or `VARBINARY` that is allowed on every database Hibernate supports
| `LONG16` | 32767 | The maximum length that can be represented using 16 bits (but this length is too large for a `VARCHAR` or `VARBINARY` column on for some database)
| `LONG32` | 2147483647 | The maximum length for a Java string
|===
We can use these constants in the `@Column` annotation:
[source,java]
----
@Column(length=LONG)
String text;
@Column(length=LONG32)
byte[] binaryData;
----
This is usually all you need to do to make use of large object types in Hibernate.
[[lobs]]
=== LOBs
JPA provides a `@Lob` annotation which specifies that a field should be persisted as a `BLOB` or `CLOB`.
[NOTE]
.Semantics of the `@Lob` amnotation
====
What the spec actually says is that the field should be persisted
> as a large object to a database-supported large object type.
It's quite unclear what this means, and the spec goes on to say that
> the treatment of the `Lob` annotation is provider-dependent
which doesn't help much.
====
Hibernate interprets this annotation in what we think is the most reasonable way.
In Hibernate, an attribute annotated `@Lob` will be written to JDBC using the `setClob()` or `setBlob()` method of `PreparedStatement`, and will be read from JDBC using the `getClob()` or `getBlob()` method of `ResultSet`.
Now, the use of these JDBC methods is usually unnecessary!
JDBC drivers are perfectly capable of converting between `String` and `CLOB` or between `byte[]` and `BLOB`.
So unless you specifically need to use these JDBC LOB APIs, you _don't_ need the `@Lob` annotation.
Instead, as we just saw in <<column-lengths>>, all you need is to specify a large enough column `length` to accommodate the data you plan to write to that column.
[WARNING]
.PostgreSQL `BYTEA` and `TEXT`
====
Unfortunately, the driver for PostgreSQL does not allow `BYTEA` or `TEXT` columns to be read via the JDBC LOB APIs.
This limitation of the Postgres driver has resulted in a whole cottage industry of bloggers and stackoverflow question-answerers recommending convoluted ways to hack the Hibernate `Dialect` for Postgres to allow an attribute annotated `@Lob` to be written using `setString()` and read using `getString()`.
But _simply removing the `@Lob` annotation has exactly the same effect!_
====