MENU

業務システム構築のためのエンティティ設計基準

エンティティは、ビジネスロジックにおける実体を表現するための概念である。データベース設計において重要な役割を果たし、現実世界の対象を抽象化したものとして定義される。

目次

エンティティの基本的な概念

エンティティは、独立して存在し得る実体を表現する。例えば、「従業員」「商品」「顧客」などが該当する。これは固有の属性を持ち、システム内で一意に識別可能な対象として扱われる。

データモデリングにおいて、エンティティは以下の特徴を持つ。

public class Employee {
    // 一意に識別するためのID
    private Long employeeId;

    // 従業員の属性
    private String name;
    private String department;
    private Date hireDate;

    // 識別子を取得するためのメソッド
    public Long getEmployeeId() {
        return employeeId;
    }
}

データベースとの関係性

エンティティはデータベースのテーブルと密接な関係を持つ。各エンティティはテーブルに対応し、エンティティの属性はテーブルのカラムとして表現される。この対応関係により、オブジェクト指向プログラミングとリレーショナルデータベースの橋渡しが実現される。

データベースとの関係性は以下のように表現される。

CREATE TABLE employees (
    employee_id BIGINT PRIMARY KEY,  -- エンティティの識別子
    name VARCHAR(100),               -- 従業員名
    department VARCHAR(50),          -- 部署
    hire_date DATE                   -- 雇用日
);

オブジェクトとの違い

エンティティは一般的なオブジェクトとは異なる特徴を持つ。最も重要な違いは、エンティティが永続化を前提としている点である。通常のオブジェクトはメモリ上にのみ存在するが、エンティティはデータベースに保存され、アプリケーションの実行が終了しても存続する。

また、エンティティは必ず一意の識別子を持つ。これは、データベース上での一意性を保証するために不可欠な要素である。

@Entity
public class Product {
    @Id  // 識別子であることを示すアノテーション
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long productId;  // データベース上で一意となる識別子

    // 他の属性やメソッド
}

このように、エンティティはデータベースとアプリケーションを結ぶ重要な要素として機能する。

Javaにおけるエンティティの役割

Javaのエンティティは、オブジェクト指向プログラミングとデータベースの架け橋として機能する。JPA(Java Persistence API)を用いることで、Javaオブジェクトとデータベースレコードの相互変換を実現する。

エンティティクラスの特徴

エンティティクラスは、特別なアノテーションと規約に従って実装される。

@Entity  // エンティティであることを示すアノテーション
@Table(name = "customers")  // マッピング先のテーブル名を指定
public class Customer {
    @Id  // 主キーであることを示す
    @GeneratedValue(strategy = GenerationType.IDENTITY)  // 主キーの生成方法を指定
    private Long id;

    @Column(nullable = false, length = 100)  // カラムの制約を設定
    private String name;

    // デフォルトコンストラクタ(JPAの要件)
    public Customer() {}
}

エンティティクラスは必ずprotectedまたはpublicアクセス修飾子を持つデフォルトコンストラクタが必要である。JPAがリフレクションを使用してインスタンスを生成する際に、この可視性レベルが要求される。セキュリティの観点からprotectedの使用が推奨される。

@Entity
public class Customer {
    @Id
    private Long id;
    
    // JPAの要件を満たすデフォルトコンストラクタ
    protected Customer() {}
    
    // ビジネスロジックで使用するコンストラクタ
    public Customer(Long id) {
        this.id = id;
    }
}

アノテーションの使用方法

JPAアノテーションを使用することで、エンティティの振る舞いを詳細に制御できる。

@Entity
@Table(name = "orders")
public class Order {
    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE)
    private Long orderId;

    @Temporal(TemporalType.TIMESTAMP)  // 日時型の指定
    private Date orderDate;

    @ManyToOne(fetch = FetchType.LAZY)  // 関連エンティティの読み込み方法を指定
    @JoinColumn(name = "customer_id")   // 外部キーのカラム名を指定
    private Customer customer;
}

データの永続化との関連性

エンティティは永続化コンテキストによって管理される。このコンテキストはエンティティの状態を追跡し、必要に応じてデータベースとの同期を行う。

@PersistenceContext
private EntityManager entityManager;

public void saveCustomer(Customer customer) {
    // エンティティの永続化
    entityManager.persist(customer);

    // 永続化コンテキストの状態がデータベースに反映される
    // トランザクションのコミット時に実際のSQLが実行される
}

エンティティの実装方法

実際のエンティティ実装にあたり、適切な設計と実装手順の理解が不可欠である。以下、具体的な実装方法について解説する。

エンティティクラスの作成手順

エンティティクラスの作成は、以下のような手順で進める。

@Entity
@Table(name = "products")
public class Product {  // Serializableの実装は任意

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @NotNull
    @Size(min = 3, max = 100)
    @Column(name = "product_name")
    private String name;

    protected Product() {}

    public Product(String name) {
        this.name = name;
    }
}

フィールドの定義とアクセス方法

フィールドの定義には、適切なアクセス制御とカプセル化が求められる。

@Entity
public class Employee {
   // プライベートフィールドの定義
   @Column(name = "salary")
   @Convert(converter = SalaryAttributeConverter.class)
   private BigDecimal salary;

   // 値の検証を含むセッター 
   public void setSalary(BigDecimal salary) {
       if (salary.compareTo(BigDecimal.ZERO) < 0) {
           throw new IllegalArgumentException("給与は0以上である必要があります");
       }
       this.salary = salary;
   }

   // イミュータブルな値を返すゲッター
   public BigDecimal getSalary() {
       return new BigDecimal(salary.toString());
   }
}

// 暗号化処理を行うコンバーター
@Converter
public class SalaryAttributeConverter implements AttributeConverter<BigDecimal, String> {
   @Override
   public String convertToDatabaseColumn(BigDecimal attribute) {
       // 暗号化処理の実装
       return encryptValue(attribute);
   }

   @Override
   public BigDecimal convertToEntityAttribute(String dbData) {
       // 復号化処理の実装
       return decryptValue(dbData);
   }
   
   // 暗号化メソッド
   private String encryptValue(BigDecimal value) {
       // 暗号化ロジックの実装
       return null; // 実際の実装では適切な暗号化処理を行う
   }
   
   // 復号化メソッド
   private BigDecimal decryptValue(String value) {
       // 復号化ロジックの実装
       return null; // 実際の実装では適切な復号化処理を行う
   }
}

リレーションシップの設定

エンティティ間の関連性を適切に定義することで、オブジェクト間の関係性を表現する。

@Entity
public class Department {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    // 一対多の関連を定義
    @OneToMany(
        mappedBy = "department",     // 関連の所有者を指定
        cascade = CascadeType.ALL,   // 操作の伝播方法を設定
        fetch = FetchType.LAZY       // 遅延ロードを指定
    )
    private Set<Employee> employees = new HashSet<>();

    // 関連の管理メソッド
    public void addEmployee(Employee employee) {
        employees.add(employee);
        employee.setDepartment(this);
    }

    public void removeEmployee(Employee employee) {
        employees.remove(employee);
        employee.setDepartment(null);
    }
}

エンティティの活用例

エンティティの実践的な活用方法について、具体的な実装例を交えて解説する。

基本的なCRUD操作

エンティティに対する基本的なデータベース操作は、EntityManagerを介して実行される。

@Stateless
public class UserService {
    @PersistenceContext
    private EntityManager em;

    // Create操作
    public User createUser(String name, String email) {
        User user = new User(name, email);
        em.persist(user);  // エンティティの永続化
        return user;
    }

    // Read操作
    public User findUser(Long id) {
        return em.find(User.class, id);  // 主キーによる検索
    }

    // Update操作
    public User updateUser(Long id, String newEmail) {
        User user = em.find(User.class, id);
        user.setEmail(newEmail);  // 永続化コンテキストが変更を検知
        return user;
    }

    // Delete操作
    public void deleteUser(Long id) {
        User user = em.find(User.class, id);
        em.remove(user);  // エンティティの削除
    }
}

エンティティマッピングの実践

複雑なデータ構造をエンティティとして表現する際、適切なマッピング設定が重要となる。

@Entity
@Table(name = "orders")
public class Order {
    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE)
    private Long id;

    // 列挙型のマッピング
    @Enumerated(EnumType.STRING)
    private OrderStatus status;

    // 埋め込み可能なオブジェクトのマッピング
    @Embedded
    private Address shippingAddress;

    // 要素コレクションのマッピング
    @ElementCollection
    @CollectionTable(
        name = "order_items",
        joinColumns = @JoinColumn(name = "order_id")
    )
    private Set<OrderItem> items = new HashSet<>();
}

トランザクション管理との連携

エンティティの操作は、トランザクション境界内で適切に管理される必要がある。

public class OrderService {
    @PersistenceContext
    private EntityManager em;

    @Transactional(rollbackOn = StockException.class)
    public Order placeOrder(Long userId, List<OrderItem> items) {
        // トランザクション開始
        User user = em.find(User.class, userId);
        Order order = new Order(user);

        for (OrderItem item : items) {
            // 在庫チェックと更新
            Product product = em.find(Product.class, item.getProductId());
            if (!product.hasStock(item.getQuantity())) {
                throw new StockException("在庫不足");
            }
            product.reduceStock(item.getQuantity());
            order.addItem(item);
        }

        em.persist(order);
        return order;
        // トランザクション終了(コミットまたはロールバック)
    }
}

以上。

よかったらシェアしてね!
  • URLをコピーしました!
目次