【发布时间】:2015-07-20 09:05:10
【问题描述】:
正如标题所示,我正在为我的数据访问层使用 Spring Boot 和 Hibernate 构建一个 Web 应用程序,并且开发是在 InteliJ IDEA 14.1.2 上完成的。
我的知识
这是我第一次使用 Spring Boot、Hibernate 和 InteliJ。我已经构建了一些小应用程序来测试 Spring Boot 和 Hibernate,但是这些应用程序与我现在正在构建的应用程序之间的复杂性差异要大一些。
环境
关于我的环境,以防万一,我在托管 Redis 3.0.1 服务器的 VirtualBox 4.3.26 VM 上运行 Windows 7 SP1 64 位、MySQL 服务器 5.6.17、InteliJ 14.1.2 和 Ubuntu Server 14.04。
目的
此时使用上述技术的目的是将不同实体的存储和检索到一个 MySQL 数据库(Redis 仅用于会话外部化和应用程序实例之间的共享)。换句话说,我正在构建我的数据访问层。
数据库
我的完整数据库架构可以在这里找到:
https://dl.dropboxusercontent.com/u/49544122/so/DB.pdf
来源
我的 Spring Boot 应用如下:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.session.data.redis.config.annotation.web.http.EnableRedisHttpSession;
import se.domain.cvs.abstraction.dataaccess.AccountRepository;
import se.domain.cvs.domain.AccountEntity;
@SpringBootApplication
@EnableRedisHttpSession
public class Application implements CommandLineRunner {
@Autowired
AccountRepository repository;
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
@Override
public void run(String... strings) throws Exception {
System.out.println("=======================================================");
AccountEntity account = repository.findByEmail("r.franklin@companya.se");
System.out.println("My name is " + account.getFirstName() + " " + account.getLastName());
System.out.println("=======================================================");
}
}
我使用 CommandLineRunner 接口只是为了测试裸数据访问层,还没有引入 REST 端点。
我的 YAML 格式的配置如下:
...
# MySQL Database Configuration
spring.datasource:
url: jdbc:mysql://localhost:3306/cvs
username: cvs
password: cvs
driverClassName: com.mysql.jdbc.Driver
spring.jpa:
database: MYSQL
show-sql: true
hibernate.ddl-auto: validate
hibernate.naming-strategy: org.hibernate.cfg.DefaultNamingStrategy
properties.hibernate.dialect: org.hibernate.dialect.MySQL5Dialect
...
JPA 实体是使用 InteliJ 自动生成的,这就是问题所在。让我们以下面的 OrderEntity 为例(为简洁起见,我省略了一些代码):
...
@Entity
@Table(name = "order", schema = "", catalog = "cvs")
public class OrderEntity {
...
private int invoiceId;
...
private InvoiceEntity invoiceByInvoiceId;
...
@Basic
@Column(name = "InvoiceID", nullable = false, insertable = false, updatable = false)
public int getInvoiceId() {
return invoiceId;
}
public void setInvoiceId(int invoiceId) {
this.invoiceId = invoiceId;
}
...
@ManyToOne
@JoinColumn(name = "InvoiceID", referencedColumnName = "InvoiceID", nullable = false)
public InvoiceEntity getInvoiceByInvoiceId() {
return invoiceByInvoiceId;
}
public void setInvoiceByInvoiceId(InvoiceEntity invoiceByInvoiceId) {
this.invoiceByInvoiceId = invoiceByInvoiceId;
}
...
}
尝试运行 Spring Boot 应用程序时出现以下错误:
org.hibernate.MappingException: Repeated column in mapping for entity: OrderEntity column: invoiceId (should be mapped with insert="false" update="false")
经过一番研究,我猜问题在于 invoiceID 现在有两种设置方式,一种是通过 setInvoiceID() 设置器,另一种是通过 OrderEntity 关联的 InvoiceEntity 对象本身,即可能导致不一致的状态。正如这里的另一位用户所说,
当创建/更新相关实体的责任不在当前实体中时,您会这样做。
在此处查看相关帖子:Please explain about: insertable=false, updatable=false
将相应字段(insertable 和 updateable)的建议值设置为 false 可修复错误。
我的问题是为什么会以错误的方式生成?我的更改修复了错误,但我想确保我的 SQL 中没有导致 InteliJ 以错误方式生成此错误的错误。完整的 SQL 脚本可以在这里找到http://pastebin.com/aDguqR1N。
此外,在生成实体时,InteliJ 需要一个 Hibernate 配置文件,我猜 Spring Boot 会在其他地方自行生成(或使用基于 Java 的配置)。无论我将它留在那里还是删除它,它似乎都不会影响应用程序。我猜 SB 读取属性的顺序会覆盖它。我可以直接删除吗?
非常感谢您的宝贵时间和提前提供的帮助,对于这篇冗长的帖子深表歉意! :)
【问题讨论】:
-
TL;博士。尝试专注于一个 ONE 问题。发布 relevant 代码,以及 Hibernate 生成的 SQL 查询。您永远不应拥有包含另一个实体 ID 的实体。您应该改为使用 ManyToOne 或 OneToOne 关联。
-
您好!感谢您的评论!在我看来,确实根本不需要 ID 属性,但我想知道的是为什么它们首先生成,以防我的 SQL 有问题。我还按照您的建议删除了帖子的第二部分。再次感谢! :)
-
我不知道。我永远不会使用工具来自动生成我的实体。它们是应用程序的核心和灵魂,我希望掌控一切。
标签: mysql spring hibernate intellij-idea spring-boot