Typed Query for those tables created from relationships (One to Many) - jpa

I am little confused, If i have two tables related then I will have a combined table in MySQL, Since we do not have a class in our project , how would my Typed Query look in order to fetch data from table that is create from Relationship( say One to Many).
eg:
#OneToMany(cascade = CascadeType.ALL)
#JoinTable(name = "CustomerBilling",joinColumns = #JoinColumn(name = "Customer_Id"),
inverseJoinColumns = #JoinColumn(name = "Billing_Id"))
private List<Billing> billing = new ArrayList<>();
with the above mentioned code i will have CustomerBilling table , So i would like to get all the records for a particular customer id. in my test (jUnit) file what Typed Query do i need to put ?
TypedQuery<Customer> a = em.createQuery("select b from CustomerBilling b where b.Customer_Id =?1", Customer.class);
This did not work Since CustomerBilling abstrate schema is not present.
Thanks
Prashanth

I am sure you have classes in your project as it is java based :)
Reading between the lines, you mean, that you have 3 tables in DB Customer, Billing and CustomerBilling, but only 2 entities in JPA the Customer and Billing as the CustomerBilling is there only to store the relation. (But your example of typed query tries to get Customer.class as the result so maybe I am wrong).
You don't reference jointable objects directly (unless they are mapped).
You can query by Customer, or by Billing but not by CustomerBilling. If there is no such entity defined in JPA, there is nothing that JPA can query about.
And you shouldn't try to do it, even for unit testing (BTW jpa tests are integration tests not unit tests).
The whole point of mapping the relationships with #OnteToMany and such is to hide, from the program, the actual DB structure. So you should access Billings of a customer, or customer for a billing, not the relation info.
Currently the relation is stored by join table, but it could be change it to join column and whole program logic would stay the same (and so should the tests).
You should test, that if you add bilings to customer and save them, you correctly retrieves them, that if you remove the biling from customer it disappears, but there is no reason to check the content of the join table.
Finally, if you really must, you can use native sql query to access CustomerBilling table
em.createNativeQuery
or
you can change your mappings, and introdude the CustomerBilling entity, and map your OneToMay on Customer to the CustomerBilling instead of Directly Billing.

Related

Join database query Vs Handeling Joins in API

I am developing an API where I am confused as to what is the efficient way to handle join query.
I want to join 2 tables data and return the response. Either I can query the database with join query and fetch the result and then return the response OR I can fire two separate queries and then I would handle the join in the API on the fly and return the response. Which is the efficient and correct way ?
Databases are pretty much faster than querying and joining as class instances. Always do joins in the database and map them from the code. Also look for any lazy loading if possible. Cause in a situation like below:
#Entity
#Table(name = "USER")
public class UserLazy implements Serializable {
#Id
#GeneratedValue
#Column(name = "USER_ID")
private Long userId;
#OneToMany(fetch = FetchType.LAZY, mappedBy = "user")
private Set<OrderDetail> orderDetail = new HashSet();
// standard setters and getters
// also override equals and hashcode
}
you might not want order details when you want the initial results.
Usually it's more efficient to do the join in the database, but there are some corner cases, mostly due to the fact that application CPU time is cheaper than database CPU time. Here are a few examples that come to mind, with a query like "table A join table B":
B is a small table that rarely changes.
In this case it can be profitable to cache the contents of this table in the application, and not query it at all.
Rows in A are quite large, and many rows of B are selected for each row of A.
This will cause useless network traffic and load as rows from A are duplicated many times in each result row.
Rows in B are quite large, and there are few distinct b_id's in A
Same as above, except this time the same few rows from B are duplicated in the result set.
In the previous two examples, it could be useful to perform the query on table A, then gather a set of unique b_id's from the result, and SELECT FROM b WHERE b_id IN (list).
Data structure and ORMs
If each table contains a different object type, and they have a "belongs to" relationship (like category and product) and you use an ORM which will instantiate objects for each selected row, then perhaps you only want one instance of each category, and not one per selected product. In this case, you could select the products, gather a list of unique category_ids, and select the categories from there. The ORM may even do that for you behind the scene.
Complicated aggregates
Sometimes, you want some stuff, and some aggregates of other stuff related to the first stuff, but it just won't fit in a neat GROUP BY, or you may need several ones.
So basically, usually the join works better in the database, so that should be the default. If you do it in the application, then you should know why you're doing it, and decide it's a good reason. If it is, then fine. I gave a few reasons, based on performance, data model, and SQL constraints, these are only examples of course.

Selecting columns from different entities

I don't know whether I should be drawing parallels, but unfortunately, that's the only way I can express my issue.
In SQL, suppose we have two tables:
Employee with columns Employee ID, Employee Name, Dept. ID
Deptartment with columns Dept. ID, Dept Name
The Dept ID. in the Employee table is a foreign key with that in the Department table.
Now suppose I want to fetch the following columns:
Employee ID, Employee Name, Department Name
using a SQL such as:
SELECT A.EMPLOYEE_ID, A.EMPLOYEE_NAME, B.DEPT_NAME
FROM EMPLOYEE A, DEPARTMENT B
WHERE A.DEPT_ID = B.DEPT_ID
How would one do this using Core Data in Swift? I guess I'm getting confused by only seeing references to
NSFetchRequest(entityName: entityName)
where the entityName refers to a single entity (table in relational DB lingo)
I'd really appreciate any pointers or examples that can help me clear my doubts.
Thanks
It is certainly possible to create a fetch request that is equivalent to your SQL query. More complex queries can be difficult if not impossible to achieve with a single fetch request. But I recommend trying NOT to draw parallels between CoreData and SQL, at least until you have got to grips with how it works.
To take your example, in the CoreData view of the world, Employee would be an entity with a relationship to another entity, Department. A fetch request based on the Employee entity will return an array of Employee objects, and (assuming you create subclasses of NSManagedObject for each entity) you can access the attributes with simple dot notation:
let employeeName = myEmployeeObject.employeeName
But you can use the same notation to traverse relationships equally easily:
let departmentName = myEmployeeObject.department.departmentName
You don't need to worry about joins, etc; CoreData handles that for you.
Now, suppose you try to do it "the SQL way". You can construct a fetch request based on the Employee entity, but specify "properties to fetch" which likewise traverse the relationship:
let fetch = NSFetchRequest(entity:"Employee")
fetch.propertiesToFetch = ["employeeID", "employeeName", "department.departmentName"]
For this to work, you would need to specify the "resultType" for the fetch request to be DictionaryResultType:
fetch.resultType = .DictionaryResultType
The results from this query would be an array of dictionaries containing the relevant keys and values. But the link with the underlying objects is lost. If you subsequently want to access any details from the relevant Department (or even the Employee), you would have to run a new fetch to get the object itself.

How to optionally persist secondary table in Eclipselink

I am working with Eclipselink and having issue with using secondary table.
I have two tables as below.
Student with columns student_id(Primary Key), student_name etc.
Registration with columns student_id(FK relationship with Student table), course_name (with not null constraint) etc.
The requirement is student may or may not have registration. If student has registration, the data should be persisted to Registration table as well. Otherwise only Student table should be persisted.
My code snippet is as below.
Student.java
------------
#Entity
#Table(name = "STUDENT")
#SecondaryTable(name = "REGISTRATION")
#Id
#Column(name = "STUDENT_ID")
private long studentId;
#Basic(optional=true)
#Column(name = "COURSE_NAME", table = "REGISTRATION")
private String courseName;
I tried the following scenarios.
1. Student with registration - Working fine. Data is added to both Student and Registration tables
2. Student without registration - Getting error such as 'COURSE_NAME' cannot be null.
Is there a way to prevent persisting into secondary table?
Any help is much appreciated.
Thanks!!!
As #Eelke states, the best solution is to define two classes and a OneToOne relationship.
Potentially you could also use inheritance, having a Student and a RegisteredStudent that adds the additional table. But the relationship is a much better design.
It‘s possible using a DescriptorEventListener. The aboutToInsert and aboutToUpdate callbacks have access to the DatabaseCalls and may even remove the statements hitting the secondary table.
Register the DescriptorEventListener with the ClassDescriptor of the entity. For registration use a DescriptorCustomizer specified in a Customizer annotation at the entity.
However, you will not succeed fetching the entities back again later on. EclipseLink uses inner joins when selecting from the secondary table, so that the row of the primary table will be gone in the results.

JPA fetching too many entries

I have a Customer Entity with a OneToMany relationship to an Invoice Entity.
In plain old sql i can do "select customer_name,customer_age,[some other fields] from customer, invoice where ... [put some filtering here]", which gets me exactly one record with the fields i need.
In JPA i use "select c from Customer c join c.invoiceCollection where ... [same filtering as above]"
This works, but i get the Customer entity with all its associated invoices.
This is nonsense, because i pull a huge amount of (invoices) data from the database, which i do not need. I need only my customer data with exactly one invoice, as specified in the where clause.
To make things worse, i have to loop over the Customer.invoiceCollection in order to find the one specific invoice needed. This costs me even more time plus it exposes my "where" clause to the middle-tier.
Question: is there a JPA select syntax which fetches exactly one record from a one-to-many relationship, as defined in the where clause?
Things tried so far:
a) lazy loading. This does not work, throws an exception whenever i try to access Customer.invoiceCollection.
Even if it worked, i'd get a Collection with some 1000 entries, which i do not need.
b) changed my jpa statement to "select c,i from Customer c join c.invoiceCollection i where ...". This returns me an array of objects, which i have to manually map to a Customer / Invoice entity.
It works, but it makes the ORM philosophy obsolete. If i perform all the mapping from relational database records/fields to java objects manually in my code, why do i need JPA?
This is one of the most infuriating things about JPA. For example, you NEED the OneToMany side if you want Customer to cascade delete Invoices. In most cases you'd like to tell Invoices to delete itself when a Customer is deleted so that Customer does not necessarily need to know about Invoice.
My suggestion for you is that you keep the OneToMany there but get the Lazy Loading working. In your code, do not access "Customer#getInvoices" directly (unless you really need all of them).
This will allow you to do queries on customers that join to invoices without loading them.
I'm guess the exception you are getting just has to do with transaction boundaries which can be easily fixed.
For a lot of these relationships I often add the OneToMany as a private instance variable but I don't create the #getter method. That way I can use it in queries, setup cascade delete, etc. but I don't provide a way to accidentally load thousands of invoices from a customer.
Oh, and for those queries where you need exactly one invoice with its associated customer you should just do the JPA query on the Invoice and then call #getCustomer on that invoice object. That will be eagerly fetched for you.
If you need exactly one invoice with the related customer, why don't you create a query simply based on Invoice?
select i from Invoice i where [same filtering..]
You should not use one-to-many relationship in this case.
One-to-many relationships are suitable for situations when objects at "many" side are logical parts of object at "one" side (e.g. relationship from Invoice to InvoiceLine).
In you case you need a unidirectional many-to-one relationship from Invoice to Customer, so that you can query it as follows:
select i from Invoice i where ...
Then you can use customer field of Invoice to access Customer or filter by its properties.

returning collections using linking tables and JPA

After struggling for days attempting to get back collections that are linked to a table via a foreign key, I just realized that the tables I am linking to are actually LINKING tables to other tables with the actual data (chock one up for normalized tables).
I am still struggling to get collections out of ManyToOne annotated variables with references to foreign keys, but is there a way I can pull the data back from the table that actually contains the information? Has anyone run into an instance of this?
UPDATE: AS per request I will be posting some code instances... This would be my named query in the entity that I will be calling...
#NamedQuery(name="getQuickLaunchWithCollections", query = "SELECT q FROM QuickLaunch q LEFT JOIN FETCH q.quickLaunchDistlistCollection LEFT JOIN FETCH q.quickLaunchPermCollection LEFT JOIN FETCH q.quickLaunchProviderCollection")})
These would be the collections that I am looking to fill...
#OneToMany(mappedBy="quickLaunchId", fetch=FetchType.EAGER)
private List<QuickLaunchPerm> quickLaunchPermCollection;
#OneToMany(mappedBy="quickLaunchId", fetch=FetchType.EAGER)
private List<QuickLaunchProvider> quickLaunchProviderCollection;
#OneToMany(mappedBy="quickLaunchId", fetch=FetchType.EAGER)
private List<QuickLaunchDistlist> quickLaunchDistlistCollection;
As you can see, I have the fetch type set to eager. So technically, I should be getting some data back? But in actuality those are just linking tables the data that I actually want to pull back. I will need to figure out how to get that data back eventually.
This is how I am calling that named query...
listQL = emf.createNamedQuery("getQuickLaunchWithCollections").getResultList();
Alright, it appears as though LEFT JOIN FETCH
is causing my runtime to throw an expception of some kind. It is pretty unclear as to what it is. But I feel as though I am getting no where with that technique. I am going to try something slightly different.
I would suggest simplifying example, to face the problem, since you are going worldwide now.
Specifying mappedBy="quickLaunchId" attribute, you are saying, that QuickLaunchPerm entity has QuickLaunch as its property named "quickLaunchId". Is this true?
If it is not, then you need to define it in QuickLaunchPerm:
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "QUICK_LAUNCH_ID")
private QuickLaunch quickLaunchId;
//getters setters