JPA passing parameter as Set - mysql

i want to pass a parameter in the jpql as set in an update statement. here is the statement:
Query query = entityManager.createQuery("UPDATE Patient patient SET "
+"patient.surname=:surname, "
+"patient.firstname=:firstname, "
+"patient.homeAddress=:homeAddress, "
+"patient.relatedPersons=:relatedPersons, "
+"patient.hospital=:hospital "
+"WHERE patient.id=:id");
query.setParameter("surname", updatablePatient.getSurname());
query.setParameter("firstname", updatablePatient.getFirstname());
query.setParameter("homeAddress", updatablePatient.getHomeAddress());
query.setParameter("relatedPersons", updatablePatient.getRelatedPersons());
query.setParameter("hospital", updatablePatient.getHospital());
query.setParameter("id", id);
but i get the following error:
org.springframework.dao.InvalidDataAccessApiUsageException: Parameter value [****] was not matching type [java.util.Set]; nested exception is java.lang.IllegalArgumentException: Parameter value [****] was not matching type [java.util.Set]
any help would be really appreciated.
thanks in advance

Update statements in JPQL are rarely used, and should be used for batch updates, but not simply to update one entity. They translate directly to SQL, and you can't update a patient and all his related persons like this in SQL. The same goes for JPQL.
To do what you want to do, just get the patient from the database, and set the new properties into the loaded patient :
Patient p = (Patient) entityManager.find(Patient.class, id);
p.setSurname(updatablePatient.getSurname());
p.setRelatedPersons(updatablePatient.getRelatedPersons());
// ... set other properties
Or, if the updatable patient is a detached copy of the patient to update, and thus has the same ID,
Patient p = (Patient) entityManager.merge(updatablePatient);
The whole point of JPA (or at least one of its points) is to be able to use and modify an object graph rather than use queries to create and update data in database.

There is no support for updating set (or in general any collection valued field) via JPQL. In JPA 2.0 specification this is spelled as follows:
update_clause ::= UPDATE entity_name [[AS] identification_variable]
SET update_item {, update_item}*
update_item ::= [identification_variable.]
{state_field | single_valued_object_field} = new_value
For more details: JPQL BNF

Related

Selecting multiple columns and set it to list of DTOs

I want to get multiple columns from database in a single query and set it to the corresponding DTO object fields.
Error message:
java.lang.IllegalStateException: No data type for node:
org.hibernate.hql.internal.ast.tree.IdentNode
+-[IDENT] IdentNode: 'payment' {originalText=payment}
Query:
TypedQuery<Object[]> query = entityManager.createQuery("SELECT
payment, createdOn,responseMessage FROM PaymentLog log WHERE log.id
=:personId", Object[].class);
query.setParameter("personId",new BigInteger(basicEntityDto.getId()));
List<Object[]> results = query.getResultList();
for (Object[] log : results) {
paymentTransaction.setAmount(log[0].toString());
paymentTransaction.setDate(log[1].toString());
paymentTransaction.setDescription(log[2].toString());
transactionList.add(paymentTransaction);
}
P.S. I know I can use JPA constructor expression. But as I have to add the DTOs in a list of DTO(i.e. transactionList), so is there a way with JPA construction expression where I can do that by running the query only one time instead in a loop for every single DTO?
You can have the JPA provider transform the result set for you by means of a constructor expression:
http://www.objectdb.com/java/jpa/query/jpql/select#Result_Classes_Constructor_Expressions_
https://en.wikibooks.org/wiki/Java_Persistence/JPQL#Constructors
This requires that the specified class has a constructor matching the select expression. This would then look something like the below:
TypedQuery<PaymentTransaction> query = entityManager.createQuery("SELECT new PaymentTransaction (log.payment, log.createdOn, log.responseMessage ) FROM PaymentLog log WHERE log.id
=:personId", PaymentTransaction.class);
query.setParameter("personId",new BigInteger(basicEntityDto.getId()));
List<PaymentTransaction> results = query.getResultList();
In JPA 2.1 you can also so like the below:
https://en.wikibooks.org/wiki/Java_Persistence/Querying#ConstructorResult_.28JPA_2.1.29
What you could do is:
TypedQuery<PaymentLog> query = entityManager.createQuery("SELECT log FROM PaymentLog log WHERE log.id =:personId", PaymentLog.class);
query.setParameter("personId",new BigInteger(basicEntityDto.getId()));
List<PaymentLog> results = query.getResultList();
for (PaymentLog log : results) {
paymentTransaction.setAmount(log.getPayment());
paymentTransaction.setDate(log.getCreatedOn());
paymentTransaction.setDescription(log.getResponseMessage());
transactionList.add(paymentTransaction);
}
It is not a good idea to select everything from the database if you are not going to use it. If the selected fields were the only columns in the table then approach above works.
If you had a lot more columns in the table, the previous would still work, but this might be better:
TypedQuery<PaymentTransaction> query = entityManager.createQuery("SELECT new PaymentTransaction (log.payment, log.createdOn, log.responseMessage) FROM PaymentLog log WHERE log.id =:personId", PaymentTransaction.class);
query.setParameter("personId",new BigInteger(basicEntityDto.getId()));
List<PaymentTransaction> results = query.getResultList();
The above query will return an already created list of PaymentTransactions. You have to note that the class PaymentTransactionshould have a constructor that accept these fields in the given order. Otherwise it will cause an exception

Derived property calling stored function throws StreamCorruptedException

I'm trying to improve performance by replacing a dynamic field (a transient getter with no underlying database representation) with a derived field so that I can use, e.g., Criteria to query my model. The original dynamic field was pretty simple:
Client resolveClient() {
if (prevCall && prevCall.client) {
return prevCall.client
} else {
return client
}
}
I don't know how to reproduce that with a single MySQL statement, so I figured I would go ahead and stick it into a stored function, defined as follows:
CREATE FUNCTION `request_client`(requestId long) RETURNS varchar(255) CHARSET utf8
begin
declare pci long;
declare clientId long;
declare clientName varchar(255);
select request.prev_call_id
from request
where request.id = requestId
into pci;
if pci is not null then
select call_history.client_id
from call_history
where call_history.call_id = pci
into clientId;
else
select request.client_id
from request
where request.id = requestId
into clientId;
end if;
select clients.client_name
from clients
where clients.client_id = clientId
into clientName;
return clientName;
end;
And then I call that function in a derived field:
String derivedFieldName
static mapping = {
derivedFieldName formula: '(select stored_function(id))'
}
The problem is that now when I run any query on the domain, even as simple as Request.list(), I get the following exception:
Class: java.io.StreamCorruptedException
Message: invalid stream header: 32303135
For extra fun, this is an abstract domain class. I don't know if that really makes any difference; it's still persisted to the database like any other domain, and I'm calling the query on the abstract class itself, not an implementation.
The most frustrating thing is that the derived field itself does work! I can successfully retrieve a client name using it; I just can't query the overall domain.
Finally, I am pretty confident that the derived property is the issue, as I have commented it out and can then successfully query the domain.
If anybody comes across this later, the problem actually was the abstract class. And not just that it's an abstract class -- it's an abstract domain class. Apparently, Grails doesn't support derived properties on those.
To move querying to the database I just had to start saving the resolved client into my Request domain :/

filling a select field with model with certain tag - laravel

I have no problem with selecting a model tagged with certain tag
$lis = Capacitytype::find(124)->entities()->orderBy('name','asc')->get();
In my Capacitytype model I have this relation:
public function entities()
{
return $this->belongsToMany('App\Models\Entity', 'entity_capacitytypes', 'capacitytype_id', 'entity_id');
}
MY PROBLEM:
I wish to use the same selection f models to fill a select field in a form. (I use select2.js)
When I try to implement this code
$lis = array(null => 'Commitee') + Capacitytype::find(124)->entities()->orderBy('name','asc')->lists('name', 'id')->all();
I get this error:
SQLSTATE[23000]: Integrity constraint violation: 1052 Column 'id' in field list is ambiguous (SQL: select name, id from entities inner join entity_capacitytypes on entities.id = entity_capacitytypes.entity_id where entities.deleted_at is null and entity_capacitytypes.capacitytype_id = 6 order by name asc)
I tried to solve this issue by adding table name to my code, but this is as far as I was able to go:
->lists('entities.name', 'entities.id')
My Question:
how modify the code to get the desired collection for my select field?
Thank you.
You could make use of Laravel's with() to perform an eager loading as a workaround for this issue. To do so, just pass the name of the relation (i.e. entities) to the method:
Capacitytype::find(124)->with('entities')->orderBy('name','asc')->lists('entities.name', 'entities.id');
Or else, you could first fetch the records, and then reformat it to match your usecase:
$list = Capacitytype::find(124)->entities()->orderBy('name','asc')->get()->toArray();
As such, you can refine the returned list of arrays, to match your id => name format (hence, a substitute for the lists method).
$refinedList = array();
foreach($list as $entity){
$refinedList[$entity['id']] = $entity['name'];
}
And, there you go.

Bulk Update with LINQ to SQL

Is there a way to do a bulk update on a collection with LINQ? Currently if I have a List<myObject> and I want to update column1 to equal TEST for every row in the List I would setup a foreach loop and then for each individual object I would set the value and then save it. This works fine but I was just wondering if there was some LINQ method out there where I could do something like myOject.BulkUpdate(columnName, value)?
Your requirement here is entirely possible using Linq expressions and Terry Aney's excellent library on this topic.
Batch Updates and Deletes with LINQ to SQL
An update in the terms of the example you gave would be as follows:
using BTR.Core.Linq;
...
Context.myObjects.UpdateBatch
(
Context.myObjects.Where(x => x.columnName != value),
x => new myObject { columnName = value}
);
Edit (2017-01-20): It's worth nothing this is now available in the form of a NuGet package # https://www.nuget.org/packages/LinqPost/.
Install-Package LinqPost
Sounds like you're using LINQ To SQL, and you've got the basics laid out already.
LINQ To SQL is about abstracting tables into classes, and doesn't really provide the 'silver bullet' or one-liner you are looking for.
The only way to do that is to achieve your one-liner would be to make a stored proc to take that column name and new value, and implement that logic yourself.
db.MassUpdateTableColumn("Customer", "Name", "TEST");
....
CREATE PROC MassUpdateTableColumn
#TableName varchar(100), #ColumnName varchar(100), #NewVal varchar(100)
AS
/*your dynamic SQL to update a table column with a new val. */
Otherwise, it's as you describe:
List<Customer> myCusts = db.Customers.ToList();
foreach(Customer c in myCusts)
{
c.Name = "TEST";
}
db.SubmitChanges();
LINQ to SQL (or EF for that matter), is all about bringing objects into memory, manipulating them, and then updating them with separate database requests for each row.
In cases where you don't need to hydrate the entire object on the client, it is much better to use server side operations (stored procs, TSQL) instead of LINQ. You can use the LINQ providers to issue TSQL against the database. For example, with LINQ to SQL you can use context.ExecuteCommand("Update table set field=value where condition"), just watch out for SQL Injection.
EF Core 7.0 introduces Bulk Update and Bulk Delete.
For example, consider the following LINQ query terminated with a call to ExecuteUpdateAsync:
var priorToDateTime = new DateTime(priorToYear, 1, 1);
await context.Tags
.Where(t => t.Posts.All(e => e.PublishedOn < priorToDateTime))
.ExecuteUpdateAsync(s => s.SetProperty(t => t.Text, t => t.Text + " (old)"));
This generates SQL to immediately update the “Text” column of all tags for posts published before the given year:
UPDATE [t]
SET [t].[Text] = [t].[Text] + N' (old)'
FROM [Tags] AS [t]
WHERE NOT EXISTS (
SELECT 1
FROM [PostTag] AS [p]
INNER JOIN [Posts] AS [p0] ON [p].[PostsId] = [p0].[Id]
WHERE [t].[Id] = [p].[TagsId] AND [p0].[PublishedOn] < #__priorToDateTime_1)

Error:insert hierarchy values using storedprocedure

I'm getting this error when trying to run a query that inserts results into a table in sql.
im passing the table name as parameter,how to give the hierarchy value to the insert statement.
here is my code:
declare #pathhere hierarchyid
select #pathhere=Path from SectionDetails where Sectionid=#sectionid and SectionName=#sectionname and Batchid=#batchid and Deptid=#deptid and Schoolid=#schoolid
insert stmt:
set #sqlstmt = 'insert into '+#batch+'(StudentID, StudentName,SectionID,SectionName,BatchID,BatchName, DeptID,DeptName, SchoolID,Path)
values('''+#sectionid+''','''+#sectionname+''','''+#sectionid+''','''+#sectionname+''','''+#batchid+''','''+#batchname+''','''+ #deptid+''','''+#deptname+''', '''+#schoolid+''','+ CAST(#pathhere as hierarchyid)+')'
exec(#sqlstmt)
im getting error in this line:
'+ CAST(#pathhere as hierarchyid)+'
as Invalid operator for data type. Operator equals add, type equals hierarchyid.
can anyone pls help me out how to pass the hierarchy value
You're trying to create a string that can be executed as a statement. So you need to get your hierarchyid into nvarchar(max) instead.
try: #pathhere.ToString()