程序员的量化交易之路(15)--Cointrader之EntityBase类(3)

本文详细介绍了实体类设计中的基类与继承策略,包括单表继承、Joined策略、Table_PER_Class策略,并通过示例代码展示了如何实现。同时解释了@MappedSuperclass注解的作用及其实现方式。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

转载需要说明出处:http://blog.youkuaiyun.com/minimicall?viewmode=contentshttp://cloudtrader.top/

1. 类代码

EntityBase是其他实体类的基类。在这里我们先将代码贴出。需要说明的是,有些实体类是需要映射到数据库的。需要JPA基础,或者Hibernate基础。

这个类就是包含一个Id,唯一标示、还有就是一个版本号。利于升级。

@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
@MappedSuperclass
public abstract class EntityBase implements Serializable {


    /**
     * 
     */
    private static final long serialVersionUID = -7893439827939854533L;


    @Id
    @Column(columnDefinition = "BINARY(16)", length = 16, updatable = false, nullable = false)
    public UUID getId() {
        ensureId();
        return id;
    }


    @Version
    private Long version;


    public Long getVersion() {
        return version;
    }


    public void setVersion(Long version) {
        this.version = version;
    }


    @Override
    public boolean equals(Object o) {
        // generated by IDEA
        if (this == o)
            return true;
        if (!(o instanceof EntityBase))
            return false;
        EntityBase that = (EntityBase) o;
        return id.equals(that.id);
    }


    @Override
    public int hashCode() {
        ensureId();
        return id.hashCode();
    }


    // JPA
    protected EntityBase() {
    }


    protected void setId(UUID id) {
        this.id = id;
    }


    private void ensureId() {
        if (id == null)
            id = UUID.randomUUID();
    }


    private UUID id;


}

2. @Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)注解说明

    在JPA中,实体继承关系的映射策略共有三种:单表继承策略(table per class)、Joined策略(table per subclass)和Table_PER_Class策略。

下面我原本抄一篇别人的帖子。出处:http://zhuchengzzcc.iteye.com/blog/1679496

1.单表继承策略

   

    单表继承策略,父类实体和子类实体共用一张数据库表,在表中通过一列辨别字段来区别不同类别的实体。具体做法如下:

a.在父类实体的@Entity注解下添加如下的注解:

@Inheritance(Strategy=InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name=”辨别字段列名”)
@DiscriminatorValue(父类实体辨别字段列值)

 

b.在子类实体的@Entity注解下添加如下的注解:

@DiscriminatorValue(子类实体辨别字段列值) 

 

 

定义了一个父类

 

Java代码   收藏代码
  1. @Entity  
  2. @Inheritance(strategy = InheritanceType.SINGLE_TABLE)  
  3. @Table(name = "WINDOW_FILE")  
  4. @DiscriminatorColumn(name = "DISCRIMINATOR", discriminatorType = DiscriminatorType.STRING, length = 30)  
  5. @DiscriminatorValue("WindowFile")  
  6. public class WindowFile {  
  7.   
  8.     @Id  
  9.     @GeneratedValue(strategy = GenerationType.AUTO)  
  10.     private Integer id;  
  11.   
  12.     @Basic  
  13.     @Column(name = "NAME")  
  14.     private String name;  
  15.   
  16.     @Basic  
  17.     @Column(name = "TYPE")  
  18.     private String type;  
  19.   
  20.     @Basic  
  21.     @Column(name = "DATE")  
  22.     private Date date;  
  23.     //省略get set  
  24. }  

 

后定义2个子类

 

Java代码   收藏代码
  1. @Entity  
  2. @DiscriminatorValue("Folder")  
  3. public class Folder extends WindowFile {  
  4.   
  5.     @Basic  
  6.     @Column(name = "FILE_COUNT")  
  7.     private Integer fileCount;  
  8.     //省略get set  
  9. }  

 

Java代码   收藏代码
  1. @Entity  
  2. @DiscriminatorValue("Document")  
  3. public class Document extends WindowFile {  
  4.   
  5.     @Basic  
  6.     @Column(name = "SIZE")  
  7.     private String size;  
  8.     //省略get set  
  9. }  

 

 以上通过列DISCRIMINATOR的不同,区分具体父子实体。

 

实际表结构如下:

WINDOW_FILE  DISCRIMINATOR,ID,NAME,DATE,TYPE,SIZE,FILE_COUNT

 

当你使用WindowFile实体时,实际表的字段为DISCRIMINATOR='WindowFile',SIZE与FILE_COUNT永远是空

当使用Folder实体时,DISCRIMINATOR='Folder',SIZE永远是空,FILE_COUNT为实际值。

Document同理,与Folder类似。

 

2.Joined策略

 

    父类实体和子类实体分别对应数据库中不同的表,子类实体的表中只存在其扩展的特殊属性,父类的公共属性保存在父类实体映射表中。具体做法:

 

 

 

@Inheritance(Strategy=InheritanceType.JOINED)

 

子类实体不需要特殊说明。

 

 

Java代码   收藏代码
  1. @Entity  
  2. @Table(name = "T_ANIMAL")  
  3. @Inheritance(strategy = InheritanceType.JOINED)  
  4. public class Animal {  
  5.   
  6.     @Id  
  7.     @Column(name = "ID")  
  8.     @GeneratedValue(strategy = GenerationType.AUTO)  
  9.     private Integer id;  
  10.   
  11.     @Column(name = "NAME")  
  12.     private String name;  
  13.   
  14.     @Column(name = "COLOR")  
  15.     private String color;  
  16.     //省略get set  
  17. }  

 

Java代码   收藏代码
  1. @Entity  
  2. @Table(name = "T_BIRD")  
  3. @PrimaryKeyJoinColumn(name = "BIRD_ID")  
  4. public class Bird extends Animal {  
  5.   
  6.     @Column(name = "SPEED")  
  7.     private String speed;  
  8.     //省略get set  
  9. }  

 

Java代码   收藏代码
  1. @Entity  
  2. @Table(name = "T_DOG")  
  3. @PrimaryKeyJoinColumn(name = "DOG_ID")  
  4. public class Dog extends Animal {  
  5.   
  6.     @Column(name = "LEGS")  
  7.     private Integer legs;  
  8.     //省略get set  
  9. }  

 

 

实际表结构如下:

T_ANIMAL  ID,COLOR,NAME

T_BIRD  SPEED,BIRD(既是外键,也是主键)

T_DOG  LEGS,DOG_ID(既是外键,也是主键)

 

3.Table_PER_Class策略:

 

Table_PER_Class策略,父类实体和子类实体每个类分别对应一张数据库中的表,子类表中保存所有属性,包括从父类实体中继承的属性。具体做法:

只需在父类实体的@Entity注解下添加如下注解:

@Inheritance(Strategy=InheritanceType.TABLE_PER_CLASS)

 

Java代码   收藏代码
  1. @Entity  
  2. @Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)  
  3. @Table(name = "T_VEHICLE")  
  4. public class Vehicle { // 基类  
  5.   
  6.     @Id  
  7.     // @GeneratedValue  
  8.     @Column(name = "ID")  
  9.     private Integer id;  
  10.   
  11.     @Column(name = "SPEED")  
  12.     private Integer speed;// 速度  
  13.     //省略get set  
  14. }  

 

Java代码   收藏代码
  1. @Entity  
  2. @Table(name = "T_CAR")  
  3. public class Car extends Vehicle {  
  4.   
  5.     @Column(name = "ENGINE")  
  6.     private String engine;// 发动机  
  7.     //省略get set  
  8. }  

 

一旦使用这种策略就意味着你不能使用AUTO generator 和IDENTITY generator,即主键值不能采用数据库自动生成。

 

实际表结构如下:

T_VEHICLE  ID,SPEED

T_CAR  ID,SPEED,ENGINE

 

3. @MappedSuperClass注解

@MappedSuperclass注解是为了在继承中,防止基类生成表格。在基类加上该标注,而不是@Entity,就可以只生成子类表,而子类表又含有基类的那些属性(列)。

一样为了更好的理解这东西,我抄别人的啦:http://blog.163.com/zzf_fly/blog/static/209589158201362643027706/,别人已经写的很好的,我就不想写。贴在这里主要是为了连贯性:

实体类baseEntity.java

package com.rock.cft.hibernate;

import java.util.Date;

import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.MappedSuperclass;

//@MappedSuperclass 用在父类上面。当这个类肯定是父类时,加此标注。如果改成@Entity,则继承后,多个类继承,只会生成一个表,而不是多个继承,生成多个表 @MappedSuperclass public abstract class BaseEntity {   private Integer id;// 数据库主键  private Date creationTime;//创建时间  private Date modificationTime;//修改时间    @Id  @GeneratedValue(strategy = GenerationType.AUTO)  public Integer getId() {   return id;  }  public void setId(Integer id) {   this.id = id;  }  public Date getCreationTime() {   return creationTime;  }  public void setCreationTime(Date creationTime) {   this.creationTime = creationTime;  }  public Date getModificationTime() {   return modificationTime;  }  public void setModificationTime(Date modificationTime) {   this.modificationTime = modificationTime;  }

  }

实体类Test_No1.java

package com.rock.cft.test.model;

import java.io.Serializable; import java.util.Date;

import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.Table;

import com.rock.cft.hibernate.BaseEntity;

@Entity @Table(name="test_no2") public class Test_No1 extends BaseEntity implements Serializable {    private String name;  private int age;

 

 public String getName() {   return name;  }  public void setName(String name) {   this.name = name;  }  public int getAge() {   return age;  }  public void setAge(int age) {   this.age = age;  }   

}

实体类Test_NO2.java

package com.rock.cft.test.model;

import java.io.Serializable; import java.util.Date;

import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.Table;

import com.rock.cft.hibernate.BaseEntity;

@Entity @Table(name="test_no2") public class Test_NO2 extends BaseEntity implements Serializable {      private Date testBri;    private String testAdr;    public Date getTestBri() {   return testBri;  }  public void setTestBri(Date testBri) {   this.testBri = testBri;  }  public String getTestAdr() {   return testAdr;  }  public void setTestAdr(String testAdr) {   this.testAdr = testAdr;  }    

   

}

 
这样在生成表的时候只生成了:test_no1、test_no2两张表,而且两张表中都含有id、creationTime、modificationTime三个属性
 
但是如果把@MappedSuperclass换成@Entity那么就会另外在生成一张baseEntity的表


 关于定义那些列的等具体细节,这里就不再说明了。


<think>我们正在讨论C#中抽象的命名规范。根据C#命名惯例和业界实践,抽象的命名通常采用以下方式: 1. **前缀使用“Abstract”**:明确表示该是抽象的,不能被实例化。例如:`AbstractShape`。 2. **后缀使用“Base”**:表示该是基,供其他继承。例如:`ShapeBase`。 3. **直接使用描述性名词**:如果上下文已经足够清晰,也可以不加特定前缀或后缀,但通常要求名本身能体现其抽象性质。例如:`Shape`(在已知该是抽象的情况下)。 然而,更常见的做法是使用“Base”后缀或“Abstract”前缀,以增强代码可读性。 参考引用[1]和[2]中的示例,它们使用了`Animal`和`Shape`作为抽象的名称,没有加前缀或后缀。但在实际项目中,为了清晰起见,推荐使用更明确的命名方式。 ### 推荐命名规范 1. **使用“Abstract”前缀**(推荐) - 示例:`AbstractShape`, `AbstractAnimal` - 优点:一目了然地表明这是一个抽象。 2. **使用“Base”后缀**(推荐) - 示例:`ShapeBase`, `AnimalBase` - 优点:表明这是基,常用于框架设计中。 3. **避免直接使用普通名词**(除非上下文非常明确) - 不推荐:`Shape`(如果项目中有多个`Shape`相关,容易混淆) - 例外:在小型项目或领域驱动设计(DDD)中,如果抽象在领域模型中非常明确,可以直接使用领域名称。 ### 代码示例 ```csharp // 使用Abstract前缀 public abstract class AbstractLogger { public abstract void Log(string message); public virtual void Flush() { /* 默认实现 */ } } // 使用Base后缀 public abstract class LoggerBase { public abstract void Log(string message); public virtual void Flush() { /* 默认实现 */ } } // 直接使用领域名(在明确是抽象的情况下) public abstract class Logger { public abstract void Log(string message); } ``` ### 最佳实践建议 - **一致性**:在整个项目中保持统一风格(要么全用“Abstract”前缀,要么全用“Base”后缀)。 - **可读性优先**:如果团队更倾向于简洁,且抽象在项目中数量不多,可以直接使用领域名。 - **文档注释**:无论采用哪种命名,都应使用XML注释说明的用途和抽象成员的作用。 ### 相关问题 1. 抽象和接口在命名上有何区别?(接口通常以"I"开头,如`ILogger`) 2. 抽象是否应该包含构造函数?如何命名抽象的构造函数? 3. 在领域驱动设计(DDD)中,抽象的命名如何与领域术语保持一致? 4. 如何通过命名区分抽象基和中间抽象层(如`ShapeBase` vs `PolygonBase`)? [^1]: C#抽象定义规范 [^2]: 面向对象设计中的命名约定</think>### C# 抽象命名规范与实践指南 根据C#设计规范和业界最佳实践,抽象的命名需遵循以下原则: #### 核心命名规范 1. **无强制前缀/后缀** C#官方规范**不要求**抽象使用特定前缀(如"Abstract")或后缀(如"Base"),但需满足: ```csharp // ✅ 规范示例 public abstract class Vehicle { ... } // 直接使用领域名词 public abstract class ShapeBase { ... } // 可选Base后缀 ``` 2. **语义优先原则** - 优先使用**领域概念名词**(如`PaymentProcessor`) - 避免技术性词汇(如`AbstractPayment`) #### 推荐实践 | 场景 | 命名方案 | 示例 | 优势 | |------|----------|------|------| | 核心领域模型 | 纯名词 | `Account`<br>`DataTransformer` | 领域驱动设计友好 [^2] | | 框架基 | Base后缀 | `ControllerBase`<br>`RepositoryBase` | 明确标识基础功能 | | 大型系统 | Abstract前缀 | `AbstractReportGenerator` | 避免与具体混淆 | | 接口协作 | Adapter后缀 | `LoggerAdapter` | 强调适配器模式 | #### 代码示例 ```csharp // ✅ 最佳实践 - 领域名词 + 明确职责 public abstract class PaymentGateway { public abstract void ProcessPayment(decimal amount); protected virtual void LogTransaction(string id) { ... } } // ✅ 框架基 - Base后缀 public abstract class EntityBase { public abstract Guid Id { get; } public virtual DateTime CreatedAt => DateTime.UtcNow; } // ⚠️ 不推荐 - 冗余前缀 public abstract class AbstractReportService // 应简化为 ReportService ``` #### 设计原则参考 1. **抽象 vs 接口命名** - 接口:`I`前缀(`IPaymentProcessor`) - 抽象:无前缀(`PaymentProcessorBase`) 2. **继承链命名** ```csharp public abstract class Shape { ... } // 顶级抽象 public abstract class Polygon : Shape { ... } // 中间抽象层 public class Rectangle : Polygon { ... } // 具体实现 ``` 3. **避免的陷阱** - 不要混合风格:`AbstractPaymentBase` ❌ - 不要包含"Utility":`AbstractMathUtility` ❌ #### 相关问题 1. 何时应该使用`Base`后缀 vs 直接领域名词? 2. 抽象中的虚方法命名规范与普通方法有何区别? 3. 在DDD架构中,抽象是否应该放在领域层还是基础设施层? 4. 如何通过命名区分抽象和对应的具体实现? [^1]: C# 抽象设计规范 [^2]: 领域驱动设计(DDD)中的建模原则 [^3]: 框架设计中的命名约定实践
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值