# hibernate mysql demo **Repository Path**: weishengshui/hibernate-mysql-demo ## Basic Information - **Project Name**: hibernate mysql demo - **Description**: hibernate mysql demo。 基于xml、annotation的hibernate demo:其中包含one-to-one、one-to-many、many-to-many映射关系的配置。独立的测试用例。 - **Primary Language**: Java - **License**: Not specified - **Default Branch**: master - **Homepage**: http://www.mkyong.com/tutorials/hibernate-tutorials/ - **GVP Project**: No ## Statistics - **Stars**: 34 - **Forks**: 14 - **Created**: 2015-01-07 - **Last Updated**: 2025-02-28 ## Categories & Tags **Categories**: sample-code **Tags**: None ## README # hibernate-mysql-demo 基于maven构建的hibernate mysql demo。
基于xml、annotation的hibernate demo:其中包含one-to-one、one-to-many、many-to-many映射关系的配置。 独立的测试用例。 内容如下: * one-to-one: xml/annotation。 * one-to-many: xml/annotation。 * many-to-many: xml/annotation。 * many-to-many "join table extra column": annotation。中间实体有复合主键,还包含了额外的字段,比如:创建时间、创建人等。 ---- ### hibernate知识点 #### 理解inverse inverse的意思是:相反、倒转;相反的、倒转的。 在hibernate xml映射配置文件里面表示由那一方维护表关系。inverse="true"表示由对方维护表关系,inverse="false"则反之。示例如下: Member与MemberCard是一对多的关系 * 1. 关系由MemberCard维护。 Member配置。[Member.hbm.xml](http://git.oschina.net/weishengshui/hibernate-mysql-demo/blob/master/hibernate-example-xml/src/main/resources/com/hibernate/xml/entity/Member.hbm.xml) ```xml ``` MemberCard配置。[MemberCard.hbm.xml](http://git.oschina.net/weishengshui/hibernate-mysql-demo/blob/master/hibernate-example-xml/src/main/resources/com/hibernate/xml/entity/MemberCard.hbm.xml) ```xml ``` 执行如下java代码 ```java Member member = new Member(); member.setMemberName("张三"); Set memberCards = new HashSet(); MemberCard card1 = new MemberCard(); card1.setCardNo("1234567890"); MemberCard card2 = new MemberCard(); card2.setCardNo("1234567891"); MemberCard card3 = new MemberCard(); card3.setCardNo("1234567892"); // 由MemberCard维护关系,必须设置Member,不然插入外键时找不到member_id card1.setMember(member); card2.setMember(member); card3.setMember(member); memberCards.add(card1); memberCards.add(card2); memberCards.add(card3); member.setMemberCards(memberCards); session.save(member); ``` 得到的sql输出如下:MemberCard在插入时,把外键也插入了。 ```sql Hibernate: insert into member (id, member_name) values (null, ?) Hibernate: insert into member_card (id, card_no, member_id) values (null, ?, ?) Hibernate: insert into member_card (id, card_no, member_id) values (null, ?, ?) Hibernate: insert into member_card (id, card_no, member_id) values (null, ?, ?) ``` * 2. 关系由Member维护。 xml映射文件的配置大致相同,只是Member中的配置inverse="false", MemberCard中的\ not-null要设置成false。 先插入MemberCard,外键暂时为null,后期再由Member update设置MemberCard的外键,所以not-null要允许为null,不然执行不通过。 保存的java代码和上面稍有不同,MemberCard不需要设置Member了。 ```java Member member = new Member(); member.setMemberName("张三"); Set memberCards = new HashSet(); MemberCard card1 = new MemberCard(); card1.setCardNo("1234567890"); MemberCard card2 = new MemberCard(); card2.setCardNo("1234567891"); MemberCard card3 = new MemberCard(); card3.setCardNo("1234567892"); memberCards.add(card1); memberCards.add(card2); memberCards.add(card3); member.setMemberCards(memberCards); session.save(member); ``` 得到的sql输出如下:多了几条update语句。 ```sql Hibernate: insert into member (id, member_name) values (null, ?) Hibernate: insert into member_card (id, card_no, member_id) values (null, ?, ?) Hibernate: insert into member_card (id, card_no, member_id) values (null, ?, ?) Hibernate: insert into member_card (id, card_no, member_id) values (null, ?, ?) Hibernate: update member_card set member_id=? where id=? Hibernate: update member_card set member_id=? where id=? Hibernate: update member_card set member_id=? where id=? ``` 从一对多的关系维护中,不难看出由“多端”维护关系,效率要高。 #### 理解mappedBy 也是定义由谁维护关系,这是annotation中用到的。 假设A/B实体是关系的双方,mappedBy定义在实体A中(关系的被拥有方),另一方B就是关系的拥有方,即维护关系的一方。 首先需要理解的是:mappedBy与注解@JoinColumn/@PrimaryKeyJoinColumn/@JoinTable是冲突的,不能同时在同一个属性上出现;因为 @JoinColumn/@PrimaryKeyJoinColumn/@JoinTable这几个注解是用于维护关系的,而mappedBy在关系维护方的对立面。 只要搞清楚了由谁维护关系,其余跟xml文件配置中的inverse差不多,可参考[理解inverse](http://git.oschina.net/weishengshui/hibernate-mysql-demo#%E7%90%86%E8%A7%A3inverse), 或者看源码[Member](http://git.oschina.net/weishengshui/hibernate-mysql-demo/blob/master/hibernate-example-annotation/src/main/java/com/hibernate/annotation/entity/Member.java)/[MemberCard](http://git.oschina.net/weishengshui/hibernate-mysql-demo/blob/master/hibernate-example-annotation/src/main/java/com/hibernate/annotation/entity/MemberCard.java) #### 理解cascade * 1. cascade定义级联操作,即"操作完自己"之后下一步做什么。 在[理解inverse](http://git.oschina.net/weishengshui/hibernate-mysql-demo#%E7%90%86%E8%A7%A3inverse)中,Member与MemberCard是一对多的关系,Member级联save-update MemberCard,所以只要保存member ```java session.save(member); ``` memberCard也跟着保存了。 * 2. 有时我们会看到这样一个级联配置:cascade="delete-orphan"。 annotation中这样配置 ```java // orphanRemoval=true级联更新删除"脱离"的子节点 @OneToMany(mappedBy = "member", orphanRemoval=true) @Cascade({ CascadeType.SAVE_UPDATE }) private Set memberCards; ``` 或者 ```java // CascadeType.DELETE_ORPHAN级联更新删除"脱离"的子节点 @OneToMany(mappedBy = "member") @Cascade({ CascadeType.SAVE_UPDATE, CascadeType.DELETE_ORPHAN }) private Set memberCards; ``` 比较新的版本,官方推荐使用orphanRemoval=true ```java public enum CascadeType { ... /** @deprecated use @OneToOne(orphanRemoval=true) or @OneToMany(orphanRemoval=true) */ @Deprecated DELETE_ORPHAN, ... } ``` 这是用于删除“脱离”的子节点。 ```java Member member = (Member)session.get(Member.class, 1); member.setMemberName("张三2"); Set memberCards = member.getMemberCards(); // 确认会员有3张会员卡 Assert.assertEquals(3, memberCards.size()); // 从中取出一张会员卡 MemberCard memberCard = memberCards.iterator().next(); // 把这张会员卡移除掉 member.getMemberCards().remove(memberCard); session.update(member); ``` 从上面的代码中可以看到只update一下member,在update member之后有一条delete语句,sql输出如下: ```sql Hibernate: select member0_.id as id2_0_, member0_.member_name as member2_2_0_ from member member0_ where member0_.id=? Hibernate: select membercard0_.member_id as member3_2_1_, membercard0_.id as id1_, membercard0_.id as id3_0_, membercard0_.card_no as card2_3_0_, membercard0_.member_id as member3_3_0_ from member_card membercard0_ where membercard0_.member_id=? Hibernate: update member set member_name=? where id=? Hibernate: delete from member_card where id=? ``` * 3. cascade:JPA & Hibernate annotation。 这里是很容易让人误解的地方。 JPA cascade: javax.persistence.CascadeType ```java public enum CascadeType { /** Cascade all operations */ ALL, /** Cascade persist operation */ PERSIST, /** Cascade merge operation */ MERGE, /** Cascade remove operation */ REMOVE, /** Cascade refresh operation */ REFRESH } ``` Hibernate cascade:org.hibernate.annotations.CascadeType ```java public enum CascadeType { ALL, PERSIST, MERGE, REMOVE, REFRESH, DELETE, SAVE_UPDATE, REPLICATE, /** @deprecated use @OneToOne(orphanRemoval=true) or @OneToMany(orphanRemoval=true) */ @Deprecated DELETE_ORPHAN, LOCK, /** @deprecated use javax.persistence.CascadeType.DETACH */ @Deprecated EVICT, DETACH } ``` 有时我们这样配置,这里应用的是JPA cascade ```java @OneToMany(mappedBy = "member", fetch = FetchType.LAZY, cascade={CascadeType.PERSIST}) private Set memberCards; ``` 保存member,发现memberCard没有跟着保存,除非使用CascadeType.ALL,否则其他的都没有用。 ```java session.save(member); ``` sql输出 ```sql Hibernate: insert into member (id, member_name) values (null, ?) ``` 但是session.persist()却可以级联保存 ```java session.persist(member); ``` sql输出 ```sql Hibernate: insert into member (id, member_name) values (null, ?) Hibernate: insert into member_card (id, card_no, member_id) values (null, ?, ?) Hibernate: insert into member_card (id, card_no, member_id) values (null, ?, ?) Hibernate: insert into member_card (id, card_no, member_id) values (null, ?, ?) ``` 如果我们想session.save()方法有效果,可以使用hibernate cascade: ```java @OneToMany(mappedBy = "member", fetch = FetchType.LAZY) @Cascade({ CascadeType.SAVE_UPDATE }) private Set memberCards; ``` 再试一下 ```java session.save(member); ``` sql输出 ```sql Hibernate: insert into member (id, member_name) values (null, ?) Hibernate: insert into member_card (id, card_no, member_id) values (null, ?, ?) Hibernate: insert into member_card (id, card_no, member_id) values (null, ?, ?) Hibernate: insert into member_card (id, card_no, member_id) values (null, ?, ?) ``` 从上面的实践中,大概可以知道cascade的类型粗略的对应着session的一个方法。更详细的内容可以查看hibernate源码 org.hibernate.engine.Cascade/org.hibernate.engine.CascadeStyle/org.hibernate.engine.CascadingActiona #### cascade vs inverse(mappedBy) cascade说明级联操作,操作完自己之后下一步做什么;inverse说明由谁来维护外键的值。 #### 参数设定的几种方式 * 1. 命名参数:冒号后面跟参数名称。 setString ```java List members = session .createQuery("from Member where memberName=:memName") .setString("memName", "张三").list(); ``` setParameter ```java // 自动判断参数的类型。 List members = session .createQuery("from Member where memberName=:memName") .setParameter("memName", "张三").list(); ``` setProperties ```java // pojo bean Member member = new Member(); member.setMemberName("张三"); List members = session .createQuery("from Member where memberName=:memberName") .setProperties(member).list(); // 还有一种方式Map。 // Map params = new HashMap(); // params.put("memName", "张三"); // List members = session // .createQuery("from Member where memberName=:memName") // .setProperties(params).list(); ``` * 2. 位置参数:基数位置从0开始。 原始JDBC中的java.sql.PreparedStatement 中的参数位置从1开始。和命名参数不同的是:命名参数可以出现多次(一个参数值可以复用),比如 memberName1=:memName and memberName2=:memName。 ```java List members = session .createQuery("from Member where memberName=?") .setString(0, "张三").list(); ``` #### session.clear() session维护着自己的一个局部缓存,我们一般称之为一级缓存。hibernate二级缓存是面向整个应用的全局缓存,整个系统共用一个缓存。 clear()方法就是清空session的缓存。 ```java tx = session.beginTransaction(); Member member = new Member(); member.setMemberName("张三"); session.save(member); tx.commit(); tx = session.beginTransaction(); session.clear(); Member member2 = (Member)session.get(Member.class, 1); // member2是因为查询重新创建的对象。 Assert.assertFalse(member2 == member); tx.commit(); ``` sql输出如下 ```sql Hibernate: insert into member (id, member_name) values (null, ?) Hibernate: select member0_.id as id2_0_, member0_.member_name as member2_2_0_ from member member0_ where member0_.id=? ``` 如果我们把session.clear();注视掉,将会只看到一条insert语句。 ```java tx = session.beginTransaction(); Member member = new Member(); member.setMemberName("张三"); session.save(member); tx.commit(); tx = session.beginTransaction(); // session.clear(); Member member2 = (Member)session.get(Member.class, 1); // member2是从缓存中取出的同一个member对象。 Assert.assertTrue(member2 == member); tx.commit(); ``` 缓存只针对id的查询有效(不产生select),下面的java代码将只输出一条insert语句。 ```java tx = session.beginTransaction(); Member member = new Member(); member.setMemberName("张三"); session.save(member); tx.commit(); tx = session.beginTransaction(); // session.clear(); List members = session.createQuery("from Member where id=1").list(); Member member2 = members.get(0); // 确定两个引用指向的是同一个对象 Assert.assertTrue(member2 == member); tx.commit(); ``` 而如果根据其它属性查询,还是会产生一条select sql语句;但是取出的对象和之前的对象还是同一个对象,说明还是会根据查询得到的对象的 id从缓存中取,而不是重新创建对象。 ```java tx = session.beginTransaction(); Member member = new Member(); member.setMemberName("张三"); session.save(member); tx.commit(); tx = session.beginTransaction(); // session.clear(); List members = session.createQuery("from Member where memberName='张三'").list(); Member member2 = members.get(0); // 确定两个引用指向的是同一个对象 Assert.assertTrue(member2 == member); tx.commit(); ``` #### session.flush() flush()方法强制数据库和缓存同步。 如果没有手工flush(),hibernate会根据session.getFlushMode()决定何时同步,默认是FlushMode.AUTO,更多内容参考 [FlushMode API](http://docs.jboss.org/hibernate/orm/3.6/javadocs/org/hibernate/FlushMode.html)。 如果调用了session.flush(),将产生两条update语句;而如果注释掉session.flush(),就只有一条update语句。 ```java tx = session.beginTransaction(); // 原来的memberName是“张三” member = (Member)session.get(Member.class, 1); member.setMemberName("张三2"); session.flush(); member.setMemberName("张三3"); session.update(member); tx.commit(); ``` sql输出 ```sql Hibernate: select member0_.id as id2_0_, member0_.member_name as member2_2_0_ from member member0_ where member0_.id=? Hibernate: update member set member_name=? where id=? Hibernate: update member set member_name=? where id=? ``` 更多内容可参考[hibernate 教程](http://www.mkyong.com/tutorials/hibernate-tutorials/) ---- ### 共用的依赖配置在parent pom.xml文件里面,所有子模块都可以“继承”到,不需要重复配置。 ```xml 4.0.0 com.wss.lsl.hibernate.mysql.demo hibernate-mysql-demo 0.0.1-SNAPSHOT pom hibernate-mysql-demo http://maven.apache.org hibernate-example-xml hibernate-example-annotation JBoss repository http://repository.jboss.com/maven2/ UTF-8 3.6.3.Final 3.6.3.Final junit junit 3.8.1 test com.h2database h2 1.4.184 test mysql mysql-connector-java 5.1.9 runtime org.hibernate hibernate-core ${hibernate-version} javassist javassist 3.12.0.GA org.hibernate hibernate-c3p0 ${hibernate-c3p0-version} org.slf4j slf4j-log4j12 1.6.1 maven-compiler-plugin 2.3.2 1.6 1.6 UTF-8 ```
---- ## 基于xml的hibernate mysql demo ### 配置文件如下 ```xml org.hibernate.dialect.MySQLDialect com.mysql.jdbc.Driver jdbc:mysql://localhost:3306/test?characterEncoding=UTF-8&useUnicode=true root 167118 false true 4 100 100 600 600 ``` ### 测试采用h2内存库模式:便于测试的执行。 ```xml org.hibernate.dialect.H2Dialect org.h2.Driver jdbc:h2:mem:test su false create-drop true 4 100 100 600 600 ```
---- ## 基于annotation的hibernate mysql demo ### xml配置文件略有不同:映射方式不一样。 注意:hibernate从3.6开始xml和annotation配置的类都可以通过"org.hibernate.cfg.Configuration"读取。之前的版本annotation配置需要通过"org.hibernate.cfg.AnnotationConfiguration"读取。 ```xml ``` ### 实体类 * one-to-one ```java /** * stock: 与stockDetail是一对一的关系 * * @author Administrator * */ @Entity @Table(name = "stock", uniqueConstraints = { @UniqueConstraint(columnNames = "stock_code"), @UniqueConstraint(columnNames = "stock_name") }) public class Stock implements Serializable { /** * */ private static final long serialVersionUID = 1L; @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "stock_id", unique = true, nullable = false) private Integer stockId; @OneToOne(mappedBy = "stock") @Cascade({ CascadeType.SAVE_UPDATE, CascadeType.DELETE }) private StockDetail stockDetail; @Column(name = "stock_code", length = 10, nullable = false) private String stockCode; @Column(name = "stock_name", length = 20, nullable = false) private String stockName; public Stock() { } public Stock(Integer stockId, StockDetail stockDetail, String stockCode, String stockName) { super(); this.stockId = stockId; this.stockDetail = stockDetail; this.stockCode = stockCode; this.stockName = stockName; } // getter/setter } ```
```java /** * stockDetail: 与stock是一对一的关系 * * @author Administrator * */ @Entity @Table(name = "stock_detail", uniqueConstraints = { @UniqueConstraint(columnNames = "comp_name") }) public class StockDetail implements Serializable { /** * */ private static final long serialVersionUID = 1L; @Id @Column(name = "stock_id") @GenericGenerator(name = "generator", strategy = "foreign", parameters = @Parameter(name = "property", value = "stock")) @GeneratedValue(generator = "generator") private Integer stockId; @OneToOne @PrimaryKeyJoinColumn private Stock stock; @Column(name = "comp_name", length = 20, nullable = false) private String compName; @Column(name = "comp_desc", length = 200) private String compDesc; @Column(name = "remark", length = 255) private String remark; @Temporal(TemporalType.DATE) @Column(name = "listed_date", length = 10) private Date listedDate; public StockDetail() { } public StockDetail(Integer stockId, Stock stock, String compName, String compDesc, String remark, Date listedDate) { super(); this.stockId = stockId; this.stock = stock; this.compName = compName; this.compDesc = compDesc; this.remark = remark; this.listedDate = listedDate; } // getter/setter } ``` * one-to-many ```java /** * 会员:与会员卡是一对多的关系 * * @author sean * */ @Entity @Table(name = "member", uniqueConstraints = { @UniqueConstraint(columnNames = "member_name") }) public class Member implements Serializable { /** * */ private static final long serialVersionUID = 1L; @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Integer id; @Column(name = "member_name", length = 20, nullable = false) private String memberName; @OneToMany(mappedBy = "member", fetch = FetchType.LAZY) @Cascade({ CascadeType.SAVE_UPDATE, CascadeType.DELETE }) private Set memberCards; public Member() { } public Member(Integer id, String memberName, Set memberCards) { super(); this.id = id; this.memberName = memberName; this.memberCards = memberCards; } // getter/setter } ```
```java /** * 会员卡:与会员是多对一的关系 * * @author sean * */ @Entity @Table(name = "member_card", uniqueConstraints = { @UniqueConstraint(columnNames = "card_no") }) public class MemberCard implements Serializable { /** * */ private static final long serialVersionUID = 1L; @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Integer id; @Column(name = "card_no", length = 20, nullable = false) private String cardNo; @ManyToOne @JoinColumn(name = "member_id", referencedColumnName="id") private Member member; public MemberCard() { } public MemberCard(Integer id, String cardNo, Member member) { super(); this.id = id; this.cardNo = cardNo; this.member = member; } // getter/setter } ``` * many-to-many ```java /** * 老师:与学生是多对多的关系。 * * @author sean * */ @Entity @Table(name = "teacher", uniqueConstraints = { @UniqueConstraint(columnNames = "teacher_name") }) public class Teacher implements Serializable { /** * */ private static final long serialVersionUID = 1L; @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Integer id; @Column(name = "teacher_name", nullable = false, length = 20) private String teacherName; /** * 定义mappedBy的对方是关系的拥有方,维护关系。 */ @ManyToMany(mappedBy = "teachers") @OrderBy("studentName asc") @Cascade({ CascadeType.SAVE_UPDATE, CascadeType.DELETE }) private Set students; public Teacher() { } public Teacher(Integer id, String teacherName, Set students) { super(); this.id = id; this.teacherName = teacherName; this.students = students; } // getter/setter } ```
```java /** * 学生:和老师是多对多的关系 * * @author sean * */ @Entity @Table(name = "student", uniqueConstraints = { @UniqueConstraint(columnNames = { "student_name" }) }) public class Student implements Serializable { /** * */ private static final long serialVersionUID = 1L; @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Integer id; @Column(name = "student_name", nullable = false, length = 20) private String studentName; /** * mappedBy定义在被拥有方:老师拥有学生。 */ @ManyToMany(fetch = FetchType.LAZY) @OrderBy("teacherName asc") @JoinTable(name = "teacher_student", joinColumns = @JoinColumn(name = "student_id", nullable = false, updatable = false), inverseJoinColumns = @JoinColumn(name = "teacher_id", nullable = false, updatable = false)) private Set teachers; public Student() { } public Student(Integer id, String studentName, Set teachers) { super(); this.id = id; this.studentName = studentName; this.teachers = teachers; } // getter/setter } ``` * many-to-many(extra column):中间表有额外的字段。 ```java /** * 代理商:与客户是多对多的关系,中间实体AgentCustomer有除关系的额外字段 * * @author Administrator * */ @Entity @Table(name = "agent", uniqueConstraints = { @UniqueConstraint(columnNames = "agent_name") }) public class Agent implements Serializable { /** * */ private static final long serialVersionUID = 1L; @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Integer id; @Column(name = "agent_name", length = 20, nullable = false) private String agentName; // orphanRemoval:级联删除孤立节点 @OneToMany(fetch = FetchType.LAZY, mappedBy = "pk.agent", orphanRemoval = true) @OrderBy("pk.customer asc") @Cascade({ CascadeType.SAVE_UPDATE, CascadeType.DELETE }) private Set agentCustomers; public Agent() { } public Agent(Integer id, String agentName, Set agentCustomers) { super(); this.id = id; this.agentName = agentName; this.agentCustomers = agentCustomers; } // getter/setter } ```
```java /** * 客户:和代理商是多对多的关系,中间实体AgentCustomer有除关系的额外字段 * * @author Administrator * */ @Entity @Table(name = "customer", uniqueConstraints = { @UniqueConstraint(columnNames = "customer_name") }) public class Customer implements Serializable { /** * */ private static final long serialVersionUID = 1L; @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Integer id; @Column(name = "customer_name", nullable = false, length = 20) private String customerName; @OneToMany(fetch = FetchType.LAZY, mappedBy = "pk.customer") @OrderBy("pk.agent asc") private Set agentCustomers; public Customer() { } public Customer(Integer id, String customerName, Set agentCustomers) { super(); this.id = id; this.customerName = customerName; this.agentCustomers = agentCustomers; } // getter/setter } ```
```java /** * 复合主键 * * @author Administrator * */ @Embeddable public class AgentCustomerId implements Serializable { /** * */ private static final long serialVersionUID = 1L; @ManyToOne private Agent agent; @ManyToOne private Customer customer; public AgentCustomerId() { } public AgentCustomerId(Agent agent, Customer customer) { super(); this.agent = agent; this.customer = customer; } @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (obj == null || getClass() != obj.getClass()) { return false; } AgentCustomerId that = (AgentCustomerId) obj; if (agent != null ? !agent.equals(that.agent) : that.agent != null) { return false; } if (customer != null ? !customer.equals(that.customer) : that.customer != null) { return false; } return true; } @Override public int hashCode() { int result; result = agent != null ? agent.hashCode() : 0; result = 31 * result + (customer != null ? customer.hashCode() : 0); return result; } // getter/setter } ```
```java /** * 代理商与客户的中间关系实体 * * @author Administrator * */ @Entity @Table(name = "agent_customer") @AssociationOverrides({ @AssociationOverride(name = "pk.agent", joinColumns = @JoinColumn(name = "agent_id")), @AssociationOverride(name = "pk.customer", joinColumns = @JoinColumn(name = "customer_id")) }) public class AgentCustomer implements Serializable { /** * */ private static final long serialVersionUID = 1L; @EmbeddedId private AgentCustomerId pk; @Temporal(TemporalType.DATE) @Column(name = "created_date", length = 10, nullable = false) private Date createdDate; @Column(name = "created_by", length = 20, nullable = false) private String createdBy; public AgentCustomer() { } public AgentCustomer(AgentCustomerId pk, Date createdDate, String createdBy) { super(); this.pk = pk; this.createdDate = createdDate; this.createdBy = createdBy; } public void setCreatedBy(String createdBy) { this.createdBy = createdBy; } public Agent getAgent() { return getPk().getAgent(); } public void setAgent(Agent agent) { getPk().setAgent(agent); } public Customer getCustomer() { return getPk().getCustomer(); } public void setCustomer(Customer customer) { getPk().setCustomer(customer); } @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (obj == null || getClass() != obj.getClass()) { return false; } AgentCustomer that = (AgentCustomer) obj; if (getPk() != null ? !getPk().equals(that.getPk()) : that.getPk() != null) { return false; } return true; } @Override public int hashCode() { return (getPk() != null ? getPk().hashCode() : 0); } // getter/setter } ```