I am trying to map a legacy database with JPA so cannot change database structure. I have an entity TableA with a oneToMany collection tableBs where the foreign key is on TableB. The objects in this collection have a ManyToOne relationship with TableC. TableB also has a oneToOne relationship with TableD with the foreign key also on tableB. TableD has a ManyToOne relationship with TableE (tableD having foreign key) and finally TableE has a oneToOne with tableC (tableE having foreign key).
When I call save on tableA I want it to cascade any changes to the collection tableBs so I am cascading save operation. This seems to work fine but when I am adding a new TableB entity to the collection, I set a tableC object on tableB entity but after save this becomes proxy but I need this to be initialised. I have mocked up an example below..
Database tables...
CREATE TABLE TABLEA (tableAPk VARCHAR2(10) PRIMARY KEY);
CREATE TABLE TABLEC (ID NUMBER PRIMARY KEY);
CREATE TABLE TABLEE (ID NUMBER PRIMARY KEY, tableEProperty NUMBER,
CONSTRAINT FK_TABLEC2 FOREIGN KEY (tableEProperty) REFERENCES TABLEC(ID));
CREATE TABLE TABLED (ID NUMBER PRIMARY KEY, tableDProperty NUMBER,
CONSTRAINT FK_TABLEE FOREIGN KEY (tableDProperty) REFERENCES TABLEE(ID));
CREATE TABLE TABLEB (ID NUMBER PRIMARY KEY, tableBProperty VARCHAR2(10), tableBProperty2 NUMBER, tableBProperty3 NUMBER,
CONSTRAINT FK_TABLEA FOREIGN KEY (tableBProperty) REFERENCES TABLEA (tableAPk),
CONSTRAINT FK_TABLEC FOREIGN KEY (tableBProperty2) REFERENCES TABLEC (ID),
CONSTRAINT FK_TABLED FOREIGN KEY (tableBProperty3) REFERENCES TABLED (ID));
CREATE SEQUENCE TABLEA_SEQ START WITH 1;
CREATE SEQUENCE TABLEB_SEQ START WITH 1;
CREATE SEQUENCE TABLEC_SEQ START WITH 1;
CREATE SEQUENCE TABLED_SEQ START WITH 1;
CREATE SEQUENCE TABLEE_SEQ START WITH 1;
Java code:
#Entity
public class TableA {
#Id
private String tableAPk;
#OneToMany(mappedBy="tableBProperty", fetch=FetchType.EAGER, cascade=CascadeType.ALL, orphanRemoval=true)
private List<TableB> tableBs = new ArrayList<TableB>();
public String getTableAPk() {
return tableAPk;
}
public void setTableAPk(String tableAPk) {
this.tableAPk = tableAPk;
}
public List<TableB> getTableBs() {
return tableBs;
}
public void setTableBs(List<TableB> tableBs) {
this.tableBs = tableBs;
}
}
#Entity
public class TableB {
#Id
#GeneratedValue(strategy=GenerationType.SEQUENCE, generator="TABLEB_SEQ")
#SequenceGenerator(sequenceName = "TABLEB_SEQ", allocationSize = 1, name = "TABLEB_SEQ")
private Integer id;
private String tableBProperty;
#ManyToOne
#JoinColumn(name = "tableBProperty2")
private TableC tableC;
#OneToOne(cascade = CascadeType.ALL, orphanRemoval = true)
#JoinColumn(name="tableBProperty3")
private TableD tableD;
public TableB() {}
public TableB(String tableBProperty, TableC tableC, TableD tableD) {
this.tableBProperty = tableBProperty;
this.tableC = tableC;
this.tableD = tableD;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getTableBProperty() {
return tableBProperty;
}
public void setTableBProperty(String tableBProperty) {
this.tableBProperty = tableBProperty;
}
public TableC getTableC() {
return tableC;
}
public void setTableC(TableC tableC) {
this.tableC = tableC;
}
public TableD getTableD() {
return tableD;
}
public void setTableD(TableD tableD) {
this.tableD = tableD;
}
}
#Entity
public class TableC {
#Id private Integer id;
#OneToOne(mappedBy="tableC")
private TableE tableE;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public TableE getTableE() {
return tableE;
}
public void setTableE(TableE tableE) {
this.tableE = tableE;
}
}
#Entity
public class TableD {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator="TABLED_SEQ")
#SequenceGenerator(sequenceName = "TABLED_SEQ", allocationSize = 1, name = "TABLED_SEQ")
private Integer id;
#ManyToOne(cascade={CascadeType.PERSIST})
#JoinColumn(name="tableDProperty")
private TableE tableE;
public TableD() {}
public TableD(TableE tableE) {
this.tableE = tableE;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
}
#Entity
public class TableE {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator="TABLEE_SEQ")
#SequenceGenerator(sequenceName = "TABLEE_SEQ", allocationSize = 1, name = "TABLEE_SEQ")
private Integer id;
#OneToOne(fetch=FetchType.LAZY)
#JoinColumn(name = "tableEProperty")
private TableC tableC;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public TableC getTableC() {
return tableC;
}
public void setTableC(TableC tableC) {
this.tableC = tableC;
}
}
public interface TableARepository extends JpaRepository<TableA, String>{
}
public interface TableCRepository extends JpaRepository<TableC, Integer> {
}
#RunWith(SpringRunner.class)
#SpringBootTest
public class TableARepositoryTest {
private static final Integer TEST_ID = -1;
private static final String TEST_ID_STRING = "TEST1";
#Autowired protected DataSource ds;
#Autowired private TableARepository repoA;
#Autowired private TableCRepository repoC;
protected JdbcTemplate jdbcTemplate;
#Before
public void setUp() throws Exception{
jdbcTemplate = new JdbcTemplate(ds);
String insertASql = "insert into TableA (tableAPk) values (?)";
jdbcTemplate.update(insertASql, new Object[]{TEST_ID_STRING});
String insertCSql = "insert into TableC (id) values (?)";
jdbcTemplate.update(insertCSql, new Object[]{TEST_ID});
String insertESql = "insert into TableE (id, tableEProperty) values (?, ?)";
jdbcTemplate.update(insertESql, new Object[]{TEST_ID, TEST_ID});
}
#After
public void tearDown() throws Exception{
String deleteBSql = "delete from TableB where tableBProperty = ?";
jdbcTemplate.update(deleteBSql, new Object[]{TEST_ID_STRING});
String deleteDSql = "delete from TableD where tableDProperty = ?";
jdbcTemplate.update(deleteDSql, new Object[]{TEST_ID});
String deleteESql = "delete from TableE where ID = ?";
jdbcTemplate.update(deleteESql, new Object[]{TEST_ID});
String deleteASql = "delete from TableA where tableAPk = ?";
jdbcTemplate.update(deleteASql, new Object[]{TEST_ID_STRING});
String deleteCSql = "delete from TableC where ID = ?";
jdbcTemplate.update(deleteCSql, new Object[]{TEST_ID});
}
#Test
public void test() {
TableA tableA = repoA.findById(TEST_ID_STRING).get();
TableC tableC = repoC.findById(TEST_ID).get();
tableA.getTableBs().add(new TableB(TEST_ID_STRING, tableC, new TableD(tableC.getTableE())));
TableA updatedTableA = null;
try {
updatedTableA = repoA.save(tableA);
} catch(Exception e) {
fail("test:"+e.getMessage());
}
assertNotNull(updatedTableA);
assertTrue(Hibernate.isInitialized(updatedTableA.getTableBs().get(0).getTableC()));
}
}
This test passes but if you inspect the returned object, it is a proxy with value TableC_$$_jvst.... My app will then fall over when it tries to serialise this object (which I need).
Wrap your entity in question with :
public static <T> T unproxy(T entity){
Hibernate.initialize(entity)
if(entity instanceof HibernateProxy){
entity = (T)((HibernateProxy)entity).getHibernateLazyInitializer().getImplementation();
} else {
entity = (T)entity
}
return entity;
}
It'll return you the unproxied Object.
Related
I have two entity classes which have a OneToOne Bidirectional connection along with #CascadeOnDelete Tag. The DDLs generated from them do not have the on delete cascade.
#Entity(name = "Test")
#CascadeOnDelete
public class Test {
#Id
#GeneratedValue
private long id;
public long getId() {
return id;
}
public void setId(long newId) {
this.id = newId;
}
#OneToOne(cascade = { CascadeType.ALL }, orphanRemoval=true, mappedBy = "test")
#CascadeOnDelete
Test1 test1 = null;
public Test1 getTest() {
return test1;
}
public void setTest(Test1 test) {
this.test1 = test;
}
}
#Entity(name = "Test1")
#CascadeOnDelete
public class Test1 {
#Id
#GeneratedValue
private long id;
public long getId() {
return id;
}
public void setId(long newId) {
this.id = newId;
}
#OneToOne
#JoinColumn(name = "test1")
#CascadeOnDelete
Test test = null;
public Test getTest() {
return test;
}
public void setTest(Test test) {
this.test = test;
}
public String getS() {
return s;
}
public void setS(String s) {
this.s = s;
}
String s;
}
Generates DDLs as
CREATE TABLE TEST (ID BIGINT NOT NULL, PRIMARY KEY (ID));
CREATE TABLE TEST1 (ID BIGINT NOT NULL, S VARCHAR(255), test1 BIGINT, PRIMARY KEY (ID));
ALTER TABLE TEST1 ADD CONSTRAINT FK_TEST1_test1 FOREIGN KEY (test1) REFERENCES TEST (ID);
CREATE TABLE SEQUENCE (SEQ_NAME VARCHAR(50) NOT NULL, SEQ_COUNT DECIMAL(15), PRIMARY KEY (SEQ_NAME));
INSERT INTO SEQUENCE(SEQ_NAME, SEQ_COUNT) values ('SEQ_GEN', 0);
Why is there no ON DELETE CASCADE; at the end of Alter table? For OneToMany, it works without problem.
Similar(yet older version) question(also unanswered):
Eclipselink - #CascadeOnDelete doesn't work on #OneToOne relations
I need help on hibernate mapping for a bean property refers to multiple classes.
In my application we are implementing permissions. These permission are not specific to certain user it may based on groups(contains list of users) and roles. So, Permissions will apply to users, roles and groups.
Following are ddl and entity classes. Please review and help me.
DDL:
--stores the application users
CREATE TABLE users (
id serial PRIMARY KEY,
name text,
CONSTRAINT uk_users_name UNIQUE (name)
);
--stores the application groups
CREATE TABLE groups (
id serial PRIMARY KEY,
name text,
CONSTRAINT uk_groups_name UNIQUE (name)
);
--stores the application roles
CREATE TABLE roles (
id serial PRIMARY KEY,
name text,
CONSTRAINT uk_roles_name UNIQUE (name)
);
--stores the application object types
CREATE TABLE app_object_types (
id serial PRIMARY KEY,
name text,
CONSTRAINT uk_app_object_types_name UNIQUE (name)
);
INSERT INTO app_object_types (name) VALUES ('USERS');
INSERT INTO app_object_types (name) VALUES ('GROUPS');
INSERT INTO app_object_types (name) VALUES ('ROLES');
CREATE TABLE app_permissions (
id serial PRIMARY KEY,
object_type_id integer REFERENCES app_object_types(id), -- To represent the object type
object_id integer, -- Objecct_id refers users -> id, groups -> id, roles - id
permission_name text,
CONSTRAINT uk_permissions UNIQUE (object_type_id, object_id, permission_name)
);
Entity Classes:
#Entity
#Table(name = "users")
public class Users {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
private int name;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public int getName() {
return name;
}
public void setName(int name) {
this.name = name;
}
}
#Entity
#Table(name = "groups")
public class Groups {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
private int name;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public int getName() {
return name;
}
public void setName(int name) {
this.name = name;
}
}
#Entity
#Table(name = "roles")
public class Roles {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
private int name;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public int getName() {
return name;
}
public void setName(int name) {
this.name = name;
}
}
#Entity
#Table(name = "app_object_types")
public class AppObjectTypes {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
private int name;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public int getName() {
return name;
}
public void setName(int name) {
this.name = name;
}
}
#Entity
#Table(name = "app_permissions")
public class AppPermissions {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
#ManyToOne
private String permissionName;
#ManyToOne
private AppObjectTypes appObjectTypes;
private int objectId;
private Class<?> dependentObject;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getPermissionName() {
return permissionName;
}
public void setPermissionName(String permissionName) {
this.permissionName = permissionName;
}
public AppObjectTypes getAppObjectTypes() {
return appObjectTypes;
}
public void setAppObjectTypes(AppObjectTypes appObjectTypes) {
this.appObjectTypes = appObjectTypes;
}
public int getObjectId() {
return objectId;
}
public void setObjectId(int objectId) {
this.objectId = objectId;
}
public Class<?> getDependentObject() {
return dependentObject;
}
public void setDependentObject(Class<?> dependentObject) {
this.dependentObject = dependentObject;
}
}
I want to map user (or) group (or) role bean object to AppPermissions -> dependentObject using hibernate. I don't know it is possible or not please help me.
I would suggest you consider the use of #Inheritance here on your AppPermission entity in order to specialize each subclass based on the dependent object types.
#Entity
#Inheritance(strategy = InheritanceType.JOINED)
#DiscriminatorColumn(name = "OBJECT_TYPE")
public class AppPermission {
#Id
#GeneratedValue
private Long permissionId;
private String name;
#Column(name = "OBJECT_TYPE", insertable = false, updatable = false)
private String objectType;
}
#Entity
#DiscriminatorValue("USER")
public class UserAppPermission extends AppPermission {
#ManyToOne(optional = false)
private User user;
}
#Entity
#DiscriminatorValue("ROLE")
public class RoleAppPermission extends AppPermission {
#ManyToOne(optional = false)
private Role role;
}
#Entity
#DiscriminatorValue("GROUP")
public class GroupAppPermission extends AppPermission {
#ManyToOne(optional = false)
private Group group;
}
The first difference here with these mappings from yours is that your AppPermission table will be constructed differently from your current schema and would look like the following (note 4 tables):
Table: AppPermission
id NOT NULL IDENTITY(1,1)
name VARCHAR(255)
OBJECT_TYPE VARCHAR(31)
Table: UserAppPermission
id NOT NULL BIGINT (FK -> AppPermission)
user_id NOT NULL BIGINT (FK -> User)
Table: RoleAppPermission
id NOT NULL BIGINT (FK -> AppPermission)
role_id NOT NULL BIGINT (FK -> Role)
Table: GroupAppPermission
id NOT NULL BIGINT (FK -> AppPermission)
group_id NOT NULL BIGINT (FK -> Group)
The whole point of a database is to help us maintain referential integrity. That's why when a table depends on a row from another table, the dependent table rows that relate to the row you wish to remove should be removed first to avoid constraint violations. This is precisely why I have split the relations into separate tables and here I've defined each relation as "optional=false" so that basically it represents a join-table.
Another additional benefit is that if your AppPermission has attributes you need to store specific to the type of dependent object, you can freely add those attributes to the subclass and those attributes are stored separately in that specific subclass's table.
This setup also eliminates your AppObjectType table because that is now driven as part of Hibernate's discriminator pattern. Be aware that if you have other "object-types" you'll need to introduce their specific implementations too with this setup.
Lastly, I exposed (which you don't have to) the OBJECT_TYPE as an non-insertable and non-updatable field because Hibernate manages that for you. But I've exposed it allowing you to make polymorphic queries and determine the object type of the resulting object without having to perform instanceof checks if you wish.
I have two tables in my database that are mapped together via composite primary key/foreign keys, and I'm having a hell of a time getting Hibernate to work with them. My database looks like this:
TABLE1 has a composite primary key of foreign keys, mapping to TABLE_A and TABLE_B. TABLE2 also has a composite primary key of foreign keys, mapping to TABLE_A, TABLE_B, and TABLE_D. In the database, TABLE2 maps back to TABLE1 using just the first two foreign keys. No problems there. It's translating this to Hibernate that is killing me.
Because TABLE2 requires an embedded id with three columns, I cannot use the #OneToMany annotation's mappedBy parameter. I get the expected error of the number of foreign keys not matching the primary key columns. So, I utilized #JoinColumns instead. And that worked perfectly fine for saving new entities. However, when I attempt to delete some mappings from TABLE2, I run into an issue where Hibernate is attempting to update TABLE2 before deleting, setting FK_TABLE_A to null, which is obviously not allowed. The best I've been able to find is that the use of inverse="true" in a mapping xml might resolve the problem, ensuring that Hibernate knows that despite the use of #JoinColumn, the TABLE1 entity should be the owner of the relationship. But I'm not using the XML, and I cannot figure out what the equivalent might be via annotations.
Here's what I have so far:
#Entity
#AssociationOverrides({
#AssociationOverride(name = "pk.tableA",
joinColumns = #JoinColumn(name = "FK_TABLE_A")),
#AssociationOverride(name = "pk.tableB",
joinColumns = #JoinColumn(name = "FK_TABLE_B")) })
#Table(name="TABLE1")
public class Table1 extends BaseObject implements Serializable
{
private static final long serialVersionUID = 1L;
private Table1Id pk = new Table1Id();
#EmbeddedId
public Table1Id getPk() {
return pk;
}
public void setPk(Table1Id pk) {
this.pk = pk;
}
private TableC tableC;
#ManyToOne
#JoinColumn(name = "FK_TABLE_C", referencedColumnName = "ID", nullable = true)
public TableC getTableC () {
return this.tableC;
}
public void setTableC(TableC tableC) {
this.tableC= tableC;
}
private List<Table2> table2s;
#OneToMany(cascade = {CascadeType.ALL}, orphanRemoval=true, fetch=FetchType.EAGER)
#JoinColumns({
#JoinColumn(name="FK_TABLE_A", referencedColumnName="FK_TABLE_A"),
#JoinColumn(name="FK_TABLE_B", referencedColumnName="FK_TABLE_B")
})
public List<Table2> getTable2s() {
return table2s;
}
public void setTable2s(List<Table2> table2s) {
this.table2s= table2s;
}
#Override
public boolean equals(Object o) {
...
}
#Override
public int hashCode() {
...
}
#Override
public String toString() {
...
}
}
#Embeddable
public class Table1Id extends BaseObject implements Serializable
{
private static final long serialVersionUID = 1L;
private TableA tableA;
private TableB tableB;
#ManyToOne
public TableA getTableA() {
return tableA;
}
public void setTableA(TableA tableA) {
this.tableA = tableA;
}
#ManyToOne
public TableB getTableB() {
return tableB;
}
public void setTableB(TableB tableB) {
this.tableB= tableB;
}
#Override
public boolean equals(Object o) {
...
}
#Override
public int hashCode() {
...
}
#Override
public String toString() {
...
}
}
#Entity
#AssociationOverrides({
#AssociationOverride(name = "pk.tableA",
joinColumns = #JoinColumn(name = "FK_TABLE_A")),
#AssociationOverride(name = "pk.tableB",
joinColumns = #JoinColumn(name = "FK_TABLE_B")),
#AssociationOverride(name = "pk.tableD",
joinColumns = #JoinColumn(name = "FK_TABLE_D")) })
#Table(name="TABLE2")
public class Table2 extends BaseObject implements Serializable
{
private static final long serialVersionUID = 1L;
private Table2Id pk = new Table2Id ();
#EmbeddedId
public Table2Id getPk() {
return pk;
}
public void setPk(Table2Id pk) {
this.pk = pk;
}
private Double value;
#Column(name = "VALUE", nullable = false, insertable = true, updatable = true, precision = 2)
#Basic
public Double getValue() {
return this.value;
}
public void setValue(Double value) {
this.goal = goal;
}
#Override
public boolean equals(Object o) {
...
}
#Override
public int hashCode() {
...
}
#Override
public String toString() {
...
}
}
#Embeddable
public class Table2Id extends BaseObject implements Serializable
{
private static final long serialVersionUID = 1L;
private TableA tableA;
#ManyToOne
public TableA getTableA() {
return tableA;
}
public void setTableA(TableA tableA) {
this.tableA= tableA;
}
private TableB tableB;
#ManyToOne
public TableB getTableB() {
return tableB;
}
public void setTableB(TableB tableB) {
this.tableB= tableB;
}
private TableD tableD;
#ManyToOne
public TableD getTableD() {
return this.tableD;
}
public void setTableD(TableD tableD) {
this.tableD= tableD;
}
#Override
public boolean equals(Object o) {
...
}
#Override
public int hashCode() {
...
}
#Override
public String toString() {
...
}
}
Normally for relationships like this, I just use the mappedBy value of the #OneToMany annotation, and everything works fine - updates, inserts, and deletes execute as expected and desired. But given the admittedly odd way the underlying tables are constructed, I cannot do that. Mapping to only a single record in the Table2Id (mappedBy="pk.tableA" or mappedBy="pk.tableB") would result in completely incorrect data. I require both fields to have an appropriate match, but near as I can tell I cannot have multiple columns listed in mappedBy. mappedBy="pk.tableA, pk.tableB" fails.
I know that I can resolve this easily by just modifying the database and adding a single ID primary key to TABLE1, and a single FK_TABLE1 primary key to TABLE2. Then I could just use my standard approach of #OneToMany(mappedBy="table1"...). But I was really hoping to avoid that, if for no other reason that I obviously don't need to do that on the database level. I'm hoping there's a way to tell Hibernate that Table1 is the owner, and that all changes to Table2 are dependent on it.
God, this was a nightmare. I finally have it figured out and in retrospect, it's something I really should have thought of sooner. Here's what worked for me in case anyone else has a similar problem in the future.
The problem is that the Table2 embedded Id was mapping directly to the same entities as the Table1 embedded Id. That's what I want with the database, but not what I want with Hibernate. Instead, the two fields for TableA and TableB should be represented by Table1 itself, and the association overrides written to match. They need to include insertable=false and updatable=false so that Table2 can't make any changes to Table1. In my case, I want just a unidirectional relationship. Table1 can then use the mappedBy parameter of the #OneToMany annotation to map directly to itself. This allows Table1 to control the relationship. So, the code should be:
#Entity
#AssociationOverrides({
#AssociationOverride(name = "pk.tableA",
joinColumns = #JoinColumn(name = "FK_TABLE_A", nullable=false)),
#AssociationOverride(name = "pk.tableB",
joinColumns = #JoinColumn(name = "FK_TABLE_B", nullable=false)) })
#Table(name="TABLE1")
public class Table1 extends BaseObject implements Serializable
{
private static final long serialVersionUID = 1L;
private Table1Id pk = new Table1Id ();
#EmbeddedId
public Table1Id getPk() {
return pk;
}
public void setPk(Table1Id pk) {
this.pk = pk;
}
private TableC tableC;
#ManyToOne
#JoinColumn(name = "FK_TABLE_C", referencedColumnName = "ID", nullable = true)
public TableC getTableC() {
return this.tableC;
}
public void setTableC(TableC tableC) {
this.tableC = tableC;
}
private List<Table2> table2s;
#OneToMany(mappedBy="pk.table1", cascade = {CascadeType.ALL}, orphanRemoval=true, fetch=FetchType.EAGER)
public List<Table2> getTable2s() {
return table2s;
}
public void setTable2s(List<Table2> table2s) {
this.table2s= table2s;
}
#Override
public boolean equals(Object o) {
...
}
#Override
public int hashCode() {
...
}
#Override
public String toString() {
...
}
}
#Entity
#AssociationOverrides({
#AssociationOverride(name = "pk.table1",
joinColumns = {
#JoinColumn(name = "FK_TABLE_A", nullable=false, insertable=false, updatable=false),
#JoinColumn(name = "FK_TABLE_B", nullable=false, insertable=false, updatable=false)
}),
#AssociationOverride(name = "pk.tableD",
joinColumns = #JoinColumn(name = "FK_TABLE_D", nullable=false)) })
#Table(name="TABLE2")
public class Table2 extends BaseObject implements Serializable
{
private static final long serialVersionUID = 1L;
private Table2Id pk = new Table2Id();
#EmbeddedId
public Table2Id getPk() {
return pk;
}
public void setPk(Table2Id pk) {
this.pk = pk;
}
private Double value;
#Column(name = "VALUE", nullable = false, insertable = true, updatable = true, precision = 2)
#Basic
public Double getValue() {
return this.value;
}
public void setValue(Double value) {
this.value = value;
}
#Override
public boolean equals(Object o) {
...
}
#Override
public int hashCode() {
...
}
#Override
public String toString() {
...
}
}
#Embeddable
public class Table2Id extends BaseObject implements Serializable
{
private static final long serialVersionUID = 1L;
private Table1 table1;
#ManyToOne
#JoinColumn(nullable=false)
public Table1 getTable1() {
return this.table1;
}
public void setTable1(Table1 table1) {
this.table1 = table1;
}
private TableD tableD;
#ManyToOne
#JoinColumn(nullable=false)
public TableD getTableD() {
return this.tableD;
}
public void setTableD(TableD tableD) {
this.tableD = tableD;
}
#Override
public boolean equals(Object o) {
...
}
#Override
public int hashCode() {
...
}
#Override
public String toString() {
...
}
}
I have two tables in db employee and department as:
CREATE TABLE test.employee (
EMPID int(10) unsigned NOT NULL DEFAULT '1',
Name varchar(45) NOT NULL DEFAULT '1',
DEPTID int(10) unsigned NOT NULL DEFAULT '1',
PRIMARY KEY (EMPID),
KEY FK_employee_1 (DEPTID),
CONSTRAINT FK_employee_1 FOREIGN KEY (DEPTID) REFERENCES department (DEPTID)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE test.department (
DEPTID int(10) unsigned NOT NULL AUTO_INCREMENT,
Name varchar(45) NOT NULL,
PRIMARY KEY (DEPTID)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;
And my mapping classes are as below:
Employee2.java
#Entity
#Table(name="EMPLOYEE")
public class Employee2 {
#Id #GeneratedValue
#Column(name="EMPID")
private String ID;
#Column(name="Name")
private String Name;
#Column(name="DEPTID")
private String DepartmentID;
public Employee2(String iD, String name, String departmentID){
ID = iD;
Name = name;
DepartmentID = departmentID;
}
public Employee2(){
}
public String getID() {
return ID;
}
public void setID(String iD) {
ID = iD;
}
public String getName() {
return Name;
}
public void setName(String name) {
Name = name;
}
public String getDepartmentID() {
return DepartmentID;
}
public void setDepartmentID(String departmentID) {
DepartmentID = departmentID;
}
#OneToOne
#JoinColumn(table = "DEPARTMENT", name = "DEPTID", referencedColumnName="DEPTID")
private Department2 ec;
public Department2 getEc() {
return ec;
}
public void setEc(Department2 ec) {
this.ec = ec;
}
}
Department2.java
#Entity
#Table(name="DEPARTMENT")
public class Department2 {
#Id #GeneratedValue
#Column(name="DEPTID")
private String ID;
#Column(name="Name")
private String Name;
public Department2(String iD, String name) {
ID = iD;
Name = name;
}
public Department2(){
}
public String getID() {
return ID;
}
public void setID(String iD) {
ID = iD;
}
public String getName() {
return Name;
}
public void setName(String name) {
Name = name;
}
}
I want to select from two tables with join as EMPLOYEE.DEPTID = DEPARTMENT.DEPTID
I dont want to write query.
Here is how I m doing it in test class
tx = session.beginTransaction();
Criteria criteria = session.createCriteria(Employee2.class, "employee").
createCriteria("employee.ec", JoinType.INNER_JOIN);
List<Equipment2> rows = criteria.list();
System.out.println(rows.size());
tx.commit();
But I m getting following exception
Failed to create sessionFactory object.org.hibernate.AnnotationException: Cannot find the expected secondary table: no DEPARTMENT available for com.cts.sm.Employee2
Exception in thread "main" java.lang.ExceptionInInitializerError
I m using Hibernate 4.2
Can you please help me as what I m missing in this.
As suggested by #jpprade
Make the following change
#ManyToOne
#JoinColumn(name ="DEPTID", updatable = false, insertable = false)
private Department2 ec;
//getter setter
Thanks
N G
I have 2 classes (BusinessAccount and Projects (shown below) that are mapped to a MySql database) where a 1:M relationship exists between BusinessAccounts and Projects. I am successfully inserting data to the database but am having a problem when it comes to querying the database. The problem that I am having is that I have no getter or setter for the foreign key, 'contractor_id' in the Projects class. The query that I want to carry out is to return the list of the names of all projects for a given BusinessAccount, by searching by the foreign key reference in the Projects table. I can do this no problem in mySQL but as there is no reference to the contractor_id as a java entity in the Projects class, I'm not sure how to do this search from within my java class. (Note: I tried to declare the foreign key along with getters and setters in the Projects class but as I have these mapped by the 1:Many relationship in the class already, it wouldn't compile as they were flagged as duplicate entities.) I'm sure it's something obvious that I'm missing but any help is much appreciated!
public List<Projects> getProjectList() {
factory = Persistence.createEntityManagerFactory(PERSISTENCE_UNIT_NAME);
EntityManager em = factory.createEntityManager();
List<Projects> projectList = new ArrayList<Projects>();
em.getTransaction().begin();
String sessionEmail=Util.getEmail();
Query myQuery = em.createQuery("SELECT u FROM BusinessAccount u WHERE u.email=:email");
myQuery.setParameter("email", sessionEmail);
List<BusinessAccount> userList=myQuery.getResultList();
BusinessAccount account =userList.get(0);
Query myQuery2 = em.createQuery("SELECT distinct p.* FROM BusinessAccount u "
+ "INNER JOIN Projects p ON p.contractor_id=:userID");
/*Note p.contractor_id above refers to the entity in the
mysql database (and won't work obviously), I want to refer
to it's java equivalent but am not sure how to do that*/
myQuery2.setParameter("userID", account.getId());
projectList=myQuery2.getResultList();
em.getTransaction().commit();
em.close();
return projectList;
}
#Entity
#Table(name = "business_accounts")
public class BusinessAccount {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
#Column(name = "first_name")
private String firstName;
#Column(name = "surname")
private String surname;
#OneToMany(mappedBy = "businessAccount", fetch = FetchType.EAGER, cascade = { CascadeType.ALL })
private List<Projects> projects;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getSurname() {
return surname;
}
public List<Projects> getProjects()
{
if (projects == null)
{
projects = new ArrayList<Projects>();
}
return projects;
}
public void setProjects(List<Projects> projects)
{
this.projects = projects;
}
}
#Entity
#Table(name = "projects")
public class Projects {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int project_id;
#Column(name = "project_name")
private String projectName;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumns({ #JoinColumn(name = "contractor_id", referencedColumnName="id") })
private BusinessAccount businessAccount;
public BusinessAccount getBusinessAccount() {
if (businessAccount == null) {
businessAccount = new BusinessAccount();
}
return businessAccount;
}
public void setBusinessAccount(BusinessAccount businessAccount) {
this.businessAccount = businessAccount;
}
public int getProject_id() {
return project_id;
}
public void setProject_id(int project_id) {
this.project_id = project_id;
}
public String getProjectName() {
return projectName;
}
public void setProjectName(String projectName) {
this.projectName = projectName;
}
}
The JPA query would be something like (you need to use the relation property, but no need for the foreign key itself - please try, it may need some tweaking):
SELECT p FROM BusinessAccount u, IN(u.projects) p WHERE u.id=:userId
But do you really need the query? You can get the related projects from the property:
BusinessAccount account = ...
List<Projects> projectList = account.getProjects();
Try this:
Query myQuery2 = em.createQuery("SELECT distinct p.* FROM BusinessAccount u "
+ "INNER JOIN Projects p ON p.businessAccount=:businessAccount");
myQuery2.setParameter("businessAccount", account);