Polish "Stop referring to STRUCT and ARRAY as they are deprecated"

See gh-33248
This commit is contained in:
Stéphane Nicoll 2024-07-21 11:18:27 +02:00
parent 955602bdbd
commit af8dc44469
10 changed files with 283 additions and 126 deletions

View File

@ -20,6 +20,7 @@ asciidoc:
fold: 'all'
table-stripes: 'odd'
include-java: 'example$docs-src/main/java/org/springframework/docs'
include-kotlin: 'example$docs-src/main/kotlin/org/springframework/docs'
spring-site: 'https://spring.io'
spring-site-blog: '{spring-site}/blog'
spring-site-cve: "{spring-site}/security"

View File

@ -43,8 +43,12 @@ repositories {
dependencies {
api(project(":spring-context"))
api(project(":spring-jdbc"))
api(project(":spring-jms"))
api(project(":spring-web"))
api("org.jetbrains.kotlin:kotlin-stdlib")
api("com.oracle.database.jdbc:ojdbc11")
api("jakarta.jms:jakarta.jms-api")
api("jakarta.servlet:jakarta.servlet-api")

View File

@ -209,51 +209,10 @@ are passed in as a parameter to the stored procedure.
The `SqlReturnType` interface has a single method (named `getTypeValue`) that must be
implemented. This interface is used as part of the declaration of an `SqlOutParameter`.
The following example shows returning the value of an Oracle `STRUCT` object of the user
The following example shows returning the value of a `java.sql.Struct` object of the user
declared type `ITEM_TYPE`:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
public class TestItemStoredProcedure extends StoredProcedure {
public TestItemStoredProcedure(DataSource dataSource) {
// ...
declareParameter(new SqlOutParameter("item", Types.STRUCT, "ITEM_TYPE",
(CallableStatement cs, int colIndx, int sqlType, String typeName) -> {
Struct struct = (Struct) cs.getObject(colIndx);
Object[] attr = struct.getAttributes();
TestItem item = new TestItem();
item.setId(((Number) attr[0]).longValue());
item.setDescription((String) attr[1]);
item.setExpirationDate((java.util.Date) attr[2]);
return item;
}));
// ...
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
class TestItemStoredProcedure(dataSource: DataSource) : StoredProcedure() {
init {
// ...
declareParameter(SqlOutParameter("item", OracleTypes.STRUCT, "ITEM_TYPE") { cs, colIndx, sqlType, typeName ->
val struct = cs.getObject(colIndx) as STRUCT
val attr = struct.getAttributes()
TestItem((attr[0] as Long, attr[1] as String, attr[2] as Date)
})
// ...
}
}
----
======
include-code::./TestItemStoredProcedure[]
You can use `SqlTypeValue` to pass the value of a Java object (such as `TestItem`) to a
stored procedure. The `SqlTypeValue` interface has a single method (named
@ -261,88 +220,15 @@ stored procedure. The `SqlTypeValue` interface has a single method (named
can use it to create database-specific objects, such as `java.sql.Struct` instances
or `java.sql.Array` instances. The following example creates a `java.sql.Struct` instance:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
final TestItem testItem = new TestItem(123L, "A test item",
new SimpleDateFormat("yyyy-M-d").parse("2010-12-31"));
SqlTypeValue value = new AbstractSqlTypeValue() {
protected Object createTypeValue(Connection conn, int sqlType, String typeName) throws SQLException {
var item = new Object[] {
testItem.getId(),
testItem.getDescription(),
new java.sql.Date(testItem.getExpirationDate().getTime())
};
return connection.createStruct(typeName, item);
}
};
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
val (id, description, expirationDate) = TestItem(123L, "A test item",
SimpleDateFormat("yyyy-M-d").parse("2010-12-31"))
val value = object : AbstractSqlTypeValue() {
override fun createTypeValue(conn: Connection, sqlType: Int, typeName: String?): Any {
val itemDescriptor = StructDescriptor(typeName, conn)
return STRUCT(itemDescriptor, conn,
arrayOf(id, description, java.sql.Date(expirationDate.time)))
}
}
----
======
include-code::./SqlTypeValueFactory[tag=struct,indent=0]
You can now add this `SqlTypeValue` to the `Map` that contains the input parameters for the
`execute` call of the stored procedure.
Another use for the `SqlTypeValue` is passing in an array of values to an Oracle stored
procedure. Oracle has its own internal `ARRAY` class that must be used in this case, and
you can use the `SqlTypeValue` to create an instance of the Oracle `ARRAY` and populate
procedure. Oracle has an `createOracleArray` method on `OracleConnection` that you can
access by unwrapping it. You can use the `SqlTypeValue` to create an array and populate
it with values from the Java `java.sql.Array`, as the following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
final Long[] ids = new Long[] {1L, 2L};
SqlTypeValue value = new AbstractSqlTypeValue() {
protected Object createTypeValue(Connection conn, int sqlType, String typeName) throws SQLException {
return conn.unwrap(OracleConnection.class).createOracleArray(typeName, ids);
}
};
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
class TestItemStoredProcedure(dataSource: DataSource) : StoredProcedure() {
init {
val ids = arrayOf(1L, 2L)
val value = object : AbstractSqlTypeValue() {
override fun createTypeValue(conn: Connection, sqlType: Int, typeName: String?): Any {
val arrayDescriptor = ArrayDescriptor(typeName, conn)
return ARRAY(arrayDescriptor, conn, ids)
}
}
}
}
----
======
[NOTE]
====
Use `unwrap(OracleConnection.class)` method if connection is not an OracleConnection's instance
====
include-code::./SqlTypeValueFactory[tag=oracle-array,indent=0]

View File

@ -323,12 +323,12 @@ use these alternative input classes.
The `SimpleJdbcCall` class uses metadata in the database to look up names of `in`
and `out` parameters so that you do not have to explicitly declare them. You can
declare parameters if you prefer to do that or if you have parameters (such as `ARRAY`
or `STRUCT`) that do not have an automatic mapping to a Java class. The first example
shows a simple procedure that returns only scalar values in `VARCHAR` and `DATE` format
from a MySQL database. The example procedure reads a specified actor entry and returns
`first_name`, `last_name`, and `birth_date` columns in the form of `out` parameters.
The following listing shows the first example:
declare parameters if you prefer to do that or if you have parameters that do not
have an automatic mapping to a Java class. The first example shows a simple procedure
that returns only scalar values in `VARCHAR` and `DATE` format from a MySQL database.
The example procedure reads a specified actor entry and returns `first_name`,
`last_name`, and `birth_date` columns in the form of `out` parameters. The following
listing shows the first example:
[source,sql,indent=0,subs="verbatim,quotes"]
----

View File

@ -0,0 +1,59 @@
/*
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.docs.dataaccess.jdbc.jdbccomplextypes;
import java.sql.Connection;
import java.sql.SQLException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import oracle.jdbc.driver.OracleConnection;
import org.springframework.jdbc.core.SqlTypeValue;
import org.springframework.jdbc.core.support.AbstractSqlTypeValue;
@SuppressWarnings("unused")
class SqlTypeValueFactory {
void createStructSample() throws ParseException {
// tag::struct[]
TestItem testItem = new TestItem(123L, "A test item",
new SimpleDateFormat("yyyy-M-d").parse("2010-12-31"));
SqlTypeValue value = new AbstractSqlTypeValue() {
protected Object createTypeValue(Connection connection, int sqlType, String typeName) throws SQLException {
Object[] item = new Object[] { testItem.getId(), testItem.getDescription(),
new java.sql.Date(testItem.getExpirationDate().getTime()) };
return connection.createStruct(typeName, item);
}
};
// end::struct[]
}
void createOracleArray() {
// tag::oracle-array[]
Long[] ids = new Long[] {1L, 2L};
SqlTypeValue value = new AbstractSqlTypeValue() {
protected Object createTypeValue(Connection conn, int sqlType, String typeName) throws SQLException {
return conn.unwrap(OracleConnection.class).createOracleArray(typeName, ids);
}
};
// end::oracle-array[]
}
}

View File

@ -0,0 +1,61 @@
/*
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.docs.dataaccess.jdbc.jdbccomplextypes;
import java.util.Date;
class TestItem {
private Long id;
private String description;
private Date expirationDate;
public TestItem() {
}
public TestItem(Long id, String description, Date expirationDate) {
this.id = id;
this.description = description;
this.expirationDate = expirationDate;
}
public Long getId() {
return this.id;
}
public void setId(Long id) {
this.id = id;
}
public String getDescription() {
return this.description;
}
public void setDescription(String description) {
this.description = description;
}
public Date getExpirationDate() {
return this.expirationDate;
}
public void setExpirationDate(Date expirationDate) {
this.expirationDate = expirationDate;
}
}

View File

@ -0,0 +1,46 @@
/*
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.docs.dataaccess.jdbc.jdbccomplextypes;
import java.sql.CallableStatement;
import java.sql.Struct;
import java.sql.Types;
import javax.sql.DataSource;
import org.springframework.jdbc.core.SqlOutParameter;
import org.springframework.jdbc.object.StoredProcedure;
@SuppressWarnings("unused")
public class TestItemStoredProcedure extends StoredProcedure {
public TestItemStoredProcedure(DataSource dataSource) {
super(dataSource, "get_item");
declareParameter(new SqlOutParameter("item", Types.STRUCT, "ITEM_TYPE",
(CallableStatement cs, int colIndx, int sqlType, String typeName) -> {
Struct struct = (Struct) cs.getObject(colIndx);
Object[] attr = struct.getAttributes();
TestItem item = new TestItem();
item.setId(((Number) attr[0]).longValue());
item.setDescription((String) attr[1]);
item.setExpirationDate((java.util.Date) attr[2]);
return item;
}));
// ...
}
}

View File

@ -0,0 +1,57 @@
/*
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.docs.dataaccess.jdbc.jdbccomplextypes
import oracle.jdbc.driver.OracleConnection
import org.springframework.jdbc.core.SqlTypeValue
import org.springframework.jdbc.core.support.AbstractSqlTypeValue
import java.sql.Connection
import java.sql.Date
import java.text.SimpleDateFormat
@Suppress("unused")
class SqlTypeValueFactory {
fun createStructSample(): AbstractSqlTypeValue {
// tag::struct[]
val testItem = TestItem(123L, "A test item",
SimpleDateFormat("yyyy-M-d").parse("2010-12-31"))
val value = object : AbstractSqlTypeValue() {
override fun createTypeValue(connection: Connection, sqlType: Int, typeName: String?): Any {
val item = arrayOf<Any>(testItem.id, testItem.description,
Date(testItem.expirationDate.time))
return connection.createStruct(typeName, item)
}
}
// end::struct[]
return value
}
fun createOracleArray() : SqlTypeValue {
// tag::oracle-array[]
val ids = arrayOf(1L, 2L)
val value: SqlTypeValue = object : AbstractSqlTypeValue() {
override fun createTypeValue(conn: Connection, sqlType: Int, typeName: String?): Any {
return conn.unwrap(OracleConnection::class.java).createOracleArray(typeName, ids)
}
}
// end::oracle-array[]
return value
}
}

View File

@ -0,0 +1,42 @@
/*
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.docs.dataaccess.jdbc.jdbccomplextypes
import org.springframework.jdbc.core.SqlOutParameter
import org.springframework.jdbc.`object`.StoredProcedure
import java.sql.CallableStatement
import java.sql.Struct
import java.sql.Types
import java.util.Date
import javax.sql.DataSource
@Suppress("unused")
class TestItemStoredProcedure(dataSource: DataSource) : StoredProcedure(dataSource, "get_item") {
init {
declareParameter(SqlOutParameter("item",Types.STRUCT,"ITEM_TYPE") {
cs: CallableStatement, colIndx: Int, _: Int, _: String? ->
val struct = cs.getObject(colIndx) as Struct
val attr = struct.attributes
val item = TestItem()
item.id = (attr[0] as Number).toLong()
item.description = attr[1] as String
item.expirationDate = attr[2] as Date
item
})
// ...
}
}

View File

@ -34,6 +34,7 @@ dependencies {
api("com.google.protobuf:protobuf-java-util:3.25.3")
api("com.h2database:h2:2.2.224")
api("com.jayway.jsonpath:json-path:2.9.0")
api("com.oracle.database.jdbc:ojdbc11:21.9.0.0")
api("com.rometools:rome:1.19.0")
api("com.squareup.okhttp3:mockwebserver:3.14.9")
api("com.squareup.okhttp3:okhttp:3.14.9")