Is it possible to use 2 sequence generators in Hibernate Entity Class. I want to use two sequence generator for my case one for the primary key and other for a simple field. How can i achieve the same?
#Data
#Table(name="a_b")
#SequenceGenerator(name = "a1_seq", sequenceName = "a1_seq", allocationSize = 1)
#SequenceGenerator(name = "b1_seq", sequenceName = "b1_seq", allocationSize = 1)
public class ABC {
#Id
#Column(name = "id")
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "a1_seq")
private Integer id;
#Column(name = "c")
private String c;
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "b1")
#Column(name = "b)
private Integer b;
}
You should have only one SequenceGenerator for the Primary Key:
#Id
#Column(name = "id")
#SequenceGenerator(name = "a1_seq", sequenceName = "a1_seq", allocationSize = 1)
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "a1_seq")
private Integer id;
and for the foreign key you could have:
#Column(name = "b, columnDefinition="serial")
private Integer b;
which should work for PostgreSQL.
Define your #SequenceGenerator at column level rather than at class level. So you can create two separate sequenceGenerator - a1_seq and b1 for two different columns.
#Data
#Table(name="a_b")
public class ABC {
#Id
#Column(name = "id")
#SequenceGenerator(name = "a1_seq", sequenceName = "a1_seq", allocationSize = 1)
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "a1_seq")
private Integer id;
#Column(name = "c")
private String c;
#SequenceGenerator(name = "b1_seq", sequenceName = "b1_seq", allocationSize = 1)
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "b1")
#Column(name = "b)
private Integer b;
}
This should work. I have not tested it but since SequenceGenerator is allowed at field level, it should work.
Trick is to add #Id annotation to both Sequence Generators. You can achieve this by using #IdClass().
#Entity
#Table(name = "table_name")
#IdClass(DBSequenceId.class)
public class DBSequence implements Serializable {
#Id
#SequenceGenerator(name = "yourName1", sequenceName = "yourSeqName1", allocationSize = 1)
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "yourName1")
#Column(name = "first_seq", updatable = false)
protected int firstSeq;
#Id
#SequenceGenerator(name = "yourName2", sequenceName = "yourSeqName2", allocationSize = 1)
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "yourName2")
#Column(name = "second_seq", updatable = false)
protected int secondSeq;
}
class DBSequenceId implements Serializable {
private int firstSeq;
private int secondSeq;
//Setters and getters are omitted
}
Tested on MariaDB
Related
I have 2 tables Employee and Address. Each have primary key empId and addressId which is generated through sequence.
Employee Class:
public class Employee {
#Id
#Column(name = "sequence_id")
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "emp_seq")
#SequenceGenerator(sequenceName = "emp_seq", allocationSize = 1, name = "emp_seq")
private Long sequenceId;
#Column(name = "emp_id")
private String empId;
#Column(name = "joiningDate")
private Date businessDate;
#OneToOne(mappedBy = "employee", cascade = CascadeType.ALL)
private Address address;
}
Address Class
public class Address {
#Id
#Column(name = "address_id")
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "address_seq")
#SequenceGenerator(sequenceName = "address_seq", allocationSize = 1, name = "address_seq")
private Long addressID;
#Column(name = "street")
private String street;
#OneToOne
#JoinColumn(name = "emp_id")
#JsonIgnore
private Employee employee;
}
I am using JPARepository to save both entities. here is my main class.
Employee e = Employee.builder().businessDate(new Date())
.empId("thinkerId").build();
Address a = Address.builder().street("32nd Street")
.employee(e).build();
e.setAddress(a);
empRepository.save(e);
Code runs successfully. But in my Address table, emp_Id column contains the primary key instead of the emp_id column.
How can i get "thinkerId" as value in Address table?
Thank you.
In Address entity you must explicitly specify the column you are referring to:
public class Address {
#Id
#Column(name = "address_id")
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "address_seq")
#SequenceGenerator(sequenceName = "address_seq", allocationSize = 1, name = "address_seq")
private Long addressID;
#Column(name = "street")
private String street;
#OneToOne
#JoinColumn(name = "emp_id", referencedColumnName = "emp_id")
#JsonIgnore
private Employee employee;
}
I have a tree-like data structure in my application.
For example, main entity is DraftDoc, it has a child entity DraftEffect. And DraftEffect has its own child entity DraftOffenderCategory.
What is the best strategy for persisting the data to the database?
To collect the whole tree on the front-end side to one big JSON and persist everything at once?
Or to take DraftDoc data (not including related entities) and persist, then take DraftEffect data and persist and so on?
#Entity
#Table(name = "TN_DRAFT_DOCS")
public class DraftDoc implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO, generator = "DRAFTDOC_SEQ")
#SequenceGenerator(name = "DRAFTDOC_SEQ", sequenceName = "NT_DRAFT_DOC_SEQ")
#Column
private Long id;
#Column(name = "ARTICLE_ID")
private Long articleId;
#Column(name = "ARTICLE_VERSION")
private Long articleVersion;
#OneToMany(cascade={CascadeType.ALL},fetch=FetchType.LAZY)
#JoinTable(name = "TN_DRAFT_R_DOC_EFF", joinColumns = { #JoinColumn(name = "doc_id", referencedColumnName = "id") }, inverseJoinColumns = { #JoinColumn(name = "eff_id", referencedColumnName = "id") })
private List<DraftEffect> effects;
...
}
#Entity
#Table(name = "TN_DRAFT_EFFECT")
public class DraftEffect {
#Id
#GeneratedValue(strategy = GenerationType.AUTO, generator = "DRAFT_EFFECT_SEQ")
#SequenceGenerator(name = "DRAFT_EFFECT_SEQ", sequenceName = "NT_DRAFT_EFFECT_SEQ")
#Column
private Long id;
#Column(name = "ARTICLE_ID")
private Long articleId;
#OneToMany(fetch = FetchType.LAZY, mappedBy = "effect")
private List<DraftAffection> affections;
...
}
#Entity
#Table(name = "TN_DRAFT_AFFECTION")
public class DraftAffection implements Serializable {
private static final long serialVersionUID = 3048761786638684368L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO, generator = "DRAFTAFFECTION_SEQ")
#SequenceGenerator(name = "DRAFTAFFECTION_SEQ", sequenceName = "NT_DRAFT_AFFECTION_SEQ")
#Column
private Long id;
#Column(name = "ARTICLE_ID")
private Long articleId;
...
}
I have two classes Cards and CardBalance. In my DB sсhema table card_balance has foreign key on table cards. But in ORM I want that entity Cards has properties CardBalance, and entity CardBalance does't have propertie Cards.
I try do this in next way:
#Entity
#Table(name = "CARD_BALANCE")
public class CardBalance {
#Id
#Column(name = "BALANCE_ID")
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "CARD_BALANCE_SEQ")
#SequenceGenerator(name = "CARD_BALANCE_SEQ", sequenceName = "CARD_BALANCE_SEQ")
private Long balanceId;
#Column(name="CARD_ID")
private Long cardId;
}
#Entity
#Table(name = "CARDS")
public class Cards implements Serializable {
#Id
#Column(name = "CARD_ID")
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "CARDS_SEQ")
#SequenceGenerator(name = "CARDS_SEQ", sequenceName = "CARDS_SEQ")
private Long cardId;
#Column(name = "CARD_NAME", length = 30, unique = true)
private String cardName;
#Column(name="PERSON_ID")
private Long personId;
#Column(name = "CARD_KEY", nullable = false)
private long cardKey;
#OneToOne
#JoinColumn(name="TYPE_ID", nullable = false)
private TypeCard typeCard;
#OneToOne
#JoinColumn(name="CARD_ID", nullable = false)
private CardBalance cardBalance;
#OneToMany(fetch = FetchType.LAZY)
#JoinColumn(name="CARD_ID")
public Set<BalanceHist> balanceHists = new HashSet<>();
#OneToMany(fetch = FetchType.LAZY)
#JoinColumn(name="CARD_ID")
public Set<Events> events = new HashSet<>();
}
but it does't work. How I can solve this problem?
First of all, you have a mistake in your #JoinColumn, it should be:
#OneToOne
#JoinColumn(name="BALANCE_ID", nullable = false)
private CardBalance cardBalance;
This are my entities:
public class Account extends AbstractEntity<Long> {
#Id
#SequenceGenerator(name = "accountSequence", sequenceName = "SQ_ACCOUNTS", allocationSize = 1)
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "accountSequence")
#Column(name = "ACC_ID", nullable = false)
private Long id;
...
}
public class Integration extends AbstractEntity<Long> {
#Id
#SequenceGenerator(name = "integrationSequence", sequenceName="SQ_INTEGRATIONS", allocationSize = 1)
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "integrationSequence")
#Column(name = "INT_ID", nullable = false)
private Long id;
...
public void addIntegration(Integration integration) {
IntegrationAccount association = new IntegrationAccount();
// This does not help
//association.setIntAccountsPK(new IntAccountsPK(integration.getId(), this.getId()));
association.setAccount(this);
association.setIntegration(integration);
this.integrationAccounts.add(association);
integration.getIntAccountsCollection().add(association);
}
}
And this is entity for join table
#Entity
#Table(name = "INT_ACCOUNTS")
public class IntegrationAccount {
#EmbeddedId
protected IntAccountsPK intAccountsPK;
#JoinColumn(name = "ACC_ID", referencedColumnName = "ACC_ID", insertable = false, updatable = false)
#ManyToOne
private Account account;
#JoinColumn(name = "INT_ID", referencedColumnName = "INT_ID", insertable = false, updatable = false)
#ManyToOne
private Integration integration;
...
}
#Embeddable
public class IntAccountsPK implements Serializable {
#Column(name = "INT_ID", nullable = false)
private Long intId;
#Column(name = "ACC_ID", nullable = false)
private Long accId;
...
}
And when i do:
account.addIntegrations(integrations.getTarget());
account.setCustomer(customer);
accountService.save(account);
I got this in my log
Caused by: org.hibernate.id.IdentifierGenerationException: null id generated for:class com.dhl.dcc.domain.IntegrationAccount
I dont have many knowledge about this kind of mapping, can you please tell me how to improve this mapping (entity for join table have to be preserved) and how to save account with related integrations? Thanks.
I know this question has already been marked as solved but I disagree with the accepted answer. This answer modifies the datamodel by adding a useless column (the new id) in the table INT_ACCOUNTS. There is another way to solve this problem in Hibernate without modifying the datamodel :
#Entity
#Table(name = "INT_ACCOUNTS")
public class IntegrationAccount implements Serializable {
#Id
#ManyToOne
#JoinColumn(name = "INT_ID_FK")
private Integration integration;
#Id
#ManyToOne
#JoinColumn(name = "ACC_ID_FK")
private Account account;
}
#Entity
#Table(name = "INTEGRATIONS")
public class Integration {
#Id
#SequenceGenerator(name = "integrationSequence", sequenceName = "SQ_INTEGRATIONS", allocationSize = 1)
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "integrationSequence")
#Column(name = "INT_ID")
private Long id;
}
#Entity
#Table(name = "ACCOUNTS")
public class Account {
#Id
#SequenceGenerator(name = "accountSequence", sequenceName = "SQ_ACCOUNTS", allocationSize = 1)
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "accountSequence")
#Column(name = "ACC_ID")
private Long id;
}
You could create a ID field for your IntegrationAccount and then create a unique constraint for your two fields.
#Entity
#Table(name = "INT_ACCOUNTS",
uniqueConstraints=#UniqueConstraint(columnNames={"ACC_ID", "INT_ID"}))
public class IntegrationAccount {
#Id
private Long id;
#JoinColumn(name = "ACC_ID", referencedColumnName = "ACC_ID", insertable = false, updatable = false)
#ManyToOne
private Account account;
#JoinColumn(name = "INT_ID", referencedColumnName = "INT_ID", insertable = false, updatable = false)
#ManyToOne
private Integration integration;
...
}
Works like a charm!
Hibernate:
/* load entities.Department */ select
department0_.name as name4_0_,
department0_.id as id4_0_
from
J_DEPT department0_
where
department0_.name=?
Hibernate:
/* load one-to-many entities.Department.employees */ select
employees0_.dept as dept4_1_,
employees0_.id as id1_,
employees0_.id as id5_0_,
employees0_.dept as dept5_0_,
employees0_.name as name5_0_
from
J_EMP employees0_
where
employees0_.dept=?
Note that ID and DEPT columns are selected twice.
#Entity
#Table(name = "J_EMP")
public class Employee {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "mySeq")
#SequenceGenerator(name = "mySeq", sequenceName = "CNTRY_SEQ")
private Long id;
private String name;
#ManyToOne
#JoinColumn(name = "dept")
private Department deptNameInEmp;
}
#Entity
#Table(name = "J_DEPT")
public class Department {
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "mySeq")
#SequenceGenerator(name = "mySeq", sequenceName = "CNTRY_SEQ")
private Long id;
#Id
#Column(name = "name")
private String deptNameInDeptEntity;
#OneToMany(mappedBy = "deptNameInEmp")
Set<Employee> employees;
}
UPDATED:
It was done intentionally to put #GeneratedValue on non-PK. However, now I've updated as you specified.
I've copy-pasted the new queries:
Hibernate:
/* load entities.Department */ select
department0_.id as id4_0_,
department0_.name as name4_0_
from
J_DEPT department0_
where
department0_.id=?
Hibernate:
/* load one-to-many entities.Department.employees */ select
employees0_.dept as dept4_1_,
employees0_.id as id1_,
employees0_.id as id5_0_,
employees0_.dept as dept5_0_,
employees0_.name as name5_0_
from
J_EMP employees0_
where
employees0_.dept=?
I guess its still the same.
And here are the tables:
CREATE TABLE "XYZ"."J_DEPT"
( "ID" NUMBER(*,0) NOT NULL ENABLE,
"NAME" VARCHAR2(255 BYTE) NOT NULL ENABLE,
CONSTRAINT "J_DEPT_PK" PRIMARY KEY ("ID")
)
CREATE TABLE "XYZ"."J_EMP"
( "ID" NUMBER NOT NULL ENABLE,
"NAME" VARCHAR2(255 BYTE),
"DEPT" NUMBER NOT NULL ENABLE,
CONSTRAINT "J_EMP_PK" PRIMARY KEY ("ID"))
here is the code -i'm pasting here as it is :
#Entity
#Table(name = "J_DEPT")
public class Department {
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "mySeq")
#SequenceGenerator(name = "mySeq", sequenceName = "CNTRY_SEQ")
#Id
private Long id;
#Column(name = "name")
private String deptNameInDeptEntity;
#OneToMany(mappedBy = "deptNameInEmp")
Set<Employee> employees;
public Set<Employee> getEmployees() {
return employees;
}
}
#Entity
#Table(name = "J_EMP")
public class Employee {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "mySeq")
#SequenceGenerator(name = "mySeq", sequenceName = "CNTRY_SEQ")
private Long id;
private String name;
#ManyToOne
#JoinColumn(name = "dept")
private Department deptNameInEmp;
public Employee() {
}
And here is the test case:
#RunWith(SpringJUnit4ClassRunner.class)
#TransactionConfiguration(transactionManager = "transactionManager", defaultRollback = false)
#ContextConfiguration(locations = { "classpath:test-applicationContext.xml" })
#Transactional
public class EmpTest {
#Autowired
private SessionFactory sessionFactory;
#Test
public void testDept() {
final Department find = (Department) sessionFactory.getCurrentSession()
.get(Department.class, Long.parseLong("1"));
System.out.println("find res = " + find);
}
}
Probably because of this part:
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "mySeq")
#SequenceGenerator(name = "mySeq", sequenceName = "CNTRY_SEQ")
private Long id;
#Id
#Column(name = "name")
private String deptNameInDeptEntity;
There is something wrong here, the GeneratedValue cannot be applied to a non PK. Did you mean:
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "mySeq")
#SequenceGenerator(name = "mySeq", sequenceName = "CNTRY_SEQ")
private Long id;
#Column(name = "name")
private String deptNameInDeptEntity;
If not, and if this was intentional, please explain your goal and show your table(s).
Update: I can't reproduce the problem. I copy pasted the code you provided and here is the query I get:
select
employee37x0_.id as id135_,
employee37x0_.dept as dept135_,
employee37x0_.name as name135_
from
J_EMP employee37x0_
where
employee37x0_.id=?
Works as expected.
A Google search on "hibernate duplicate aliases" reveals some (old) issues so I don't exclude anything but I couldn't find any proof of recent existing problems. Can you provide a test case (using an embedded database)?