小编Unc*_*air的帖子

如何在@ExceptionHandler中获取@RequestBody(Spring REST)

我使用的是Spring Boot 1.4.1,其中包括spring-web-4.3.3.我有一个带有@ControllerAdvice注释的类和带有注释的方法@ExceptionHandler来处理服务代码抛出的异常.在处理这些异常时,我想记录@RequestBody那是PUT和POST操作请求的一部分,这样我就可以看到导致问题的请求体,在我的情况下,这对于诊断至关重要.

Per Spring Docs方法的方法签名@ExceptionHandler可以包括各种各样的东西,包括HttpServletRequest.请求体通常可以从这里通过getInputStream()或获得getReader(),但是如果我的控制器方法解析请求体,就像"@RequestBody Foo fooBody"我所做的一样,HttpServletRequest's输入流或读取器在调用异常处理程序方法时已经关闭.本质上,Spring已经读取了请求主体,类似于此处描述的问题.使用servlet是一个常见问题,请求体只能读取一次.

不幸的@RequestBody是,不是异常处理程序方法可用的选项之一,如果是那样的话,我可以使用它.

我可以添加一个InputStream异常处理程序方法,但最终与HttpServletRequest的InputStream相同,因此具有相同的问题.

我也尝试获取当前请求,((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest()这是获取当前请求的另一个技巧,但这最终是Spring传递到异常处理程序方法的相同HttpServletRequest,因此也有同样的问题.

我已经阅读了一些像这样的解决方案,涉及在过滤器链中插入一个自定义请求包装器,它将读取请求的内容并缓存它们,以便可以多次读取它们.我不喜欢这个解决方案,因为我不想中断整个过滤器/请求/响应链(并可能引入性能或稳定性问题)只是为了实现日志记录,如果我有任何大的请求,如上传的文件(我这样做,我不想在内存中缓存它.此外,@RequestBody如果我只能找到它,Spring可能已经在某处缓存了.

顺便提一下,许多解决方案建议使用ContentCachingRequestWrapperSpring类,但根据我的经验,这不起作用.除了没有记录之外,查看其源代码看起来它只是缓存参数,而不是请求体.尝试从此类获取请求正文总是会产生一个空字符串.

所以我正在寻找我可能错过的任何其他选择.谢谢阅读.

spring servlets exception-handling spring-boot

25
推荐指数
3
解决办法
7172
查看次数

配置多个数据源后无法设置JPA命名策略(Spring 1.4.1/Hibernate 5.x)

我使用的是使用Hibernate 5.0.11的Spring Boot 1.4.1.最初我使用application.properties如下配置数据源:

spring.datasource.uncle.url=jdbc:jtds:sqlserver://hostname:port/db
spring.datasource.uncle.username=user
spring.datasource.uncle.password=password
spring.datasource.uncle.dialect=org.hibernate.dialect.SQLServer2012Dialect
spring.datasource.uncle.driverClassName=net.sourceforge.jtds.jdbc.Driver
Run Code Online (Sandbox Code Playgroud)

我用"叔叔"配置它,因为这将是我将配置的多个数据源之一的名称.我根据Spring文档配置了这样的数据源:

@Bean
@Primary
@ConfigurationProperties(prefix = "spring.datasource.uncle")
public DataSource uncleDataSource() {
    return DataSourceBuilder.create().build();
}
Run Code Online (Sandbox Code Playgroud)

在这一点上一切正常.

我创建了一个@Entity没有任何@Column注释的类,让Hibernate找出列名,例如,如果我有一个名为Java的属性idBank,Hibernate将自动假设列名为id_bank.这在生成ddl,运行SQL语句等时使用.我想利用此功能,因为我将拥有大量实体类,并且不希望创建和维护所有@Column注释.在这一点上,这很好.

然后我添加了另一个这样的数据源:

spring.datasource.aunt.url=jdbc:sybase:Tds:host2:port/db2
spring.datasource.aunt.username=user2
spring.datasource.aunt.password=password2
spring.datasource.aunt.dialect=org.hibernate.dialect.SybaseDialect
spring.datasource.aunt.driverClassName=com.sybase.jdbc4.jdbc.SybDriver
Run Code Online (Sandbox Code Playgroud)

......还有这个,遵循Spring文档设置多个数据源.显然,一旦定义了第二个数据源,它就无法配置默认bean,你必须定义自己的EntityManagerTransactionManager.所以除了上面配置的数据源之外,我还添加了以下配置:

@Bean
@Primary
PlatformTransactionManager uncleTransactionManager(@Qualifier("uncleEntityManagerFactory") final EntityManagerFactory factory) {
    return new JpaTransactionManager(factory);
}

@Bean
@Primary
LocalContainerEntityManagerFactoryBean uncleEntityManagerFactory(
        EntityManagerFactoryBuilder builder) {
    return builder
            .dataSource(uncleDataSource())
            .packages(Uncle.class)
            .persistenceUnit("uncle")
            .build();
}

@Bean
@ConfigurationProperties(prefix = "spring.datasource.aunt")
public DataSource auntDataSource() {
    return …
Run Code Online (Sandbox Code Playgroud)

spring hibernate spring-data-jpa spring-boot

23
推荐指数
3
解决办法
5396
查看次数

JPA/EclipseLink:EntityManager.getTransaction()是创建新事务还是返回活动事务?

我正在使用EclipseLink 2.3.0.我有一个方法,我从单元测试(因此在容器之外,没有JTA)调用,看起来像这样:

EntityManager em = /* get an entity manager */;
em.getTransaction().begin();
// make some changes
em.getTransaction().commit();
Run Code Online (Sandbox Code Playgroud)

这些更改没有被持久化到数据库,并且查看了很长时间,最后意识到EntityManager.getTransaction()实际上返回了一个新的EntityTransaction,而不是两个调用中的同一个.结果是第一次调用创建一个新事务并开始它,第二次调用创建另一个事务并提交它.由于第一个事务从未提交,因此不会保存更改.我们这样验证了这个:

log.info(em.getTransaction().toString());
log.info(em.getTransaction().toString());
Run Code Online (Sandbox Code Playgroud)

这导致了这些日志消息:

INFO: org.eclipse.persistence.internal.jpa.transaction.EntityTransactionImpl@1e34f445
INFO: org.eclipse.persistence.internal.jpa.transaction.EntityTransactionImpl@706a4d1a
Run Code Online (Sandbox Code Playgroud)

两个不同的对象ID验证有两个不同的实例.将代码更改为:

EntityManager em = /* get an entity manager */;
EntityTransaction tx = em.getTransaction();
tx.begin();
// make some changes
tx.commit();
Run Code Online (Sandbox Code Playgroud)

......纠正了这个问题.现在,当我运行代码时,我看到生成的SQL语句用于执行数据库工作,并查看数据库中的数据已更改.

我对这个结果感到有些惊讶,因为我在网上看到了很多代码示例(通常用于JPA,特别是用于EclipseLink),它们推荐了我们用于管理事务的代码.我搜索了很多关于此的信息,但没有找到任何东西.发生什么了?

我在JPA规范中查找了具体指定getTransaction()的功能,如果事务是新的或相同的话,它并不具体.persistence.xml中是否有一个设置来控制它?该行为是否特定于JPA规范的每个实现?

非常感谢任何信息或指导.

jpa transactions eclipselink

13
推荐指数
2
解决办法
8950
查看次数

使用bash进程替换和尾部的结果不正确?

使用bash进程替换,我想同时在文件上运行两个不同的命令.在这个例子中,没有必要,但想象"cat/usr/share/dict/words"是一个非常昂贵的操作,例如解压缩50gb文件.

cat /usr/share/dict/words | tee >(head -1 > h.txt) >(tail -1 > t.txt) > /dev/null
Run Code Online (Sandbox Code Playgroud)

在这个命令之后我希望h.txt包含单词文件"A"的第一行,并且t.txt包含文件"Zyzzogeton"的最后一行.

然而实际发生的是h.txt包含"A"但是t.txt包含"argillaceo",它大约是文件的5%.

为什么会这样?似乎要么"尾巴"进程提前终止,要么流正在混淆.

像这样运行另一个类似命令的行为符合预期:

cat /usr/share/dict/words | tee >(grep ^a > a.txt) >(grep ^z > z.txt) > /dev/null
Run Code Online (Sandbox Code Playgroud)

在这个命令之后,我希望a.txt包含所有以"a"开头的单词,而z.txt包含所有以"z"开头的单词,这正是发生的事情.

那么为什么这不适用于"尾巴",以及其他命令会不起作用?

bash pipe tail tee

13
推荐指数
1
解决办法
402
查看次数

如何使用内存Derby数据库在Hive(Scala)中进行测试

我在Scala 2.11中使用spark-hive 2.3.0,并设置了一个单元测试框架。spark-hive附带了TestHiveContextTestHiveSparkSession可以方便地从单元测试中调用Hive,而无需运行Hadoop,Spark或集群,这非常适合自动化测试。

Hive需要一个用于其metastore的数据库,以这种方式运行时,它会将Derby用作嵌入式数据库javax.jdo.option.ConnectionURL,默认配置为jdbc:derby:;databaseName=<file-path>;create=true。该<file-path>是在本地文件系统中的位置,是运行德比一个选项。

另一个选择是在内存中运行Derby,通常就像将此URL更改为一样容易jdbc:derby:memory:databaseName;create=true。但是,Hive无法做到这一点,因为配置是在内部HiveUtils类中进行的,并且不能被覆盖。我曾尝试在Spark Session Builder中对其进行更改,但后来HiveUtils创建我的时,所做的更改就被淹没了TestHiveContext

在我的情况下,最好使用内存数据库,因为我们的开发人员在Windows上运行(肯定不是我/我们的选择),并且在创建这些文件时,经常会出现诸如权限或文件名中的无效字符之类的问题(因为从未真正打算使用Hadoop)才能在Windows上使用),并且由于无法清理(由于这些问题),这些文件通常会被丢弃。我们希望测试是完全独立的,以便它们可以运行和完成而没有副作用,因此可以在多种环境(开发人员,CI,Jenkins,AWS等)中运行。

有趣的是,我在TestHive.scala

{ // set the metastore temporary configuration
  val metastoreTempConf = HiveUtils.newTemporaryConfiguration(useInMemoryDerby = false) ++ Map(
Run Code Online (Sandbox Code Playgroud)

因此,存在使用内存数据库的标志,但这是不可配置的,并且没有将其设置为的代码路径true

有什么方法可以配置或编写此文件,以便TestHiveDerby可以在内存中吗?尝试设置的值javax.jdo.option.ConnectionURL通过任蜂房的site.xml或HDFS-site.xml中不工作,我认为这是因为如何TestHiveTestHiveContextTestHiveSparkSession被初始化,他们有自己的代码路径从非测试路径分开。他们提供的功能对测试框架非常有帮助,但显然没有提供覆盖此值和其他设置的方法。

到目前为止,我能看到的最好的选择是重写或编写我自己的TestHiveContext类,该类从该类中借鉴了很多功能并覆盖了我需要的部分,但是对于我认为可以通过简单配置完成的工作来说,这是一个相对较大的工作更改。

unit-testing hive derby scalatest

9
推荐指数
1
解决办法
1052
查看次数

最小的 Spark 会话/配置以获得最佳的单元测试性能?

Spark 2.1.0 和 Scala 2.11。

我们的项目有数百个单元测试,它们执行相对简单的操作,例如创建 3 或 4 个对象的数据集并对它们执行简单的转换。其中许多测试需要长达 5-10 秒的时间才能运行,数百个测试加起来需要几分钟,并且正在成为我们 CI 构建的一个问题。操作非常简单,我想知道是否有我们可以使用的 Spark 配置来加快速度。

例如,简单地创建一个这样的数据集:

val histData = Seq(
  FooType(id = "id1", code = "code1",orgId = 1l),
  FooType(id = "id2", code = "code2",orgId = 1l),
  FooType(id = "id3", code = "code3",orgId = 1l)
).toDS()
Run Code Online (Sandbox Code Playgroud)

需要 800 毫秒(FooType 是一个案例类)。在创建 2 或 3 个这样的数据集和一些过滤器/映射/连接操作后(我真的不认为这些细节很重要,但如果你让我知道,我会发布它们),collect()需要 1000-2000 毫秒。将一些这样的操作加起来,测试可能需要 5-10 秒。

对于单元测试我们只关心测试的功能方面,我们不需要线程化、缩放、缓存、磁盘存储等。测试数据很小(通常小于1KB)并且是在内存中创建的(不从磁盘或任何外部源读取),并且对内存中的转换对象执行断言。我知道在幕后 Spark 可能会调用 DAGScheduler、代码生成器等,我想知道是否有办法在没有该功能的情况下执行作业。或者,如果确实必须这样做,请在单元测试套件开始时进行一次并在整个过程中使用它。

会话是用这样的东西创建的:

session = SparkSession.builder.config("spark.sql.shuffle.partitions","10").getOrCreate()
Run Code Online (Sandbox Code Playgroud)

并且在每个单元测试中使用相同的会话。我们直接从单元测试中调用 Spark API 方法,因此没有 Spark 提交或单独的进程或作业,它们都在 IDE 或调用单元测试的 gradle 创建的 JVM 中运行。 …

performance unit-testing apache-spark

7
推荐指数
1
解决办法
309
查看次数

如何在连接中使用Column.isin和数组列?

case class Foo1(codes:Seq[String], name:String)
case class Foo2(code:String, description:String)

val ds1 = Seq(
  Foo1(Seq("A"),           "foo1"),
  Foo1(Seq("A", "B"),      "foo2"),
  Foo1(Seq("B", "C", "D"), "foo3"),
  Foo1(Seq("C"),           "foo4"),
  Foo1(Seq("C", "D"),      "foo5")
).toDS

val ds2 = Seq(
  Foo2("A", "product A"),
  Foo2("B", "product B"),
  Foo2("C", "product C"),
  Foo2("D", "product D"),
  Foo2("E", "product E")
).toDS

val j = ds1.join(ds2, ds2("code") isin (ds1("codes")))
Run Code Online (Sandbox Code Playgroud)

希望这个Scala代码片段能够清楚地说明我要完成的任务,我们的数据结构化使得一个数据集具有包含值数组的列,并且我希望将该集合中的值连接到另一个数据集.因此,例如Seq("A", "B")ds1将联同"A""B"ds2.

Column上的"isin"运算符似乎正是我想要的,这构建并运行,但是当我运行它时,我收到以下错误:

org.apache.spark.sql.AnalysisException:由于数据类型不匹配,无法解析'(codeIN(codes))':参数必须是相同的类型;;

进一步阅读我看到isin()想要采取varargs("splatted args")并且似乎更适合a filter().所以我的问题是,这是这个运算符的预期用途,还是有其他方法来执行这种类型的连接?

scala apache-spark apache-spark-sql

5
推荐指数
1
解决办法
1625
查看次数

如何为 Scala 集合创建编码器(以实现自定义聚合器)?

Spark 2.3.0 与 Scala 2.11。我正在Aggregator根据此处的文档实施自定义。聚合器需要 3 种类型的输入、缓冲区和输出。

我的聚合器必须对窗口中所有以前的行进行操作,所以我这样声明:

case class Foo(...)

object MyAggregator extends Aggregator[Foo, ListBuffer[Foo], Boolean] {
    // other override methods
    override def bufferEncoder: Encoder[ListBuffer[Mod]] = ???
}
Run Code Online (Sandbox Code Playgroud)

覆盖方法之一应该返回缓冲区类型的编码器,在这种情况下是ListBuffer. 我找不到任何合适的编码器,org.apache.spark.sql.Encoders也找不到任何其他编码方式,所以我不知道该返回什么。

我想创建一个新的 case 类,它有一个 type 的单个属性ListBuffer[Foo]并将其用作我的缓冲区类,然后使用Encoders.product它,但我不确定是否有必要或者是否还有其他我遗漏的东西。感谢您提供任何提示。

scala apache-spark apache-spark-sql apache-spark-encoders

4
推荐指数
1
解决办法
1054
查看次数

Spark / Scala,数据集和案例类的多态性

我们将Spark 2.x与Scala一起用于具有13种不同ETL操作的系统。它们中的7个相对简单,每个都由单个域类驱动,并且主要区别在于此类以及如何处理负载的一些细微差别。

加载类的简化版本如下所示,在本示例中,要加载7个比萨饼配料,这里是Pepperoni:

object LoadPepperoni {
  def apply(inputFile: Dataset[Row],
            historicalData: Dataset[Pepperoni],
            mergeFun: (Pepperoni, PepperoniRaw) => Pepperoni): Dataset[Pepperoni] = {
    val sparkSession = SparkSession.builder().getOrCreate()
    import sparkSession.implicits._

    val rawData: Dataset[PepperoniRaw] = inputFile.rdd.map{ case row : Row =>
      PepperoniRaw(
          weight = row.getAs[String]("weight"),
          cost = row.getAs[String]("cost")
        )
    }.toDS()

    val validatedData: Dataset[PepperoniRaw] = ??? // validate the data

    val dedupedRawData: Dataset[PepperoniRaw] = ??? // deduplicate the data

    val dedupedData: Dataset[Pepperoni] = dedupedRawData.rdd.map{ case datum : PepperoniRaw =>
        Pepperoni( value = ???, key1 = ???, key2 …
Run Code Online (Sandbox Code Playgroud)

polymorphism design-patterns scala apache-spark

2
推荐指数
1
解决办法
954
查看次数