最近在用 Scala + SpringMVC + CXF + Hibernate(或 Squeryl)做 Restful Web Service。
Scala也出来十几年了,但是普及度还是很不够,所以Spring对Scala的特性支持,还好,Scala本来就可以和Java混合编程,所以只要些许地方做出改动,也可以跑的很欢畅。。。所以写一篇博客,把踩过的坑都写下来:
1> Jackson格式化
因为Scala case class / 枚举类 等特殊类型的存在,jackson 默认 几乎不支持scala,可以在pom中加入以下依赖:
<dependency> <groupId>com.fasterxml.jackson.module</groupId> <artifactId>jackson-module-scala_2.11</artifactId> <version>2.8.8</version> </dependency>
然后 自定义一个JacksonJsonProvider
@Provider @Consumes(Array("*/*")) @Produces(Array("*/*")) class ScalaJacksonJsonProvider(mapper: ObjectMapper, annotationsToUse: Array[Annotations]) extends JacksonJsonProvider(mapper, annotationsToUse) { mapper.registerModule(DefaultScalaModule) mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false) mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL) def this() = this(new ObjectMapper with ScalaObjectMapper, JacksonJsonProvider.BASIC_ANNOTATIONS) def this(annotationsToUse: Array[Annotations]) = this(new ObjectMapper with ScalaObjectMapper, annotationsToUse) def this(mapper: ObjectMapper) = this(mapper, JacksonJsonProvider.BASIC_ANNOTATIONS) }
以上代码中的DefaultScalaModule 就是 刚才添加的jackson-module-scala_2.11中的
以上代码的主要作用就是 注册 DefaultScalaModule, 这样就可以让jackson支持Scala的类型
枚举类型的JSON转化
Scala中的 枚举类型 和 Java中 完全不一样,格式化出来的内容 和 Java枚举类型格式化得出的 完全不一样,包含了很多 属性。
为了支持对枚举类型的格式化,首先需要定义一个TypeReference
/** * Sim卡状态<br/> * 1: 开户 * 2:销户 */ object Status extends Enumeration { type Status = Value val Enable = Value(1, "01") val Disable = Value(2, "02") } /** * JSON 格式化 Scala Enumeration时,@JsonScalaEnumeration 的参数 */ class StatusType extends TypeReference[Status.type]
如上所示:StatusType就是 辅助类
然后在定义枚举属性时,加上@JsonScalaEnumeration注解即可
@JsonScalaEnumeration(classOf[StatusType]) var status: Status
这样,Jackson格式化后的字符串,还是有限制,是Status的 name 属性,也就是 “启用” / “禁用”
2> Hibernate Entity适配
Hibernate几乎不用做任何修改就可以支持Scala,和 使用Java Entity一样,
@Entity @Table(name = "tb_sim_info") class SimInfo extends Serializable { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "sim_id") var id: java.lang.Long = _ var msisdn: String = _ var imsi: String = _ var iccid: String = _ @Temporal(TemporalType.TIMESTAMP) @Column(name = "issue_date_time") var issueDate: Date = _ @Type(`type` = "com.cmdt.common.mno.service.sh.enums.StatusUserType") @JsonScalaEnumeration(classOf[StatusType]) var status: Status = _ @OneToOne(mappedBy="sim",fetch=FetchType.EAGER) var detail: SimDetailInfo = _ } object SimInfo { def apply(msisdn: String, imsi: String, iccid: String, issueDate: Date = null): SimInfo = { val sim = new SimInfo() sim.msisdn = msisdn sim.imsi = imsi sim.iccid = iccid sim.issueDate = issueDate sim.status = Status.Enable sim } }
如图所示,只有在涉及到枚举类 或 Option时,会有问题。
Option是因为JVM编译时会进行类型擦除,所以我在Entity里面也没有使用。
枚举类型, 用到了Hibernate的高级特性UserType,相当于 自定义控制枚举类型的Hibernate序列化及反序列化
/** * Hibernate PO 和 数据库表映射时的 * Scala Enumeration 序列化/反序列化 UserType */ class StatusUserType extends EnumerationAbstractUserType(Status) import org.hibernate.usertype.UserType import java.io.Serializable import java.sql.{Types, ResultSet, PreparedStatement} import org.hibernate.engine.spi.SessionImplementor /** Base class for persisting Enumerations as varchar, or enum in mysql */ private[enums] abstract class EnumerationAbstractUserType(val et: Enumeration) extends UserType { override def sqlTypes() = Array(Types.VARCHAR) override def returnedClass = classOf[et.Value] override def equals(x: Object, y: Object): Boolean = x == y override def hashCode(x: Object) = x.hashCode() override def nullSafeGet(resultSet: ResultSet, names: Array[String], session: SessionImplementor, owner: Object): Object = { val value = resultSet.getString(names(0)) if (resultSet.wasNull()) return null else { return et.withName(value) } } override def nullSafeSet(statement: PreparedStatement, value: Object, index: Int, session: SessionImplementor): Unit = { if (value == null) { statement.setNull(index, Types.VARCHAR) } else { val en = value.toString statement.setString(index, en) } } override def deepCopy(value: Object): Object = value override def isMutable() = false override def disassemble(value: Object) = value.asInstanceOf[Serializable] override def assemble(cached: Serializable, owner: Object): Object = cached.asInstanceOf[Object] override def replace(original: Object, target: Object, owner: Object) = original } /** Base class for persisting Enumerations as integers */ abstract class EnumerationAbstractIntUserType(val et: Enumeration) extends UserType { override def sqlTypes() = Array(Types.INTEGER) override def returnedClass = classOf[et.Value] override def equals(x: Object, y: Object): Boolean = x == y override def hashCode(x: Object) = x.hashCode() override def nullSafeGet(resultSet: ResultSet, names: Array[String], session: SessionImplementor, owner: Object): Object = { val value = resultSet.getInt(names(0)) if (resultSet.wasNull()) return null else { return et(value) } } override def nullSafeSet(statement: PreparedStatement, value: Object, index: Int, session: SessionImplementor): Unit = { if (value == null) { statement.setNull(index, Types.INTEGER) } else { val en = value.asInstanceOf[et.Value] statement.setInt(index, en.id) } } override def deepCopy(value: Object): Object = value override def isMutable() = false override def disassemble(value: Object) = value.asInstanceOf[Serializable] override def assemble(cached: Serializable, owner: Object): Object = cached.asInstanceOf[Object] override def replace(original: Object, target: Object, owner: Object) = original }
Option属性,也可以写一个类似的 UserType,太麻烦了,我就没有使用。。。
经过以上适配后,Hibernate也可以正常支持Scala了,其他使用 和 Java 没有什么不同。
3> Squeryl ORM
相比Hibernate,我觉得 Squeryl ORM 更适合Scala,毕竟这是原生的Scala ORM框架,使用起来 更简洁,而且基本上是以Scala的方式进行数据库映射,有兴趣的可以去百度,我就不介绍具体用法了。
Squeryl比较坑的一个地方是,需要自己维护Session,为了提高数据库的并发性,所以可以考虑用数据库连接池来维护Session,具体如下所示:
@Service class MnoDB extends Logging{ @Resource val cpds:ComboPooledDataSource = null @PostConstruct def configureDb() { SessionFactory.concreteFactory = Some(() => connection) def connection = { logInfo("Creating connection with c3po connection pool") Session.create(cpds.getConnection, new MySQLInnoDBAdapter) } } @PreDestroy def close(): Unit = { cpds.close() } }
Session管理解决后,还有一个 比较坑的地方是,Squeryl 默认把 java.lang.Date序列为2017-06-27 00:00:00这种格式,直接把Time擦除了,如果想格式化为 日期时间,则必须使用java.sql.Timestamp。
要解决这个比较坑,
>要么下源码,修改 org.squeryl.internals.FieldMapper,将initialize()方法中的 register(dateTEF) 注释掉;
>也可以在工程中自定义一个 org.squeryl.internals.FieldMapper,同样的将register(dateTEF) 注释掉,但是自定义的FieldMapper的package必须为:org.squeryl.internals
然后 还需要自定义一个 Date 序列化/反序列化的类型定义
implicit val jodaTimeTEF = new NonPrimitiveJdbcMapper[Timestamp, Date, TTimestamp](timestampTEF, this) { /** * Here we implement functions fo convert to and from the native JDBC type */ def convertFromJdbc(t: Timestamp) = new Date(t.getTime) def convertToJdbc(t: Date) = new Timestamp(t.getTime()) } /** * We define this one here to allow working with Option of our new type, this allso * allows the 'nvl' function to work */ implicit val optionJodaTimeTEF = new TypedExpressionFactory[Option[Date], TOptionTimestamp] with DeOptionizer[Timestamp, Date, TTimestamp, Option[Date], TOptionTimestamp] { val deOptionizer = jodaTimeTEF } /** * the following are necessary for the AST lifting */ implicit def jodaTimeToTE(s: Date) = jodaTimeTEF.create(s) implicit def optionJodaTimeToTE(s: Option[Date]) = optionJodaTimeTEF.create(s)
Squeryl感觉用起来很爽,但是 还是有很多坑,以下两个是我在Entity定义时碰到的
1>如果Entity中有 枚举类型属性,则 必须定义def this() 构造方法,并且在构造方法中,需要对枚举类型给出一个默认值进行实例化,
@JsonIgnoreProperties(Array("detail")) class SimInfo(@Column(name = "sim_id") val id: Long, val msisdn: String, val imsi: String, val iccid: String, @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") @Column(name = "issue_date_time") val issueDate: Date, @JsonScalaEnumeration(classOf[StatusType]) @OptionType(classOf[Status]) @JsonProperty("sim_status") var status: Option[Status] ) { def this() = this(0, "", "", "", null, Some(Status.Enable)) lazy val detail: OneToMany[SimDetailInfo] = MnoDB.simToDetails.left(this) }
2>如果Entit 包含 Option属性,则必须在该属性上加@OptionType注释,告诉SquerylOption的实际类型,如上述代码中的 status属性
总体而言,Scala编写代码,可以使用函数编程的特性,写的代码更简洁易懂,而且基本上 可以完美继承Java的第三方插件,以后尽量用Scala来写后台服务,就是太小众了,碰到一点问题,必须Google,欢迎使用Scala的朋友 来交流
我的QQ: 174492779
相关推荐
##Gradle+Scala+Java+SpringMVC Web应用解决方案 ###框架组成(Project Architecture) Gradle > > finished > 2016-10-23 Gretty Integration> Server Hot Deploy Scheme> > finished > 2016-11-05 Spring 4.25 ...
Scala是一种强大的多范式编程语言,它结合了面向对象和函数式编程的特性。MyBatis则是一款流行的Java持久层框架,主要用于简化数据库操作。在本项目中,"scala + mybatis 数据库查询",我们将探讨如何将Scala与...
2. **Scala**: Scala是一种多范式的编程语言,结合了面向对象和函数式编程的特点。它运行在Java虚拟机(JVM)上,可以无缝集成Java库,因此非常适合构建复杂、高性能的分布式系统。在大数据领域,Scala由于其强大的...
它提供了一个统一的API来访问各种持久化技术,如Hibernate、EclipseLink等。通过SpringData JPA,你可以利用注解驱动的方法来执行CRUD操作,减少了大量编写DAO层代码的工作。此外,它还支持动态查询,使得在不编写...
Scala+maven安装方法 Scala 是一种基于 Java 平台的编程语言,具有很高的扩展性和灵活性。为了方便开发者快速搭建 Scala 环境,本文将介绍 Scala+maven 的安装方法,并对 Scala IDE 的下载和配置进行详细的讲解。 ...
基于Spark+Scala+MongoDB的大数据实战,商品推荐系统设计与实现.zip 1、该资源内项目代码经过严格调试,下载即用确保可以运行! 2、该资源适合计算机相关专业(如计科、人工智能、大数据、数学、电子信息等)正在做...
基于java+scala+Python+spark实现的图书推荐系统的设计与实现+详细文档+全部资料(高分毕业设计).zip基于java+scala+Python+spark实现的图书推荐系统的设计与实现+详细文档+全部资料(高分毕业设计).zip ...
hadoop+scala+spakr配置,步骤详细,值得收藏。
基于Scala+Flink实现实时冰蝎(Behinder)流量检测源码+部署文档+全部资料齐全 高分项目.zip基于Scala+Flink实现实时冰蝎(Behinder)流量检测源码+部署文档+全部资料齐全 高分项目.zip 【备注】 1、该项目是个人高分...
在本项目中,"springboot+scala+react"的组合为我们提供了一个现代Web应用程序的开发框架,结合了Spring Boot的强大后端能力、Scala的高效编程语法以及React的前端交互优势。下面将详细介绍这三个关键技术及其在项目...
:warning:Scala + Spring + Hibernate + Maven + Selenium WebDriver 该项目包含使用Scala,Spring,Hibernate和Maven的最新版本启动Webapp项目的源代码,包括基于Selenium WebDriver PageObject的Web测试。...
基于Scala+Hadoop+Spark的大数据金融信贷风险控系统源码+详细文档+全部数据资料 高分项目.zip 【备注】 1、该项目是个人高分项目源码,已获导师指导认可通过,答辩评审分达到95分 2、该资源内项目代码都经过测试运行...
### sbt + Scala + IDEA 安装配置及创建导入 sbt 项目的详细步骤 #### 一、环境搭建 本文档将详细介绍如何在 Windows 10 和 JDK 1.8 的环境下,搭建完整的 Scala + sbt + IntelliJ IDEA 开发环境。这对于初学者来...
基于Scala+Spark+Hive的NBA历史球员价值挖掘可视化系统源码+详细文档+全部数据资料 高分项目.zip 【备注】 1、该项目是个人高分项目源码,已获导师指导认可通过,答辩评审分达到95分 2、该资源内项目代码都经过测试...
基于Scala+Spark+PageRank算法构建仿微博用户好友的分布式推荐系统源码+详细文档+全部数据资料 高分项目.zip 【备注】 1、该项目是个人高分项目源码,已获导师指导认可通过,答辩评审分达到95分 2、该资源内项目代码...
基于Java+Scala+Spark的图书推荐系统源码+详细文档+全部数据资料 高分项目.zip 【备注】 1、该项目是个人高分项目源码,已获导师指导认可通过,答辩评审分达到95分 2、该资源内项目代码都经过测试运行成功,功能ok的...
【play2+scala+mongodb demo示例】是一个用于学习如何集成和使用Play Framework 2、Scala编程语言以及MongoDB数据库的实战项目。这个项目旨在帮助初学者理解这三者之间的协同工作,通过创建一个基本的CRUD(创建、...
基于Scala+Spark的交通分析系统源码+详细文档+全部数据资料 高分项目.zip 【备注】 1、该项目是个人高分项目源码,已获导师指导认可通过,答辩评审分达到95分 2、该资源内项目代码都经过测试运行成功,功能ok的情况...
基于Scala+Spark的外卖数据分析和推荐源码+详细文档+全部数据资料 高分项目.zip 【备注】 1、该项目是个人高分项目源码,已获导师指导认可通过,答辩评审分达到95分 2、该资源内项目代码都经过测试运行成功,功能ok...