How to create foreign key in Hibernate on Integer column - java

I have an entity in Java and I would like Hibernate to create a foreign key from an Integer field (since I don't have an object reference):
#Entity
public class Invoice {
...
#Column(nullable = true)
private Integer generatedBy;
...
I think I'd like to do something like this with an attribute:
#ForeignKey(name="FK_Invoice_GeneratedBy", references="UserTable.UserId")
#Column(nullable = true)
private Integer generatedBy;
What is the best way to achieve this? I would preferably not have to maintain these relationships in a separate file (if possible).

There doesn't seem to be a solution to this, thus accepting this as an answer.

There is a way to do it, but it is not very nice...
You can have your integer attribute, AND an object attribute mapped this way:
#Column(ame = "GENERATED_BY", nullable = true)
private Integer generatedBy;
#ForeignKey(name="FK_Invoice_GeneratedBy")
#JoinColumn(name = "GENERATED_BY", nullable = false, updatable = false, insertable = false)
private User generatedByUser;
You may keep no external access to your generatedByUser field, it will only show hibernate that there is a relationship. You can set the Integer field at will, when you load this object from DB later you'll have your user reference.
Again, not very pretty, but can be useful sometimes.

Related

Hibernate Envers - select "all" changes for entity ID

I have problem with Hibernate Envers.
I have classes like:
#Entity
#Table(name = "REVINFO")
#RevisionEntity(MyRevisionEntityListener.class)
public class RevEntity {
#Id
#RevisionNumber
#Column(name = "REV", nullable = false, updatable = false)
private Integer id;
#RevisionTimestamp
#Column(name = "REVTSTMP", nullable = false, updatable = false)
private Date timestamp;
#Column(name = "MODIFIED_BY", length = 100)
private String modifiedBy;
#Column(name = "COMMENT", length = 100)
private String comment;
public class MyRevisionEntityListener implements RevisionListener {
#Override
public void newRevision(Object revisionEntity) {
RevEntity a = (RevEntity) revisionEntity;
a.setComment("Some value");
}
}
How can i select every change for entity ID and their "REVINFO" object?
I've got something like this:
List resultList = AuditReaderFactory.get(entityManager)
.createQuery()
.forRevisionsOfEntityWithChanges(ClientType.class, true)
.add(AuditEntity.id().eq(entityId))
.getResultList();
And it's almost work good. I received every "change" but REVINFO looks strange. All fields are null - and there are 1 more object $$_hibernate_interceptor which actually hold "information" but i cannot acces it via code (or i dont know how). See example at the image.
So my question is:
1 - How can i get REVINFO values ?
2 - Do i realy have to use entityManager, or can it be achived with different approach ?
Edit 2:
Correct me if i am wrong, but does forRevisionsOfEntityWithChanges works as Lazy Initialization? I mean, if i try to receive for example modifiedBy field i actually get my data. Debugger log make me confused.
The call to forRevisionsOfEntityWithChanges returns an object array that contains:
Entity instance
Revision Entity
Revision Type
Property names that were changed.
How can i get REVINFO values ? 2 - Do i realy have to use entityManager, or can it be achived with different approach ?
So in your code, to get the revision info attributes, you would do the following. Note that in this code, the type of the revision-info object will depend on your configuration or if you're using a custom revision-info entity class in your deployment. Just be sure to cast it to the proper type.
for (Object entry : resultList) {
final Object[] row = (Object[]) entry;
final TheRevisionEntityClassType revisionInfo = row[1];
// now you can get the revision entity attributes from revisionInfo using getters
}
Correct me if i am wrong, but does forRevisionsOfEntityWithChanges works as Lazy Initialization? I mean, if i try to receive for example modifiedBy field i actually get my data. Debugger log make me confused.
Depending on the query, yes Hibernate may use proxies and its important to understand that in this case, the visual representation you get in the debugger may or may not be accurate depending if the object's internal state gets initialized by the debugger window or not.

QueryDSL, Hibernate, JPA — using .fetchJoin() and getting data in first SELECT, so why N+1 queries after?

I'm trying to query for a list of entities (MyOrders) that have mappings to a few simple sub-entities: each MyOrder is associated with exactly one Store, zero or more Transactions, and at most one Tender. The generated SELECT appears correct - it retrieves all the columns from all four joined tables - but afterwards, two more SELECTs are executed for each MyOrder, one for Transactions and one for Tender.
I'm using QueryDSL 4.1.3, Spring Data 1.12, JPA 2.1, and Hibernate 5.2.
In QueryDSL, my query is:
... = new JPAQuery<MyOrder>(entityManager)
.from(qMyOrder)
.where(predicates)
.join(qMyOrder.store).fetchJoin()
.leftJoin(qMyOrder.transactions).fetchJoin()
.leftJoin(qMyOrder.tender).fetchJoin()
.orderBy(qMyOrder.orderId.asc())
.transform(GroupBy
.groupBy(qMyOrder.orderId)
.list(qMyOrder));
which is executed as:
SELECT myorder0_.ord_id AS col_0_0_,
myorder0_.ord_id AS col_1_0_,
store1_.sto_id AS sto_id1_56_1_, -- store's PK
transactions3_.trn_no AS trn_no1_61_2_, -- transaction's PK
tender4_.tender_id AS pos_trn_1_48_3_, -- tender's PK
myorder0_.ord_id AS ord_id1_39_0_,
myorder0_.app_name AS app_name3_39_0_, -- {app_name, ord_num} is unique
myorder0_.ord_num AS ord_num8_39_0_,
myorder0_.sto_id AS sto_id17_39_0_,
store1_.division_num AS div_nu2_56_1_,
store1_.store_num AS store_nu29_56_1_,
transactions3_.trn_cd AS trn_cd18_61_2_,
tx2myOrder2_.app_name AS app_name3_7_0__, -- join table
tx2myOrder2_.ord_no AS ord_no6_7_0__,
tx2myOrder2_.trn_no AS trn_no1_7_0__,
tender4_.app_name AS app_name2_48_3_,
tender4_.ord_num AS ord_num5_48_3_,
tender4_.tender_cd AS tender_cd_7_48_3_,
FROM data.MY_ORDER myorder0_
INNER JOIN data.STORE store1_ ON myorder0_.sto_id=store1_.sto_id
LEFT OUTER JOIN data.TX_to_MY_ORDER tx2myOrder2_
ON myorder0_.app_name=tx2myOrder2_.app_name
AND myorder0_.ord_num=tx2myOrder2_.ord_no
LEFT OUTER JOIN data.TRANSACTION transactions3_ ON tx2myOrder2_.trn_no=transactions3_.trn_no
LEFT OUTER JOIN data.TENDER tender4_
ON myorder0_.app_name=tender4_.app_name
AND myorder0_.ord_num=tender4_.ord_num
ORDER BY myorder0_.ord_id ASC
which is pretty much what I'd expect. (I cut out most of the data columns for brevity, but everything I need is SELECTed.)
When querying an in-memory H2 database (set up with Spring's #DataJpaTest annotation), after this query executes, a second query is made against the Tender table, but not Transaction. When querying a MS SQL database, the initial query is identical, but additional queries happen against both Tender and Transaction. Neither makes additional calls to load Store.
All the sources I've found suggest that the .fetchJoin() should be sufficient (such as Opinionated JPA with Query DSL; scroll up a few lines from the anchor) and indeed if I remove them, the initial query only selects columns from MY_ORDER. So it appears that .fetchJoin() does force generation of a query that fetches all the side tables in one go, but for some reason that extra information isn't being used. What's really weird is that I do see the Transaction data being attached in my H2 quasi-unit test without a second query (if and only if I use .fetchJoin() ) but not when using MS SQL.
I've tried annotating the entity mappings with #Fetch(FetchMode.JOIN), but the secondary queries still fire. I suspect there might be a solution involving extending CrudRepository<>, but I've had no success getting even the initial query correct there.
My primary entity mapping, using Lombok's #Data annotations, other fields trimmed out for brevity. (Store, Transaction, and Tender all have an #Id a handful of simple numeric and string field-column mappings, no #Formulas or #OneToOnes or anything else.)
#Data
#NoArgsConstructor
#Entity
#Immutable
#Table(name = "MY_ORDER", schema = "Data")
public class MyOrder implements Serializable {
#Id
#Column(name = "ORD_ID")
private Integer orderId;
#NonNull
#Column(name = "APP_NAME")
private String appName;
#NonNull
#Column(name = "ORD_NUM")
private String orderNumber;
#ManyToOne
#JoinColumn(name = "STO_ID")
private Store store;
#OneToOne
#JoinColumns({
#JoinColumn(name = "APP_NAME", referencedColumnName = "APP_NAME", insertable = false, updatable = false),
#JoinColumn(name = "ORD_NUM", referencedColumnName = "ORD_NUM", insertable = false, updatable = false)})
#org.hibernate.annotations.ForeignKey(name = "none")
private Tender tender;
#OneToMany
#JoinTable(
name = "TX_to_MY_ORDER", schema = "Data",
joinColumns = { // note X_to_MY_ORDER.ORD_NO vs. ORD_NUM
#JoinColumn(name = "APP_NAM", referencedColumnName = "APP_NAM", insertable = false, updatable = false),
#JoinColumn(name = "ORD_NO", referencedColumnName = "ORD_NUM", insertable = false, updatable = false)},
inverseJoinColumns = {#JoinColumn(name = "TRN_NO", insertable = false, updatable = false)})
#org.hibernate.annotations.ForeignKey(name = "none")
private Set<Transaction> transactions;
/**
* Because APP_NAM and ORD_NUM are not foreign keys to TX_TO_MY_ORDER (and they shouldn't be),
* Hibernate 5.x saves this toString() as the 'owner' key of the transactions collection such that
* it then appears in the transactions collection's own .toString(). Lombok's default generated
* toString() includes this.getTransactions().toString(), which causes an infinite recursive loop.
* #return a string that is unique per order
*/
#Override
public String toString() {
// use appName + orderNumber since, as they are the columns used in the join, they must (?) have
// already been set when attaching the transactions - primary key sometimes isn't set yet.
return this.appName + "\00" + this.orderNumber;
}
}
My question is: why am I getting redundant SELECTs, and how can I not do that?
I'm a little too late on the answer, but today the same problem happened to me. This response might not help you, but at least it would save someone the headache we went through.
The problem is on the relations between the entities, not in the query. I tried with QueryDSL, JPQL, and even native SQL but the problem was always the same.
The solution was to trick JPA into believing that the relations were there via annotating the child classes with #Id on those joined fields.
Basically you'll need to set Tender's id like this and use it from MyOrder like if it was a normal relationship.
public class Tender {
#EmbeddedId
private TenderId id;
}
#Embeddable
public class TenderId {
#Column(name = "APP_NAME")
private String appName;
#Column(name = "ORD_NUM")
private String orderNumber;
}
The same would go for the Transaction entity.

JPA foreign key zero 0 value hibernate TransientPropertyValueException

I have found that hibernate (or mariadb) JPA does not seem to work with 0 values in a foreign key.
I have a parent class
class Parent {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "PARENT_ID", unique = true, nullable = false)
private Integer parentId;
}
And a child class
class Child {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "CHILD_ID", unique = true, nullable = false)
private Integer childId;
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "PARENT_ID", nullable = false)
private Parent parent;
}
So we have 2 rows in parent. parent_id=0 and parent_id=1
My problem is that I get an error when attempting to use parent with ID 0. i.e. This code
Parent p = entityManager.find(Parent.class, new Integer(0));
Child c = new Child();
c.setParent(p);
entityManager.persist(c);
Will fail with the error:
java.lang.IllegalStateException:
org.hibernate.TransientPropertyValueException: Not-null property
references a transient value - transient instance must be saved before
current operation : com.whatever.Child.parent -> com.whatever.Parent
But the following works fine:
Parent p = entityManager.find(Parent.class, new Integer(1));
Child c = new Child();
c.setParent(p);
entityManager.persist(c);
So I assume the PARENT_ID=0 somehow confusing JPA into thinking it is not a valid parent object.
Or is this actually a mariadb issue? Related to the fact that you have to change a session setting in order to insert 0's into AUTO_INCREMENT columns.
Is there any config or annotation I can do to make this work. Unfortunately we are putting JPA code on an existing system, so changing the PARENT_ID values is not a trivial task. (and everybody hates data conversion).
Any tips very much appreciated.
MariaDB/MySQL handle AUTO_INCREMENT thus: Numbers are 1 or greater; 0 is a valid sequence number. If JPA cannot live with those (and some other) limitations, JPA is broken. (Sorry, but I get irritated with 3rd party software that makes life difficult for MySQL users.)

JPA - Generated Id from Sequence is always 0

I'm trying to use JPA to generate IDs from sequences in my database (Oracle 9i)
From what I found here and there, here is the group of annotations I've set on my ID variable :
#Id
#SequenceGenerator(name="PROCEDURENORMALE_SEQ_GEN", sequenceName = "PROCEDURENORMALE_SEQ")
#GeneratedValue(strategy=GenerationType.SEQUENCE, generator = "PROCEDURENORMALE_SEQ_GEN")
#Column(name = "IDPROCEDURENORMALE", unique = true, nullable = false, precision = 10, scale = 0)
private long idProcedureNormale;
However, whenever I create a new object, this id is always set to 0, and because of that I can't persist data. I've tried to change the strategy from GenerationType.SEQUENCE to GenerationType.AUTO, nothing changed. For this specific table, Sequence number is supposed to be around 8300.
Where did I go wrong ?
I actually solved my issue, that happened not to be directly related with what I exposed.
This object I was trying to persist is part of a relatively complex object, and in the parent object I didn't add a CascadeType to the JPA mapping annotation :
#OneToMany(fetch = FetchType.LAZY, mappedBy = "dossier")
private Set<Procedurenormale> proceduresNormales = new HashSet<>(0);
Changing this annotation to the following solved the issue :
#OneToMany(fetch = FetchType.LAZY, mappedBy = "dossier", cascade = CascadeType.ALL)
private Set<Procedurenormale> proceduresNormales = new HashSet<>(0);

Hibernate - unique column constraint being ignored

I have a MySQL table to hold tags (i.e. like those used here on Stack Overflow). It simply has an id (pk) and a tag column to hold the tag itself.
The annotated get methods for my Tag entity are shown below.
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id", unique = true, nullable = false)
public int getId() {
return this.id;
}
#Column(name = "tag", unique = true, nullable = false)
public String getTag() {
return this.tag;
}
I am using a unique column constraint on tag as there should never be more than one row for a given tag. However, Hibernate appears to be ignoring this, i.e. I can save the exact same tag many times and it simply creates a new row rather than throwing an exception.
Am I missing something or should this be working?
From JavaDoc of UniqueConstraint (unique=true on #Colunm is just a shortcut):
This annotation is used to specify that a unique constraint is to be included in the generated DDL for a primary or secondary table.
So it does not seem to enforce uniqueness upon inserts. You should create a unique constraint in the database in any case.
You miss that this is only a information.
You should add also constraint on the column in database.

Categories