#OneToOne-Relation with "is not null" - java

I'm using JPA2, Unitils, jUnit + other stuff.
My problem concerns two entities:
#Entity
public class CaseStuff implements Serializable {
....
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long caseStuffId;
#OneToOne(mappedBy = "CaseStuff")
private XCase xcase;
...
}
#Entity
public class XCase implements Serializable {
....
#OneToOne
#JoinColumn(name = "CASESTUFFID")
private CaseStuff caseStuff;
....
}
Both tables got the ID of the other table, so it could also be mapped the other way around.
In a jUnit-Test, Unitils inserts one CaseStuff record with no XCase. I confirmed, that it really is null.
BUT, then I use the following query:
"select s from CaseStuff s where s.xcase is null"
and it returns 0 CaseStuff objects. Doing the same with "is not null" returns the object, but when inspecting CaseStuff.xcase while debugging, it is clearly null.
Any idea, what's going awry here?
EDIT:
The SQL generated by Hibernate translates to
select
*
from
CaseStuff casestuff0_
where
(
casestuff0_.xcaseid is null
)
*Replaced all field names by *
EDIT2:
Before I changed it to a OneToOne-Relation, it was an unneccessary ManyToOne.
Previously the jUnit test had the query
"select s from CaseStuff s where not exists (select x from s.xcase x)"
This still works correctly for some reason, as if s.xcase was still a Set.
Thanks to all who try to figure it out with me.

As you defined your entities Xcase will have the foreign key
So Xcase table beside it's id will have caseStuff_id (or other name) as foreign key to the Xcase id.
Like in this image (make abstraction of the names)
In your case Person is Xcase and CaseStuff is PersonDetails
So probably you CaseStuff table (if it was generated by the persistence provider does not have a case id)
Please check on the db level to see how your tables are defined, structurally speaking.

Related

Hibernate join-table two composite keys that are both foreign keys

I have two different tables, both of which have composite embedded keys. Both composite keys have in composition the same id A_ID.
I want to join table M with table D in a one to many relationship using a join-table.
The following are some pseudo-java code converted from XML ORM mappings. So please excuse any mistakes written here. The mappings in the final code work so the typos here are not to blame.
#Entity()
public class M {
#EmbeddedId()
private EmbeddedMId id;
#OneToMany(name="d", #JoinTable(name="M-D",
joinColumns={
#JoinColumn(name="M_ID", referencedColumnName="M_ID"),
#JoinColumn(name="A_ID", referencedColumnName="A_ID", table="M")
},
inverseJoinColumns={
#InverseJoinColumn(name="D_ID", referencedColumnName="D_ID"),
#InverseJoinColumn(name="A_ID", referencedColumnName="A_ID", table="D", insertable="false", updatable="false")
}
))
private Set<D> dSet;
}
#Embeddable()
public class EmbeddedMId {
#Basic() private String A_ID;
#Basic() private String M_ID;
}
#Embeddable()
public class EmbeddedDId {
#Basic() private String A_ID;
#Basic() private String D_ID;
}
As you can see, the embeddables both use A_ID therefore we tried to make the 2nd A_ID in the join-table be readonly. The application starts and the mappings seem to be okay.
The problem is whenever I want to insert a new D object in the M entity, hibernate throws an SQL error invalid column index because while the prepared statement is correct as seen bellow, hibernate only provides the first 2 parameters instead of all three. (Values provided by hibernate are (VALID_M_ID, VALID_A_ID) instead of providing 3 values)
INSERT INTO M_D("M_ID", "A_ID", "D_ID") VALUES (?, ?, ?)
If I rename the 2nd inverseJoinColumn to have a new column name and make it insertable/updatable, the problem is solved. But this means that the A_ID is duplicated in both A_ID and A_REPEAT_ID column and this is what I want to avoid.
#InverseJoinColumn(name="A_REPEAT_ID", referencedColumnName="A_ID", table="D")
Is there a way to tell Hibernate that my EmbeddedDId needs to be mapped over the D_ID and A_ID (readonly) correctly when doing the insertions?
I hope my explanation is clear enough, but feel free to ask for any clarifications.
Hibernate version is 5.2.17-FINAL
EDIT
The only other entity that is important in this case is pretty simple. But as requested I'll write it here
#Entity()
public class D {
#EmbeddedId()
private EmbeddedDId id;
/* other basic fields here */
}
I don't think insertable = false, updatable = false does what you want here. If you want the target column A_ID on D to be readonly, then you will have to map the column in the target entity D and specify there that the column is insertable = false, updatable = false but not on this association.

hibernate h2 embeddable list expected "identifier"

I'm trying to associate a list of function (whom Embeddable) within my Employee Entity and H2 seems unhappy with this saying that it expected an "identifier"
Caused by: org.h2.jdbc.JdbcSQLException: Syntax error in SQL statement "
CREATE TABLE EMPLOYEE_FUNCTIONS (
EMPLOYEE_EMPLOYEEID VARCHAR(255) NOT NULL,
ACTIVE BOOLEAN NOT NULL,
DEPARTMENTNUMBER INTEGER NOT NULL,
DESCRIPTION VARCHAR(255),
ORDER[*] INTEGER NOT NULL
) "; expected "identifier";
The thing is I already done that with an other project and I don't see why it doesn't work.
Employee.java
#Entity
public class Employee extends AbstractScheduleEntity<EmployeeSchedule> {
public static final String ACOMBA_UNIQUE_FIELD = "acombaUnique";
#Id
#GenericGenerator(name = "sequence_id", strategy =
"ca.tecsar.core.sql.ServerSequenceGenerator")
#GeneratedValue(generator = "sequence_id")
#Column(name = "EmployeeID", unique = true, nullable = false)
private String employeeID;
#ElementCollection
private List<Function> functions;
//getter and setter
}
Function.java
#Embeddable
public class Function implements Serializable {
private int order;
private boolean active;
private String description;
private int departmentNumber;
//getter and setter
}
I removed a few properties in Employee that wasn't necessary.
What may cause this error? Is it because I have a String as identifier in my Employee? If so how can I tell to Hibernate to add Employee_EmployeeID as identifier?
Thanks
Turns out I was being dumb and named a column "Order". Wonder why H2 wasn't happy :upside_down:
Changed the variable name to something else and it worked!
I have the same problem while naming the fields: private String to and private String from , changed to ex. dayTo , dayFrom , and it worked.
Got same issue with Order as entity. Changed table name to "orderz" and goes on.
I had the same problem with Spring and H2 database for tests, My entity had the field name "interval", I renamed to "inter" and resolved the problem.
So, these errors happen due to a sql reserved names in entities.
I was facing a similar issue because of a field in my entity called "interval".
The answers to this post help me identify the root cause, however changing the name of the field was not a solution for me because "interval" was perfect for my class.
The solution I used was to rename the table column by using hibernate annotation.
#Column(name = "FREQUENCY_INTERVAL")
private int interval;
This helped me keep the variable name as 'interval' while mapping to a column name that is acceptable by the database.
I also had an issue with an entity field called row as it is also a keyword
I specified a Column name ("SEAT_ROW") to get around it.
#Entity
data class Seat (
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
val id : Long,
#Column(name="SEAT_ROW")
val row : Char,
val num : Int,
val price : BigDecimal,
val description : String
) {
override fun toString(): String = "Seat $row-$num $$price ($description)"
}
H2 Compatibility and Keywords Table
I was using order as table name in h2. Changed to something like _order and I get it!
As you can see here, order is a reserved word by h2.
try to put the ; before the " and test ?
in my case problem cause was incorrect syntax in insert statement
problem :
insert into 'table name missing' (column names...) values(values...);
after adding table name
fix:
insert into 'table name specified' (column names...) values(values...);
some times trivial mistakes are hard to spot :)
I had the somewhat same problem but instead mine was missing a semi-colon.

EntityNotFoundException LEFT JOIN JPA - Hibernate

Exception in thread "main" javax.persistence.EntityNotFoundException:
Unable to find CNPJ with id 00001388000307
I was reading jpa documentation, i read that this exception is thrown when it try to accesse(by the method getReference of EntityManger interface) and the entity doesn't exists
Thrown by the persistence provider when an entity reference obtained by EntityManager.getReference is accessed but the entity does not exist.
I have this entitys: Salesman e CNPJ. It's possible that exists many salesmen with the same CNPJ, in other words, a relationship #ManyToOne.
This relationship is working, OK.
But, when i try to execute the query
select r from Salesman r join fetch r.yearMonth left join fetch r.cnpj
to bring the salesmen with its yearMonth(it's working!) relationship and its CNPJ relationship, throws the Exception, when i try to do a LEFT JOIN, that i mentioned.
When i don't execute a LEFT JOIN, works great, all Salesmen with his CNPJs and nothin of Exceptions, BUUUUT, some salesmen don't have CNPJ nad i have to bring them too, there's a necessity to do a LEFT JOIN.
#Entity
public class Salesman{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
#JoinColumn(name = "year_month")
#ManyToOne
private YearMonth yearMonth;
private String cnpjS;
#JoinColumn(name = "cnpj")
#ManyToOne
private CNPJ cnpj;
public AnoMes getYearMonth() {
return yearMonth;
}
public CNPJ getCnpj() {
return cnpj;
}
}
#Entity
public class CNPJ {
#Id
#Column(name = "CCG", length = 14)
private String ccg;
public String getCcg() {
return ccg;
}
}
The select generated by Hibernate:
select *
from salesman s
inner join yearmonth y on s.ano_mes = y.id
left outer join cnpj c on s.cnpjS = c.CCG
This consult returns this values, rcnpj is cnpj from Salesman and bcnpj is from CNPJ. Some Salesmen came with CNPJ NULL, i think that it's the problem. I don't think so.
Try to enable hibernate sql logging with <property name="show_sql">true</property> to see how real query (native, invoked on db) looks like, maybe there is a inner join after hibernate processing.
To change it you can try #Column(nullable=true) annotation for your relationship or #Fetch(FetchMode.SELECT) to see how real query will change.
The reason is that you have a reference to cnpj with ID 00001388000307, but such row of cnpj doesn't exist.
You can try to use #NotFound(action=NotFoundAction.IGNORE) to skip such error, but it is recommended to fix your database of handle a exception.
You are using the cnpj column on the table Salesman as both a foreign key and a value column. In that case #staszko032's suggestion using #NotFound(action=NotFoundAction.IGNORE) looks like will work as you expect
But I think you should refactor your model if possible. You could create a CNPJ entity always, in that case you won't have Salesman without a realation to CNPJ and could use INNER JOIN, or use 2 columns, one that will be used when the Salesman doesn't have a concrete related CNPJ (the value as string) and the other when it has (the foreign key)

JPA One To One relationship between two columns (not PK and not FK)

I have two tables, one named "Category" and the other "Rule" that are related logically with One to One relationship using a field (code) different than the Primary Key (PK) of table and not phisically managed with a Foreign Key (FK):
CATEGORY
ID (PK) NUMBER
COD_RULE VARCHAR
NAME VARCHAR
.....
RULE
ID (PK) NUMBER
CODE VARCHAR
TYPE VARCHAR
.....
I haven't on Rule table FK to category ID but only unique constraint (the relation is 1 to 1)
Implemented in this way in JPA
public Category implement Serializable {
#Id
#Column (name="ID")
private Long id;
#NotNull
#OneToOne(cascade=CascadeType.ALL)
#JoinColumn(name="CODE_RULE" referencedColumnName="CODE", nullable=false)
private Rule;
#Column (name="NAME")
private String name;
//Getter and Setter methods
.......
}
public Rule implement Serializable {
#Id
#Column (name="ID")
private Long id;
#NotNull
#Column (name="CODE")
private String code;
#Column (name="TYPE")
private String type;
//Getter and Setter methods
.......
}
I need to:
When retrieve Categories obtain also all informations of associated Rule
SELECT c.*, r.type FROM Category c LEFT OUTER JOIN Rule r WHERE c.CODE_RULE = r.CODE
When edit Category maintain aligned CODE_RULE with CODE, so If I change CODE_RULE I would yo change automatically the CODE on Rule
UPDATE Category SET COD_RULE='5A', NAME='Test' WHERE ID=1
UPDATE Rule SET CODE='5A' WHERE CODE='AB'
I see on the specification that:
There are three cases for one-to-one associations: either the
associated entities share the same primary keys values, a foreign key
is held by one of the entities (note that this FK column in the
database should be constrained unique to simulate one-to-one
multiplicity), or a association table is used to store the link
between the 2 entities (a unique constraint has to be defined on each
fk to ensure the one to one multiplicity).
But with this implementation satisfy point 1. But not the point 2.
Suppose that I've already created Category (on ID = 1) and associated rule, when I edit category (having CODE_RULE = CODE = "AB") and change the code to "5A":
#PersistentContext
private EntityManager em;
.......
Category cat = em.find(Category.class, 1L);
cat.setName("Test");
cat.getRule().setCode("5A");
em.merge(cat);
I see that the code has been updated on Rule but not in Category:
BEFORE EDIT
Category (ID, COD_RULE, NAME) --> (1, AB, First Category)
Rule (ID, CODE, TYPE) --> (10, AB, C)
AFTER EDIT
Category (ID, COD_RULE, NAME)--> (1, AB, Test)
Rule (ID, CODE, TYPE) --> (10, 5A, C)
How can I do this work in JPA?
Is this type of operation supported in the JPA specification?
Is there an alternative (i.e. I have to merge before Rule and then Category)?
From your datamodel, it looks more like a Many To One relationship between Category and Rule, given in your data model only restrict each Category can refer to [0..1] Rule, but not restricting how many Categories that a Rule can be referred by.
Based on your comment, it seems that you can change the data model. Normally if it is a ~ToOne relationship, you should have the referring side referring as FK, which looks like this:
(Tables)
CATEGORY (
CATEGORY_ID NUMBER PK,
CATEGORY_CODE VARCHAR, // Unqiue
RULE_ID NUMBER FK to RULE,
... (Don't refer by RULE_CODE!!)
)
RULE (
RULE_ID NUMBER PK,
RULE_CODE VARCHAR, // unique, can be updated
...
)
Entity should look like
class Category {
#Id #Column(name="CATEGORY_ID)
Long id;
#ManyToOne // or #OneToOne if you really insist
#JoinColumn(name="RULE_ID)
Rule rule;
)
(class Rule is straight-forward, I will skip)
The HQL you mentioned should be
// When retrieving Category together with Rule
from Category c join fetch c.rule
for Point 2, as you mentioned in comment, you are trying to align Rule's code with Category's code, when Category's code is updated. This should be implemented as:
class Category {
//.....
public void setCode(String code) {
this.code = code;
this.rule.setCode(code);
}
//....
)
Base on personal experience, when using JPA, life will be much easier to drive data model base on domain model design. It should save a lot of problem caused by "data-model that looks tolerable".

JPA Mapping Multi-Rows with ElementCollection

I'm trying to follow the JPA tutorial and using ElementCollection to record employee phone numbers:
PHONE (table)
OWNER_ID TYPE NUMBER
1 home 792-0001
1 work 494-1234
2 work 892-0005
Short version
What I need is a class like this:
#Entity
#Table(name="Phones")
public class PhoneId {
#Id
#Column(name="owner_id")
long owner_id;
#Embedded
List<Phone> phones;
}
that stores each person's phone numbers in a collection.
Long version
I follow the tutorial code:
#Entity
#Table(name="Phones")
public class PhoneId {
#Id
#Column(name="owner_id")
long owner_id;
#ElementCollection
#CollectionTable(
name="Phones",
joinColumns=#JoinColumn(name="owner_id")
)
List<Phone> phones = new ArrayList<Phone>();
}
#Embeddable
class Phone {
#Column(name="type")
String type = "";
#Column(name="number")
String number = "";
public Phone () {}
public Phone (String type, String number)
{ this.type = type; this.number = number; }
}
with a slight difference that I only keep one table. I tried to use the following code to add records to this table:
public static void main (String[] args) {
EntityManagerFactory entityFactory =
Persistence.createEntityManagerFactory("Tutorial");
EntityManager entityManager = entityFactory.createEntityManager();
// Create new entity
entityManager.getTransaction().begin();
Phone ph = new Phone("home", "001-010-0100");
PhoneId phid = new PhoneId();
phid.phones.add(ph);
entityManager.persist(phid);
entityManager.getTransaction().commit();
entityManager.close();
}
but it keeps throwing exceptions
Internal Exception: org.postgresql.util.PSQLException: ERROR: null
value in column "type" violates not-null constraint Detail: Failing
row contains (0, null, null). Error Code: 0 Call: INSERT INTO Phones
(owner_id) VALUES (?) bind => [1 parameter bound] Query:
InsertObjectQuery(tutorial.Phone1#162e295)
What did I do wrong?
Sadly, i think the slight difference that you only keep one table is the problem here.
Look at the declaration of the PhoneId class (which i would suggest is better called PhoneOwner or something like that):
#Entity
#Table(name="Phones")
public class PhoneId {
When you declare that a class is an entity mapped to a certain table, you are making a set of assertions, of which two are particularly important here. Firstly, that there is one row in the table for each instance of the entity, and vice versa. Secondly, that there is one column in the table for each scalar field of the entity, and vice versa. Both of these are at the heart of the idea of object-relational mapping.
However, in your schema, neither of these assertions hold. In the data you gave:
OWNER_ID TYPE NUMBER
1 home 792-0001
1 work 494-1234
2 work 892-0005
There are two rows corresponding to the entity with owner_id 1, violating the first assertion. There are columns TYPE and NUMBER which are not mapped to fields in the entity, violating the second assertion.
(To be clear, there is nothing wrong with your declaration of the Phone class or the phones field - just the PhoneId entity)
As a result, when your JPA provider tries to insert an instance of PhoneId into the database, it runs into trouble. Because there are no mappings for the TYPE and NUMBER columns in PhoneId, when it generates the SQL for the insert, it does not include values for them. This is why you get the error you see - the provider writes INSERT INTO Phones (owner_id) VALUES (?), which PostgreSQL treats as INSERT INTO Phones (owner_id, type, number) VALUES (?, null, null), which is rejected.
Even if you did manage to insert a row into this table, you would then run into trouble on retrieving an object from it. Say you asked for the instance of PhoneId with owner_id 1. The provider would write SQL amounting to select * from Phones where owner_id = 1, and it would expect that to find exactly one row, which it can map to an object. But it will find two rows!
The solution, i'm afraid, is to use two tables, one for PhoneId, and one for Phone. The table for PhoneId will be trivially simple, but it is necessary for the correct operation of the JPA machinery.
Assuming you rename PhoneId to PhoneOwner, the tables need to look like:
create table PhoneOwner (
owner_id integer primary key
)
create table Phone (
owner_id integer not null references PhoneOwner,
type varchar(255) not null,
number varchar(255) not null,
primary key (owner_id, number)
)
(I've made (owner_id, number) the primary key for Phone, on the assumption that one owner might have more than one number of a given type, but will never have one number recorded under two types. You might prefer (owner_id, type) if that better reflects your domain.)
The entities are then:
#Entity
#Table(name="PhoneOwner")
public class PhoneOwner {
#Id
#Column(name="owner_id")
long id;
#ElementCollection
#CollectionTable(name = "Phone", joinColumns = #JoinColumn(name = "owner_id"))
List<Phone> phones = new ArrayList<Phone>();
}
#Embeddable
class Phone {
#Column(name="type", nullable = false)
String type;
#Column(name="number", nullable = false)
String number;
}
Now, if you really don't want to introduce a table for the PhoneOwner, then you might be able to get out of it using a view. Like this:
create view PhoneOwner as select distinct owner_id from Phone;
As far as the JPA provider can tell, this is a table, and it will support the queries it needs to do to read data.
However, it won't support inserts. If you ever needed to add a phone for an owner who is not currently in the database, you would need to go round the back and insert a row directly into Phone. Not very nice.

Categories