Spring5参考指南
  • 简介
  • 前言
    • 1. “Spring”代表什么
    • 2. Spring和Spring框架的历史
    • 3. 设计哲学
    • 4. 反馈和贡献
    • 5. 开始
  • 核心技术
    • 1. IoC容器
      • 1.1 IoC容器和Beans介绍
      • 1.2 IoC容器概述
      • 1.3 Bean概述
      • 1.4 依赖
      • 1.5 Bean作用域
      • 1.6 自定义Bean
      • 1.7 Bean的继承
      • 1.8 容器扩展点
      • 1.9 基于注解的容器配置
      • 1.10 类路径扫描和托管组件
      • 1.11 使用JSR 330标准注解
      • 1.12 基于Java的容器配置
      • 1.13 环境抽象
      • 1.14 注册LoadTimeWeaver
      • 1.15 ApplicationContext的其他功能
      • 1.16 BeanFactory
    • 2.资源
      • 2.1介绍
      • 2.2资源接口
      • 2.3内置资源实现
      • 2.4ResourceLoader
      • 2.5ResourceLoaderAware接口
      • 2.6资源作为依赖
      • 2.7应用程序上下文和资源路径
    • 3.验证,数据绑定,和类型转换
      • 3.1使用Spring Validator接口
      • 3.2将代码解析为错误消息
      • 3.3bean操作和BeanWrapper
      • 3.4Spring类型转换
      • 3.5Spring字段格式化
      • 3.6配置全局Date和Time格式
      • 3.7Spring验证
    • 4.SpEL Spring表达式语言
      • 4.1求值
      • 4.2bean定义中的表达式
      • 4.3语言引用
    • 5.Spring AOP
      • 5.1什么是AOP
      • 5.1Spring AOP的能力和目标
      • 5.3AOP代理
      • 5.4@AspectJ 支持
      • 5.5基于Schema的AOP支持
      • 5.6选择要使用的AOP声明样式
      • 5.7混合Aspect类型
      • 5.8代理机制
      • 5.9程序创建@AspectJ代理
      • 5.10在Spring应用程序中使用AspectJ
      • 5.11更多资源
    • 6.Spring AOP APIs
      • 6.1Pointcut API
      • 6.2Advice API
      • 6.3Advisor API
      • 6.4使用ProxyFactoryBean来创建AOP代理
      • 6.5简介的代理定义
      • 6.6使用ProxyFactory创建AOP代理
      • 6.7操作被通知的对象
      • 6.8使用auto-proxy功能
      • 6.9使用TargetSource的实现
      • 6.10定义新的Advice Types
    • 7.Null-safety
    • 8.数据缓存和解码器
    • 9.附录
      • 9.1XML Schemas
      • 9.2创建XML Schemas
  • 测试
    • 1.Spring测试介绍
    • 2.单元测试
      • 2.1Mock Objects
      • 2.2单元测试支持类
    • 3.集成测试
      • 3.1概览
      • 3.2集成测试的目的
      • 3.3JDBC测试支持
      • 3.4注解
      • 3.5Spring TestContext框架
      • 3.6Spring MVC测试框架
      • 3.7WebTestClient
    • 4.更多资源
  • 数据访问
    • 1.事务管理
    • 2.DAO支持
    • 3.JDBC
      • 3.1选择JDBC数据库访问方法
      • 3.2包层次结构
      • 3.3使用JDBC核心类控制基本JDBC处理和错误处理
      • 3.4控制数据库连接
      • 3.5JDBC批处理操作
      • 3.6使用SimpleJdbc
      • 3.7将JDBC操作建模为Java对象
      • 3.8参数和数据值处理的常见问题
      • 3.9嵌入式数据库支持
      • 3.10初始化数据源
    • 4.ORM
      • 4.1Spring ORM介绍
      • 4.2ORM集成的一般注意事项
      • 4.3Hibernate
      • 4.4JPA
    • 5.使用Object-XML映射封装XML
  • Web Servlet
    • 1. Spring Web MVC
      • 1.1 DispatcherServlet
      • 1.2 Filters
      • 1.3 Controllers注解
      • 1.4 URI链接
      • 1.5 异步请求
      • 1.6 CORS
      • 1.7 Web Security
      • 1.8 HTTP Caching
      • 1.9 View技术
      • 1.10 MVC配置
      • 1.11 HTTP/2
    • 2. REST客户端
    • 3. 测试
    • 4. WebSockets
      • 4.1 WebSocket介绍
      • 4.2 WebSocket API
      • 4.3 SockJS Fallback
      • 4.4 STOMP
  • Web Reactive
    • 1.Spring WebFlux
      • 1.1 Overview
      • 1.2 Reactive Core
      • 1.3 DispatcherHandler
      • 1.4 Annotated Controllers
      • 1.5 Functional Endpoints
      • 1.6 URI Links
      • 1.7 CORS
      • 1.8 Web Security
      • 1.9 View Technologies
      • 1.10 HTTP Caching
      • 1.11 WebFlux Config
      • 1.12 HTTP/2
    • 2.WebClient
    • 3.WebSockets
    • 4.测试
    • 5.Reactive库
由 GitBook 提供支持
在本页
  • 3.8.1 提供参数的SQL类型信息
  • 3.8.2 处理BLOB和CLOB对象
  • 3.8.3 传入IN子句的值列表
  • 3.8.4 处理存储过程调用的复杂类型

这有帮助吗?

  1. 数据访问
  2. 3.JDBC

3.8参数和数据值处理的常见问题

Spring Framework的JDBC支持提供的不同方法中存在参数和数据值的常见问题。本节介绍如何解决它们。

3.8.1 提供参数的SQL类型信息

通常,Spring根据传入的参数类型确定参数的SQL类型。可以在设置参数值时显式提供要使用的SQL类型。有时需要正确设置NULL值。

您可以通过几种方式提供SQL类型信息:

  • JdbcTemplate的许多更新和查询方法都采用int数组形式的附加参数。该数组用于通过使用java.sql.Types类中的常量值来指示相应参数的SQL类型。为每个参数提供一个条目。

  • 您可以使用SqlParameterValue类包装需要此附加信息的参数值。为此,请为每个值创建一个新实例,然后在构造函数中传入SQL类型和参数值。您还可以为数字值提供可选的比例参数。

  • 对于使用命名参数的方法,可以使用SqlParameterSource类,BeanPropertySqlParameterSource或MapSqlParameterSource。它们都具有用于为任何命名参数值注册SQL类型的方法。

3.8.2 处理BLOB和CLOB对象

您可以在数据库中存储图像,其他二进制数据和大块文本。这些大对象称为二进制数据的BLOB(二进制大型对象),而字符数据称为CLOB(字符大型对象)。在Spring中,可以直接使用JdbcTemplate来处理这些大对象,也可以使用RDBMS Objects和SimpleJdbc类提供的更高抽象来处理这些大对象。所有这些方法都使用LobHandler接口的实现来对LOB(大对象)数据进行实际管理。 LobHandler通过getLobCreator方法提供对LobCreator类的访问,该方法用于创建要插入的新LOB对象。

LobCreator和LobHandler为LOB输入和输出提供以下支持:

  • BLOB

    • byte []:getBlobAsBytes和setBlobAsBytes

    • InputStream:getBlobAsBinaryStream和setBlobAsBinaryStream

  • CLOB

    • String:getClobAsString和setClobAsString

    • InputStream:getClobAsAsciiStream和setClobAsAsciiStream

    • Reader:getClobAsCharacterStream和setClobAsCharacterStream

下一个示例显示了如何创建和插入BLOB。稍后,我们展示如何从数据库中读取回它。

本示例使用JdbcTemplate和AbstractLobCreatingPreparedStatementCallback的实现。它实现了一种方法setValues。此方法提供了一个LobCreator,我们可以使用它来设置SQL插入语句中的LOB列的值。

对于此示例,我们假设存在一个变量lobHandler,该变量已经设置为DefaultLobHandler的实例。通常,您可以通过依赖注入来设置此值。

以下示例显示如何创建和插入BLOB:

final File blobIn = new File("spring2004.jpg");
final InputStream blobIs = new FileInputStream(blobIn);
final File clobIn = new File("large.txt");
final InputStream clobIs = new FileInputStream(clobIn);
final InputStreamReader clobReader = new InputStreamReader(clobIs);

jdbcTemplate.execute(
    "INSERT INTO lob_table (id, a_clob, a_blob) VALUES (?, ?, ?)",
    new AbstractLobCreatingPreparedStatementCallback(lobHandler) {  
        protected void setValues(PreparedStatement ps, LobCreator lobCreator) throws SQLException {
            ps.setLong(1, 1L);
            lobCreator.setClobAsCharacterStream(ps, 2, clobReader, (int)clobIn.length());  
            lobCreator.setBlobAsBinaryStream(ps, 3, blobIs, (int)blobIn.length());  
        }
    }
);

blobIs.close();
clobReader.close();

如果在从DefaultLobHandler.getLobCreator()返回的LobCreator上调用setBlobAsBinaryStream,setClobAsAsciiStream或setClobAsCharacterStream方法,则可以选择为contentLength参数指定负值。 如果指定的内容长度为负,则DefaultLobHandler将使用set-stream方法的JDBC 4.0变体,而不使用length参数。 否则,它将指定的长度传递给驱动程序。

请参阅有关JDBC驱动程序的文档,以用于验证它是否支持流式LOB而不提供内容长度。

现在是时候从数据库中读取LOB数据了。 同样,您将JdbcTemplate与相同的实例变量lobHandler和对DefaultLobHandler的引用一起使用。 以下示例显示了如何执行此操作:

List<Map<String, Object>> l = jdbcTemplate.query("select id, a_clob, a_blob from lob_table",
    new RowMapper<Map<String, Object>>() {
        public Map<String, Object> mapRow(ResultSet rs, int i) throws SQLException {
            Map<String, Object> results = new HashMap<String, Object>();
            String clobText = lobHandler.getClobAsString(rs, "a_clob");  
            results.put("CLOB", clobText);
            byte[] blobBytes = lobHandler.getBlobAsBytes(rs, "a_blob");  
            results.put("BLOB", blobBytes);
            return results;
        }
    });

3.8.3 传入IN子句的值列表

SQL标准允许基于包含变量值列表的表达式选择行。 一个典型的示例是select * from T_ACTOR where id in (1, 2, 3)。 JDBC标准不直接为准备好的语句支持此变量列表。 您不能声明可变数量的占位符。 您需要准备好所需数量的占位符的多种变体,或者一旦知道需要多少个占位符,就需要动态生成SQL字符串。 NamedParameterJdbcTemplate和JdbcTemplate中提供的命名参数支持采用后一种方法。 您可以将值作为原始对象的java.util.List传入。 此列表用于插入所需的占位符,并在语句执行期间传递值。

传递许多值时要小心。 JDBC标准不保证您可以为in表达式列表使用100个以上的值。 各种数据库都超过了这个数目,但是它们通常对允许多少个值有硬性限制。 例如,Oracle的限制为1000。

除了值列表中的原始值之外,您还可以创建对象数组的java.util.List。 该列表可以支持为in子句定义的多个表达式,例如,T_ACTOR的select * from((1,'Johnson'),(2,'Harrop'\))中的(id,last_name)。 当然,这要求您的数据库支持此语法。

3.8.4 处理存储过程调用的复杂类型

调用存储过程时,有时可以使用特定于数据库的复杂类型。 为了容纳这些类型,Spring提供了一个SqlReturnType来处理从存储过程调用返回的值,并提供SqlTypeValue作为参数作为参数传递给存储过程。

SqlReturnType接口具有必须实现的单个方法(名为getTypeValue)。 此接口用作SqlOutParameter声明的一部分。 以下示例显示了返回用户声明类型为ITEM_TYPE的Oracle STRUCT对象的值:

public class TestItemStoredProcedure extends StoredProcedure {

    public TestItemStoredProcedure(DataSource dataSource) {
        ...
        declareParameter(new SqlOutParameter("item", OracleTypes.STRUCT, "ITEM_TYPE",
            new SqlReturnType() {
                public Object getTypeValue(CallableStatement cs, int colIndx, int sqlType, String typeName) throws SQLException {
                    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;
                }
            }));
        ...
    }

您可以使用SqlTypeValue将Java对象(例如TestItem)的值传递给存储过程。 SqlTypeValue接口具有必须实现的单个方法(名为createTypeValue)。 传入活动连接,您可以使用它来创建特定于数据库的对象,例如StructDescriptor实例或ArrayDescriptor实例。 下面的示例创建一个StructDescriptor实例:

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 {
        StructDescriptor itemDescriptor = new StructDescriptor(typeName, conn);
        Struct item = new STRUCT(itemDescriptor, conn,
        new Object[] {
            testItem.getId(),
            testItem.getDescription(),
            new java.sql.Date(testItem.getExpirationDate().getTime())
        });
        return item;
    }
};

现在,您可以将此SqlTypeValue添加到包含用于存储过程的execute调用的输入参数的Map中。

SqlTypeValue的另一个用途是将值数组传递给Oracle存储过程。 在这种情况下,Oracle有其自己的内部ARRAY类,您可以使用SqlTypeValue创建Oracle ARRAY的实例,并使用Java ARRAY中的值填充它,如以下示例所示:

final Long[] ids = new Long[] {1L, 2L};

SqlTypeValue value = new AbstractSqlTypeValue() {
    protected Object createTypeValue(Connection conn, int sqlType, String typeName) throws SQLException {
        ArrayDescriptor arrayDescriptor = new ArrayDescriptor(typeName, conn);
        ARRAY idArray = new ARRAY(arrayDescriptor, conn, ids);
        return idArray;
    }
};
上一页3.7将JDBC操作建模为Java对象下一页3.9嵌入式数据库支持

最后更新于3年前

这有帮助吗?