I have following POJOs:
class Month {
long id;
String description;
List<Day> days; // always contains 29, 30 or 31 elements
}
class Day {
byte nr; // possible values are 1-31
String info;
}
Is there a way to store these objects into following DB structure using JPA+Hibernate:
Table MONTHS:
id;description;
Table DAYS:
id-of-month;nr-of-day;info;
Any better solution for this situation?
If you can't change your pojo's or table structure you are a bit screwed. If you can then a simple annotated pojo will work.
class Month {
#Id
private long id;
private String description;
#OneToMany(mappedBy="month",fetchType=Lazy)
private List<Day> days;
}
---- Surrogate key required DB change for Days table
class Day {
#Id
private int id;
private Month month;
private byte nr; // possible values are 1-31
private String info;
}
Here is one solution I found:
class Month {
long id;
String description;
#CollectionOfElements(fetch = FetchType.EAGER)
#IndexColumn(name = "nr-of-day")
List<Day> days; // always contains 29, 30 or 31 elements
}
#Embeddable
class Day {
byte nr; // possible values are 1-31
String info;
}
#CollectionOfelements and #IndexColumn are Hibernate annotations. If I use #OneToMany annotation available in JPA, hibernate creates 3 tables instead of 2.
My only problem now is that Day.nr is saved twice: first as IndexColumn of the List (0-based counter) and second time as field of class Day (1-based counter).
Can you map a Month #Entity class UNIDIRECTIONAL relationship with Day #Entity class without #Embeddable with CascadeType.PERSIST instead, where the identifier of #Entity Day class is composed by Month identifier and the list index as follow ?
#Entity public class Month {
#Id
#GeneratedValue
private Integer id;
// one way relationship
#OneToMany(cascade=CascadeType.PERSIST)
#JoinColumn(name="MONTH_ID")
#IndexColumn(name="childIndex")
private List<Day> dayList = new ArrayList<Day>();
}
#Entity public class Day {
#EmbeddedId // composed by month foreign key and index column
private DayId id;
}
I hope you solve this problem
Regards
Arthur Ronald F D Garcia (Java programmer)
Natal/Rn - Brazil
Related
I have a parent Table, called "PN", which in it's entity contains a list of another Entity "PnDett", which is related to the first table.
I want to execute a query that will give me the list of PN with my where condition, but that will filter the list of "PnDett" also based on a where condition.
How can i achieve this?
This is the PN mapping:
#Entity
#Table(name = "PN")
public class Pn implements java.io.Serializable {
private static final long serialVersionUID = 2556879508428749494L;
#Id
#Column(name="ID_PN", unique = true, nullable = false)
private BigDecimal idPN;
#Temporal(TemporalType.TIMESTAMP)
#Column(name="DATA_DOC")
private Date dataDoc;
#Temporal(TemporalType.TIMESTAMP)
#Column(name="DATA_COMP_BANC")
private Date dataCompBanc;
#Column(name="STATO_PN")
private String statoPN;
#Column(name="TESTO_TESTATA")
private String testoTestata;
#Temporal(TemporalType.TIMESTAMP)
#Column(name="DATA_INVIO_SAP")
private Date dataInvioSap;
#Temporal(TemporalType.TIMESTAMP)
#Column(name="DATA_INS")
private Date dataIns;
#Column(name="ID_UTENTE_AGG")
private BigDecimal idUtenteAgg;
#Temporal(TemporalType.TIMESTAMP)
#Column(name="DATA_CONTABILE")
private Date dataContabile;
#Temporal(TemporalType.TIMESTAMP)
#Column(name="DATA_REND_INTEGR")
private Date dataRendIntegr;
#Temporal(TemporalType.TIMESTAMP)
#Column(name="DATA_AGG")
private Date dataAgg;
#OneToMany(fetch = FetchType.LAZY, mappedBy = "pn")
private Set<PnDett> pnDetts = new HashSet<>(0);
This is the PnDett mapping:
#Entity
#Table(name = "PN_DETT")
public class PnDett implements java.io.Serializable {
private static final long serialVersionUID = 2556879508428749494L;
#Id
#Column(name="ID_PN_DETT", unique = true, nullable = false)
private BigDecimal idPNDett;
#ManyToOne(fetch=FetchType.LAZY)
#JoinColumn(name="ID_PN", nullable=false)
private Pn pn;
#OneToOne
#JoinColumn(name="ID_DOM_TIPO_SCRITTURA")
private DomTipoScrittura domTipoScrittura;
#Column(name="TIPO_DOCUMENTO")
private String tipoDocumento;
#Column(name="ID_CALCOLO")
private String idCalcolo;
#Column(name="CONTO_COGE")
private String contoCoge;
#Column(name = "IMPORTO_AVERE")
private BigDecimal importoAvere;
#Column(name = "IMPORTO_DARE")
private BigDecimal importoDare;
#OneToOne
#JoinColumn(name="ID_UTENTE")
private Utente utente;
#Column(name = "DATA_INS")
private Date dataIns;
#Column(name = "TIPO_DETTAGLIO")
private String tipoDettaglio;
#Column(name = "SE_CANC")
private Integer seCancellato;
In HQL, i wrote this query "select * from Pn firstNote where firstNote.dataContabile = (a param i put myself, in this case 20 april 2020) and firstNote.pnDetts.seCancellato = (a param i put myself, in this case 0) and firstNote.pnDetts.importoDare <> 0 or firstNote.pnDetts.importoAvere <> 0"
The results are, quite frankly, a mess. I have only two lines in my PN table that have the parameter dataContabile set at 20 april 2020, and yet i get 18 results, and the results are an Object[] which somehow containes both entities. Inside the PN object i do have the pnDetts list filtered to match the date i'm searching them for, but the other filters don't even work. How can i fix this mess? Ideally, my result should be a list of two PN objects that have the pnDetts list filtered, but i don't know how to achieve this.
Edit:
Ok, i've made an SQL query and found out that the PnDett lines related to that date that have either importoDare <> 0 or importoAvere<>0 are exactly 18, that's why i get 18 sets of objects[]. But how can i have just two PN objects, with the list inside filtered instead?
In order to make the filter work, we can make use of fetch, this will make sure that the initial query fetches all the required data.
select * from Pn firstNote join fetch firstNote.pnDetts pnd where firstNote.dataContabile = :param1 and pnd.seCancellato = :param2 and pnd.importoDare <> 0 or pnd.importoAvere <> 0
Like mentioned by Vishnu you can use join fetch, but there are many issues with such an approach. Since the result of the query are managed entities, filtering the collection is problematic. When your transaction finishes, the filtered out elements might be removed so it's important that you immediately detach all entities after such a query e.g. view entityManager.clear().
If you also want Pn objects that have empty pnDetts because nothing matches, you are out of luck with join fetch and have to use a normal join like this:
SELECT firstNote, pnd
FROM Pn firstNote
LEFT JOIN firstNote.pnDetts pnd
ON pnd.seCancellato = :param2 AND pnd.importoDare <> 0
OR pnd.importoAvere <> 0
WHERE firstNote.dataContabile = :param1
This is a scalar query which will return a Object[] so you have to collect the lists manually.
I have hibernate query, where I need to transform one column with date_trunc, however I don't know how to bind this transformed value to my joined entity object
Is there any possibility to pass this transformed column to mine hibernate entity?
Of course it's conceptual code in case it wouldn't make sens for you:
#Entity
class SomeClass {
private ZonedDateTime orderTime;
#ManyToOne(targetEntity = SomeStats.class)
#JoinColumn(name = "SOME_STATS_ID", referencedColumnName="ID")
private SomeStats someStats; // Class that I need aggregate
private long id;
// ... other fields that aren't grouped
}
class SomeStats {
private long id;
private double price; // I need to take average of this
private ZonedDateTime paymentTime; // And truncate this
}
I need some idea to make something similar to:
SELECT someclass.*, AVG(somestats.price), date_trunc('hour', somestats.payment_time) as timebucket
FROM SOME_CLASS someclass
JOIN SOME_STATS somestats ON somestats.id = someclass.some_stats_id
GROUP BY someclass.id, timebucket
but with average as SomeStats.price and truncated date as SomeStats.paymentTime, is it even possible?
I have in my sql server 2016 database three tables as example for an Hibernate application which uses a join table with an additional column to assign articles buyed from an customer with a specific amount. My tables in the DB are:
table_article (a_id int, description varchar(255))
a_id description
1 fish
2 fish
3 water
table_customer (customer_id int, name varchar(255)
customer_id name
1 john
2 jane
3 jack
ac_join_table (a_id, customer_id, amount, pk(a_id, customer_id))
a_id customer_id amount
1 2 10
2 1 3
2 3 7
2 2 18
3 1 5
So customer jane with id 2 buys 10 fishes. If I run my programm it looks like the table columns from the join table were interpreted different or exchanged. Because if I run the following code jane buys 3 fishes, 18 fishes and 7 fishes. It looks like the article_id is interpreted as customer_id if I run:
// should return that jane (customer_id: 2) buys 10 fishes (a_id: 1, amount: 10)
// but it interprets a_id as customer_id so jane buys 3, 7 and 18 fishes...
Cust c = sessionObj.get(Cust.class, 2);
List<ArtCustJoin> articles = c.getArtikel();
for (ArtCustJoin artCustJoin : articles) {
System.out.println(artCustJoin.getArt().getDescription());
}
My Java classes for the Hibernate model are:
Art.java for table_article
#Entity
#Table(name="table_article")
public class Art {
#Id
#Column(name = "a_id")
private int article_id;
#Column
private String description;
// …
#OneToMany(mappedBy="ku")
private List<ArtCustJoin> customers;
// …
// getters and setters
// ...
}
Cust.java for table_customer
#Entity
#Table(name="table_customer")
public class Cust {
#Id
private int customer_id;
#Column
private String name;
#OneToMany(mappedBy="art")
private List<ArtCustJoin> artikel;
// getter + setter
// ...
}
ArtCustJoin.java for the join table ac_join_table. The join table has an additional column amount
#Entity
#Table(schema="dbo", name="ac_join_table")
public class ArtCustJoin {
// Use Compound Key instead of single primitive key
#EmbeddedId
CompositeKey id;
#ManyToOne
#JoinColumn(name="a_id", columnDefinition="int", foreignKey = #ForeignKey(name = "FK_ARTID"))
#MapsId("article_id") // maps to attribute with this name in class Artikel
private Art art;
#ManyToOne
#JoinColumn(name="customer_id", foreignKey = #ForeignKey(name = "FK_CUSTID"))
#MapsId("customer_id") // maps to attribute with this name in class Kunde
private Cust ku;
#Column
private int amount;
public CompositeKey getId() {
return id;
}
public void setId(CompositeKey id) {
this.id = id;
}
// getter + setter
// ...
}
And a class for the composite key:
#Embeddable
public class CompositeKey implements Serializable{
#Column
private Integer a_id;
#Column
private Integer customer_id;
public CompositeKey() {}
// ...
}
I dont know where the error is that column a_id is interpreted as customer_id...
I'm no java developer but both #OneToMany(mappedBy= seem weird and look like the reason of the issue. Isn't Customer supposed to be mapped to ArtCustJoin by customer_id? instead of #OneToMany(mappedBy="art"). The same applies to Art class.
I've read that OneToOne mapping is "relationship in Java is where the source object has an attribute that references another target object and (if) that target object had the inverse relationship back to the source object it would also be a OneToOne relationship."
source: http://en.wikibooks.org/wiki/Java_Persistence/OneToOne
Based on above, I assume that one table can reference another with cardinality equal to (zero) one, and this one can inverse reference first table with this same cardinality (zero) one.
So, I have created this simple entities (Cust can have one Adress, and Adress can have one Cust)
#Entity
public class Cust {
#Id
#GeneratedValue
private Long id;
private String desc;
#OneToOne(fetch = FetchType.LAZY, cascade={CascadeType.PERSIST, CascadeType.REMOVE})
#JoinColumn(name="adress_fk")
private Adress adress;
//getters, setters...
#Entity
public class Adress {
#Id
#GeneratedValue
private Long id;
private String val1;
private String val2;
#OneToOne(mappedBy = "adress")
private Cust b;
//getters, setters...
I was curious what will happen when I'll try to persist two custs with this same adress.
I wrote sample code to give a try:
Adress thisSameAddress = new Adress();
thisSameAddress.setVal1("blabla");
thisSameAddress.setVal2("nlanla");
Cust b = new Cust();
b.setAdress(thisSameAddress );
b.setDesc("asdasd");
Cust c = new Cust();
c.setAdress(thisSameAddress );
c.setDesc("eeee");
tx.begin();
em.persist(b);
em.persist(c);
tx.commit();
I was expecting some exception when trying to persist two custs with this same address. However, code ran and in database I can see one Adress and Two Custs:
SELECT * FROM CUST;
ID DESC ADRESS_FK
1 asdasd 1
2 eeee 1
SELECT * FROM ADRESS;
ID VAL1 VAL2
1 a c
Why JPA allowed for this kind of operation? This behaves like it is Many (Cust) to One (Adress) relationship..
JPA implementation is 4.3.6 Final and DB is H2 1.4.181
Possible duplicate of http://stackoverflow.com/questions/8968294/why-onetoone-is-allowing-duplicate-associations It looks like #OneToOne does not enforce unique constraints
We are generating Sales Report using Hibernate.
Scenario
When user clicks on generate report button after entering some criteria, I am fetching data from database using hibernate, then we are doing some data manipulation to generate actual report data. Report data is stored in ArrayList, which then persisted into database in CommissionSummary table, which is mapped with hibernate entity as below
CommussionSummary.java
#Column(length=100)
private String advisorName;
private String advisorCodeParent;
#Column(length=100)
private String advisorNameParent;
#Column(length=100)
private String advisorPost;
#Column
private Double percentage;
#Column
private Double diffPercentage;
#Column
private Double saleAmount;
#Column
private Long saleCount;
#Column
private Double commissionAmount;
#Column
private Integer month;
#Column
private Integer year;
Report is generated for every month.
My Question is: For 05 July 2012 user has generated data so i am storing this information in CommissionSummary table. Now user has generating the same report on 15 July 2012, then it should override earlier month data.
Override criteria should be month and year.
I believe what you need is a simple data checking.
Solution One:
1. Load data from DB and check those data for possible duplication.
2. If you find them equal, delete the older version.
Solution Two:
One other possible solution is to define your columns unique, so if a user wants to put the same data, he will be receiving some exceptions. Something like this:
#Table(name="TABLE_NAME", uniqueConstraints = {
#UniqueConstraint(columnNames={"advisorName", "advisorNameParent", "advisorPost", "percentage", "diffPercentage" , "saleAmount", "saleCount", "commissionAmount" })
#Column(name = "ADVISOR_NAME", length=100)
private String advisorName;
private String advisorCodeParent;
#Column(name = "ADVISOR_PARENT_NAME", length=100)
private String advisorNameParent;
#Column(name = "ADVISOR_POST" , length=100)
private String advisorPost;
#Column(name = "PERCENTAGE")
private Double percentage;
#Column (name = "DIFF_PERCENTAGE")
private Double diffPercentage;
#Column (name = "SALE_AMOUNT")
private Double saleAmount;
#Column (name = "SALE_COUNT")
private Long saleCount;
#Column (name = "COMMISSION_AMOUNT")
private Double commissionAmount;
Using this, you can check if there were some errors you can do your desired action.
Get the CommissionSummary for the given month an year using a HQL query:
select c from CommissionSummary where c.year = :year and c.month = :month
If not null, delete it:
session.delete(existingCommissionSummary);
Then save the new one.