我使用的是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类,但根据我的经验,这不起作用.除了没有记录之外,查看其源代码看起来它只是缓存参数,而不是请求体.尝试从此类获取请求正文总是会产生一个空字符串.
所以我正在寻找我可能错过的任何其他选择.谢谢阅读.
我使用的是使用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,你必须定义自己的EntityManager和TransactionManager.所以除了上面配置的数据源之外,我还添加了以下配置:
@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) 我正在使用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规范的每个实现?
非常感谢任何信息或指导.
使用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"开头的单词,这正是发生的事情.
那么为什么这不适用于"尾巴",以及其他命令会不起作用?
我在Scala 2.11中使用spark-hive 2.3.0,并设置了一个单元测试框架。spark-hive附带了TestHiveContext,TestHiveSparkSession可以方便地从单元测试中调用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中不工作,我认为这是因为如何TestHive,TestHiveContext和TestHiveSparkSession被初始化,他们有自己的代码路径从非测试路径分开。他们提供的功能对测试框架非常有帮助,但显然没有提供覆盖此值和其他设置的方法。
到目前为止,我能看到的最好的选择是重写或编写我自己的TestHiveContext类,该类从该类中借鉴了很多功能并覆盖了我需要的部分,但是对于我认为可以通过简单配置完成的工作来说,这是一个相对较大的工作更改。
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 中运行。 …
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().所以我的问题是,这是这个运算符的预期用途,还是有其他方法来执行这种类型的连接?
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它,但我不确定是否有必要或者是否还有其他我遗漏的东西。感谢您提供任何提示。
我们将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) apache-spark ×4
scala ×3
spring ×2
spring-boot ×2
unit-testing ×2
bash ×1
derby ×1
eclipselink ×1
hibernate ×1
hive ×1
jpa ×1
performance ×1
pipe ×1
polymorphism ×1
scalatest ×1
servlets ×1
tail ×1
tee ×1
transactions ×1