JPA retrieve part of object - database

In sql you can easily select a few columns out of the whole table. How do you achieve such a thing in JPA ?
And that's not actually my main question. It's more of an design one.
Let's say i have a parent object with some information fields and a collection of child objects field with a one to many connection.
#Entity
#Table(name = "code")
public class CodeList extends Code {
/** The code list's values. */
#OneToMany(cascade = CascadeType.PERSIST, fetch = FetchType.LAZY)
#JoinColumn(name = "codeListId")
private List<CodeValue> codeValues;
/** The code list's display type. */
#Column(length = 255, name = "DISPLAY_TYPE")
private String displayType;
....
I am trying to implement a rest service using JPA (Hibernate) and jax-rs but what if i want my service to be able to retrieve just the information of the CodeList ( the displayType ) or just the collection of codeValues without any extra performance overhaul (nulling out the objects) or retrieval from the database of extra data that i dont need ?

By default #OneToMany relationship is LAZY .
simple value :
SELECT c.displayType FROM CodeList c WHERE c.id=:id
relationship
SELECT v FROM CodeList c LEFT JOIN c.codeValues v WHERE c.id=:id

Use JPQL to determine which values you want to retrieve.
select e.displayType from CodeList e
OR
select e from CodeList d left join fetch d.codeValues e

Related

Load unmapped tables in Symfony with Doctrine

I have tables in my database, that are not managed by Symfony; there are no entities for these tables. They are tables from another application, I import them and use Symfony to generate statistics from the data in the tables.
How do I access this?
Can i use doctrine and a regular repository for this?
I just want to read data, not update.
Right now I'm using straight mysqli_connect and mysqli_query, but that just doesn't feel right using Symfony 5.
You should just be able to query with sql. The following example comes straight from the docs:
// src/Repository/ProductRepository.php
// ...
class ProductRepository extends ServiceEntityRepository
{
public function findAllGreaterThanPrice(int $price): array
{
$conn = $this->getEntityManager()->getConnection();
$sql = '
SELECT * FROM product p
WHERE p.price > :price
ORDER BY p.price ASC
';
$stmt = $conn->prepare($sql);
$stmt->execute(['price' => $price]);
// returns an array of arrays (i.e. a raw data set)
return $stmt->fetchAllAssociative();
}
}
https://symfony.com/doc/current/doctrine.html#querying-with-sql

Inner join using SqlQuery with Entity Framework in MVC

var getdata = (DB.books.SqlQuery("select *
from book
inner join allclass on allclass.id = book.class_id_fk
inner join universty on universty.id = allclass.universty_id_fk")
).ToList();
I want to run this SQL query for joining table data in Entity Framework - how can I do this ?
Another way to do this with linq query but i want to get data with SQL query using Entity Framework
You cannot use DB.books if the result of the query is not of type books. You must instead use DB.Database class when the return type of the raw sql is not of any entity type from the database.
Follow these steps:
1) Create a model that matches your list of output columns like below.
public class MyModel
{
public string Column1{ get; set; }
public decimal Column2{ get; set; }
...
}
2) Alter your query like below.
var getdata = (DB.Database.SqlQuery<List<MyModel>>("select Column1,Column2.. //List all the columns instead of using *
from book
inner join allclass on allclass.id = book.class_id_fk
inner join universty on universty.id = allclass.universty_id_fk")
);

Inserting/Updating Rows to DB table where Rows result from VO [Backed by EO] based on Union Query

Jdev Version : 11.1.1.7
I have created a Department VO based Department EO with the following query :
SELECT DeptEO.DEPARTMENT_ID,
DeptEO.DEPARTMENT_NAME,
DeptEO.MANAGER_ID,
DeptEO.LOCATION_ID,
DeptEO.ACTIVE
FROM DEPARTMENTS DeptEO where DeptEO.DEPARTMENT_ID > 250
UNION
SELECT 280 , 'Advertising',200,1700,'Y' from Dual
For the simplicity , I have used a sample statement from dual table , in real scenario , the query after UNION clause will populate from a table.
After running the query ,I get the result that is desired on the UI .
Now my requirement is to insert this newly created row with DEPARTMENT_ID as 280 , into DB table DEPARTMENTS.
While committing , ADF throws error as " oracle.jbo.RowAlreadyDeletedException: JBO-29114 " which is correct as the this row is missing from DB table , so when it goes for taking a lock on the row for update , it doesn't find anything .
Is there any way that i can instruct ADF to consider this row for Insert rather than update .
We also tried to populate the data of this row into a new row instance created from RowSetIterator , and afterwards remove the culprit row by calling removeFromCollection() and then inserting the duplicated row , but still no luck .
Other approaches that we are thinking of are :
1- Create another VO/EO and insert values in table through them .
2- Create a DB View for this query and trigger on this view , so when ever an update operation comes , we do our logic in trigger i.e. decide whether to update or insert the data.
Can you please guide what should be done in such scenario .
Regards,
Siddharth
Edit : Code for Inserting Row (What I was trying but it's not working)
RowSetIterator rsi=iterator.getRowSetIterator();
Row editableRow= rsi.createRow();
while(rsi.hasNext()){
Row r =rsi.next();
if((""+r.getAttribute("DepartmentId")).toString().equals("280") ){
System.err.println("? Equality row found!!!");
editableRow.setAttribute("DepartmentId", r.getAttribute("DepartmentId"));
editableRow.setAttribute("DepartmentName", r.getAttribute("DepartmentName"));
editableRow.setAttribute("ManagerId", r.getAttribute("ManagerId"));
editableRow.setAttribute("LocationId", r.getAttribute("LocationId"));
editableRow.setAttribute("Active", r.getAttribute("Active"));
rsi.removeCurrentRowFromCollection();
}
}
if(editableRow !=null){
System.err.println("? Row value after removal : "+editableRow.getAttribute("DepartmentName"));
rsi.insertRow(editableRow);
operBindingCommit.execute();
}
Your use case can be implemented in a couple of ways. First way is to iterate over row set in managed bean and check if department with id 280 exists, if yes then update the row otherwise invoke Create with parameters for department VO. The second way, and would say the better way, is to create a method for update/insert at business component level, either in ViewObjectImpl or in ApplicationModuleImpl and then invoke it from managed bean.
Here is the sample code for insert/update method written in VOImpl
public void updateInsertJobs(String jobId, String jobTitle,
String minSalary, String maxSalary)
{
RowSetIterator rSet = this.createRowSetIterator(null);
JobsViewRowImpl row = new JobsViewRowImpl();
Boolean jobExist = false;
if (null != jobId)
{
try
{
while (rSet.hasNext())
{
row = (JobsViewRowImpl) rSet.next();
if (row.getJobId().equals(jobId))
{
row.setJobTitle(jobTitle);
row.setMinSalary(new Number(minSalary));
row.setMaxSalary(new Number(maxSalary));
jobExist = true;
}
}
if (!jobExist)
{
JobsViewRowImpl r = (JobsViewRowImpl) this.createRow();
r.setJobId(jobId);
r.setJobTitle(jobTitle);
r.setMinSalary(new Number(minSalary));
r.setMaxSalary(new Number(maxSalary));
this.insertRow(r);
}
this.getDBTransaction().commit();
}
catch (Exception e)
{
e.printStackTrace();
}
}
}
Make sure to expose the method in Client Interface in order to be able to access it from data control.
Here is how to invoke the method from managed bean:
public void insertUpdateData(ActionEvent actionEvent)
{
BindingContainer bc =
BindingContext.getCurrent().getCurrentBindingsEntry();
OperationBinding oB = bc.getOperationBinding("updateInsertJobs");
oB.getParamsMap().put("jobId", "TI_STF");
oB.getParamsMap().put("jobTitle", "Technical Staff");
oB.getParamsMap().put("minSalary", "5000");
oB.getParamsMap().put("maxSalary", "18000");
oB.execute();
}
Some references which could be helpful:
http://mahmoudoracle.blogspot.com/2012/07/adf-call-method-from-pagedefinition.html#.VMLYaf54q-0
http://adftidbits.blogspot.com//2014/11/update-vo-data-programatically-adf.html
http://www.awasthiashish.com/2012/12/insert-new-row-in-adf-viewobject.html
Your view object become readonly due to custom sql query.
However you still can create row in dept table using entity.
Create java implemetation including accessors for DeptEO.
Create custom method in view object and create new entity or update existing using entity definition there. To find that required row exist, you can check that entity with this key is already exists. Something like this (assuming deptId is your primary key):
public void createOrUpdateDept(BigInteger deptId){
DeptEOImpl dept;
EntityDefImpl deptDef = DeptEOImpl.getDefinitionObject();
Key key = new Key(new Object[]{deptId});
dept = deptDef.findByPrimaryKey(getDBTransaction(), key);
if (dept == null){
// Creating new entity if it doesn't exist
dept = deptDef.createInstance2(getDBTransaction(), null);
dept.setDepartmentId(deptId);
}
// Changing other attributes
dept.setDepartmentName("New name");
// Commiting changes and refreshing ViewObject if required
getDBTransaction().commit();
executeQuery();
}
This code is just a sample, use it as reference/idea, don't blindly copy/paste.

Importing SQL Server's CONTAINS() as a model defined function

I am trying to import SQL Server's CONTAINS() function in my Entity Framework model so that I can use it in my LINQ queries.
I have added this to my EDM:
<Function Name="FullTextSearch" ReturnType="Edm.Boolean">
<Parameter Name="Filter" Type="Edm.String" />
<DefiningExpression>
CONTAINS(*, Filter)
</DefiningExpression>
</Function>
Add created my method stub:
[EdmFunction("MyModelNamespace", "FullTextSearch")]
public static bool FullTextSearch(string filter)
{
throw new NotSupportedException("This function is only for L2E query.");
}
I try to call the function like this:
from product in Products
where MyModel.FullTextSearch("FORMSOF(INFLECTIONAL, robe)")
select product
The following exception is raised:
The query syntax is not valid. Near term '*'
I realize that the function I defined is not directly linked to the entity set being queried so that could also be a problem.
Is there any way to pull this off?
The function you have defined above uses Entity SQL, not Transact SQL, so I think the first step is to figure out whether CONTAINS(*,'text') can be expressed in Entity SQL.
Entity SQL doesn't support the * operator as described here: http://msdn.microsoft.com/en-us/library/bb738573.aspx and if I try
entities.CreateQuery<TABLE_NAME>("select value t from TABLE_NAME as t where CONTAINS(*, 'text')");
I get the same error you got above. If I try to explicitly pass the column it works:
entities.CreateQuery<TABLE_NAME>("select value t from TABLE_NAME as t where CONTAINS(t.COLUMN_NAME, 'text')");
But when I look at the SQL it translated it to a LIKE expression.
ADO.NET:Execute Reader "SELECT
[GroupBy1].[A1] AS [C1]
FROM ( SELECT
COUNT(1) AS [A1]
FROM [dbo].[TABLE_NAME] AS [Extent1]
WHERE (CASE WHEN ([Extent1].[COLUMN_NAME] LIKE '%text%') THEN cast(1 as bit) WHEN ( NOT ([Extent1].[COLUMN_NAME] LIKE '%text%')) THEN cast(0 as bit) END) = 1
) AS [GroupBy1]"
If you cannot express the query using Entity SQL you'll have to use a Stored Procedure or other mechanism to use Transact SQL directly.
This is way beyond me but could you try
from product in Products where MyModel.FullTextSearch(product, "FORMSOF(INFLECTIONAL, robe)") select product
My reasoning is that in SQL Server it is expecting two parameters.
I inserted a little function into my code, in a class which inherits from the Context class, which points to my SQL function supporting Full Text searching, my solution is a little more closed ended to yours (not allowing the specification of the type of text search), it returns an IEnumerable, essentially a list of primary keys matching the searching criteria, something like this;
public class myContext : DataContext
{
protected class series_identity
{
public int seriesID;
series_identity() { }
};
[Function(Name = "dbo.fnSeriesFreeTextSearchInflectional", IsComposable = true)]
protected IQueryable<series_identity> SynopsisSearch([Parameter(DbType = "NVarChar")] string value)
{
return this.CreateMethodCallQuery<series_identity>(this, ((MethodInfo)(MethodInfo.GetCurrentMethod())), value);
}
public IEnumerable<int> Search(string value)
{
var a = from t1 in SynopsisSearch(value)
select t1.seriesID;
return a;
}
};
usage is something like;
myContext context = new myContext();
IEnumerable<int> series_identities = (from t1 in context.Search("some term")
select t1).Distinct();

Hierarchical Hibernate, how many queries are executed?

So I've been dealing with a home brew DB framework that has some seriously flaws, the justification for use being that not using an ORM will save on the number of queries executed.
If I'm selecting all possibile records from the top level of a joinable object hierarchy, how many separate calls to the DB will be made when using an ORM (such as Hibernate)?
I feel like calling bullshit on this, as joinable entities should be brought down in one query , right? Am I missing something here?
note: lazy initialization doesn't matter in this scenario as all records will be used.
Hibernate will almost always retrieve object hierarchies using a single query; I don't recall seeing it do otherwise. It's easy to test, anyway. With this very simple mapping:
#Entity
public static class Person {
#Id
public String name;
}
#Entity
public static class Student extends Person {
public float averageGrade;
}
#Entity
public static class Teacher extends Person {
public float salary;
}
Then Hibernate gives me the following results for a very simple browse query (sessionFactory.openSession().createCriteria(Person.class).list();).
With #Inheritance(strategy = InheritanceType.SINGLE_TABLE) on the parent:
select this_.name as name0_0_, this_.averageGrade as averageG3_0_0_,
this_.salary as salary0_0_, this_.DTYPE as DTYPE0_0_ from HibernateTest$Person this_
With #Inheritance(strategy = InheritanceType.JOINED) on the parent:
select this_.name as name0_0_, this_1_.averageGrade as averageG1_1_0_,
this_2_.salary as salary2_0_, case when this_1_.name is not null then 1
when this_2_.name is not null then 2 when this_.name is not null then 0
end as clazz_0_ from HibernateTest$Person this_ left outer
join HibernateTest$Student this_1_ on this_.name=this_1_.name left outer join
HibernateTest$Teacher this_2_ on this_.name=this_2_.name
With #Inheritance(strategy = InheritanceType.JOINED) on the parent:
select this_.name as name0_0_, this_.averageGrade as averageG1_1_0_,
this_.salary as salary2_0_, this_.clazz_ as clazz_0_ from
( select null as averageGrade, name, null as salary, 0 as clazz_
from HibernateTest$Person union select averageGrade, name, null as salary,
1 as clazz_ from HibernateTest$Student union select null as averageGrade,
name, salary, 2 as clazz_ from HibernateTest$Teacher ) this_
As you can see, each is one query, with JOINs or UNIONs as appropriate depending on the mapping type.
Bobah is right,
You should give hibernate a try in order to see how many request will be sent to the database, however, in hibernate you can also specify and tune specific request by using HQL.
In addition with hibernate tools, you could also use P6spy driver, so you'll be able to see all the request that hibernate send to your database, with the value for each filter of the request.

Resources