jpa 实体映射视图_JPA教程:实体映射-第2部分

jpa 实体映射视图

一篇文章中,我展示了一种持久保存实体的简单方法。 我解释了JPA用于确定实体默认表的默认方法。 假设我们要覆盖此默认名称。 我们之所以喜欢这样做,是因为数据模型是以前设计和修复的,并且表名与我们的类名不匹配(例如,我见过人们创建带有“ tbl_”前缀的表)。 那么我们应该如何覆盖默认表名称以匹配现有数据模型?

事实证明,这非常简单。 如果我们需要覆盖JPA假定的默认表名,则有两种方法可以做到:

  1. 我们可以使用@Entity批注的name属性来提供一个明确的实体名称,以与数据库表名称匹配。 对于我们的示例,如果表名是tbl_address,则可以在Address类中使用@Entity(name =“ tbl_address”)
  2. 我们可以在@Entity批注下面使用@Table (在javax.persistence包中定义)注解,并使用其name属性显式指定表名。
@Entity
@Table(name = "tbl_address")
public class Address {
  // Rest of the class
}

通过这两种方法,@ Table批注提供了更多用于自定义映射的选项。 例如,某些数据库(例如PostgreSQL)具有schemas概念,您可以使用schemas进一步对表进行分类/分组。 由于此功能,您可以在一个数据库中创建两个具有相同名称的表(尽管它们将属于两个不同的模式)。 要访问这些表,然后在查询中添加架构名称作为表前缀。 因此,如果PostgreSQL数据库有两个不同的模式,分别命名为public (这类似于PostgreSQL数据库的默认模式)和document ,并且这两个模式都包含名为document_collection的表,那么这两个查询都是完全有效的:

-- fetch from the table under public schema
SELECT *
FROM   public.document_collection;

-- fetch from the table under document schema
SELECT *
FROM   document.document_collection;

为了将实体映射到文档架构中的document_collection表,您将使用@Table注释,其架构属性设置为document

@Entity
@Table(name="document_collection", schema="document")
public class DocumentCollection {
  // rest of the class
}

当以这种方式指定时,就像我们在查询中所做的那样,当JPA进入数据库以访问表时,模式名称将作为表名称的前缀添加。

如果不是在@ Table批注中指定架构名称,而是将架构名称附加在表名称本身中,该怎么办?

@Entity
@Table(name = "document.document_collection")
public class DocumentCollection {
  // rest of the class
}

不能保证以这种方式将模式名称与表名称内联,因为在JPA规范(非标准)中未指定对此名称的支持。 因此,即使您的持久性提供者支持它,也最好不要养成这样做的习惯。

接下来,我们将注意力转移到各列。 为了确定默认列,JPA进行了类似于以下操作:

  1. 首先,它检查是否给出了任何显式的列映射信息。 如果找不到列映射信息,它将尝试猜测列的默认值。
  2. 为了确定默认值,JPA需要知道实体状态的访问类型,即读取/写入实体状态的方式。 在JPA中,两种不同的访问类型是可能的-字段和属性。 在我们的示例中,我们使用了字段访问(实际上,JPA从@Id批注的位置/位置假定了这一点,但稍后会对此进行更多介绍)。 如果您使用此访问类型,则将使用Reflection API从实体字段直接写入/读取状态。
  3. 知道访问类型后,JPA然后尝试确定列名称。 对于字段访问类型,JPA直接将字段名称视为列名称,这意味着如果实体具有名为status的字段,则它将映射到名为status的列。

至此,我们应该很清楚地址实体的状态如何保存到相应的列中。 Address实体的每个字段在数据库表tbl_address中都有一个对应的列,因此JPA将它们直接保存到其对应的列中。 id字段已保存到id列中, city字段已保存到city列中,依此类推。

好的,接下来让我们继续覆盖列名。 据我所知,只有一种方法(如果您碰巧知道任何其他方法,请注释!)可以使用@Column (在javax.persistence包中定义)覆盖实体状态的默认列名称。 )注释。 因此,如果将tbl_address表的id列重命名为address_id,那么我们可以将字段名称更改为address_id ,也可以使用@Column批注将其name属性设置为address_id

@Entity
@Table(name = "tbl_address")
public class Address {
  @Id
  @GeneratedValue
  @Column(name = "address_id")
  private Integer id;

  // Rest of the class
}

您可以看到,对于以上所有情况,JPA使用的默认方法都非常明智,大多数情况下您都会对此感到满意。 但是,更改默认值也很容易,并且可以很快完成。

如果我们在地址实体中有一个不希望保存在数据库中的字段怎么办? 假设Address实体有一个名为transientColumn的列,该列在数据库表中没有任何对应的默认列:

@Entity
@Table(name = "tbl_address")
public class Address {
  @Id
  @GeneratedValue
  @Column(name = "address_id")
  private Integer id;

  private String street;
  private String city;
  private String province;
  private String country;
  private String postcode;
  private String transientColumn;

  // Rest of the class
}

如果使用上述更改来编译代码,则将出现如下所示的异常:

Exception in thread “main” java.lang.ExceptionInInitializerError
at com.keertimaan.javasamples.jpaexample.Main.main(Main.java:33)
Caused by: javax.persistence.PersistenceException: Unable to build entity manager factory
at org.hibernate.jpa.HibernatePersistenceProvider.createEntityManagerFactory(HibernatePersistenceProvider.java:83)
at org.hibernate.ejb.HibernatePersistence.createEntityManagerFactory(HibernatePersistence.java:54)
at javax.persistence.Persistence.createEntityManagerFactory(Persistence.java:55)
at javax.persistence.Persistence.createEntityManagerFactory(Persistence.java:39)
at com.keertimaan.javasamples.jpaexample.persistenceutil.PersistenceManager.<init>(PersistenceManager.java:31)
at com.keertimaan.javasamples.jpaexample.persistenceutil.PersistenceManager.<clinit>(PersistenceManager.java:26)
… 1 more
Caused by: org.hibernate.HibernateException: Missing column: transientColumn in jpa_example.tbl_address
at org.hibernate.mapping.Table.validateColumns(Table.java:365)
at org.hibernate.cfg.Configuration.validateSchema(Configuration.java:1336)
at org.hibernate.tool.hbm2ddl.SchemaValidator.validate(SchemaValidator.java:155)
at org.hibernate.internal.SessionFactoryImpl.<init>(SessionFactoryImpl.java:525)
at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:1857)
at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl$4.perform(EntityManagerFactoryBuilderImpl.java:850)
at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl$4.perform(EntityManagerFactoryBuilderImpl.java:843)
at org.hibernate.boot.registry.classloading.internal.ClassLoaderServiceImpl.withTccl(ClassLoaderServiceImpl.java:398)
at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.build(EntityManagerFactoryBuilderImpl.java:842)
at org.hibernate.jpa.HibernatePersistenceProvider.createEntityManagerFactory(HibernatePersistenceProvider.java:75)
… 6 more

例外是,持久性提供程序无法在数据库中找到任何名称为transientColumn的列 ,并且我们没有采取任何措施使持久性提供程序明确表示我们不希望将此字段保存在数据库中。 持久性提供程序将其用作实体中映射到数据库列的任何其他字段。

为了解决此问题,我们可以执行以下任一操作:

  1. 我们可以使用@Transient (在javax.persistence包中定义)注解来对transientColumn字段进行注解,以使持久性提供程序知道我们不希望保存该字段,并且该表中没有任何对应的列。
  2. 我们可以使用Java默认具有的transient关键字。

我想到的这两种方法之间的区别在于,如果我们使用transient关键字而不是annotation,则如果Address对象之一从一个JVM序列化到另一个JVM,那么transitionColumn字段将再次被重新初始化(就像Java中的其他任何临时字段)。 对于注释,这不会发生,并且transientColumn字段将在序列化过程中保留其值。 根据经验,如果我不需要担心序列化(在大多数情况下不需要),我总是使用批注。

因此,使用注释,我们可以立即解决问题:

@Entity
@Table(name = "tbl_address")
public class Address {
  @Id
  @GeneratedValue
  @Column(name = "address_id")
  private Integer id;

  private String street;
  private String city;
  private String province;
  private String country;
  private String postcode;

  @Transient
  private String transientColumn;

  // Rest of the class
}

今天的人们就这样。 如果发现任何错误/有任何意见,请随时发表评论!

直到下一次。

翻译自: https://www.javacodegeeks.com/2014/10/jpa-tutorial-mapping-entities-part-2.html

jpa 实体映射视图

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值