Currently i am mapping from list of pojos to Record, and i want to be able to insert multiple rows at once. how can i do that in JOOQ with one transaction?
List<Record> recordList = receiverList.stream().map(r -> {
return dslContext.newRecord(Table, r);
}).collect(Collectors.toList());
I have tried put the list in the "values", but getting exception "The number of values must match the number of fields"
dslContext.insertInto(Table).values(recordList);
Your error is because .values(...) is waiting for field values not Record.
Maybe you can do a batch execution :
dslContext.batchInsert(recordList);
As Lukas mentioned it, it will execute it in a single jdbc statement which is atomic
instead of batchInsert you can also do:
var insertStepN = dslContext.insertInto(Table).set(dslContext.newRecord(Table, recordList.get(0));
for (var record : recordList.subList(1, recordList.size()) {
insertStepN = insertStepN.newRecord().set(dslContext.newRecord(Table, record));
}
insertStepN.returning().fetch().into(YourClass.class);
this way you can get the inserted values back using returning(), which you won't get with batchInsert().
Related
Feeling a bit stupid, but I have a simple architecture where the repositories are the only ones accessing ~Record classes and the services work on POJOs.
So basic flow is
repository fetches into POJO
service modifies POJO
repository receives POJO to update DB
repository matches updated POJO to record
repository stores (insert or update) the record
repository maps updated record (may have received generated values from insert) back to POJO
service receives updated POJO
i.e. something like
fun save(set: MySet): MySet {
set.description = set.description ?: ""
val record = ctx.newRecord(MY_SET, set).apply {
store()
}
// "When store() performs an INSERT statement, jOOQ attempts to load any generated keys from the database back into the record."
// cf. https://www.jooq.org/doc/latest/manual/sql-execution/crud-with-updatablerecords/simple-crud/
return record.into(MySet::class.java)
}
This fails because to quote documentation for newRecord:
Create a new pre-filled Record that can be inserted into the corresponding table.
This performs roughly the inverse operation of Record.into(Class)
The resulting record will have its internal "changed" flags set to true for all values. This means that UpdatableRecord.store() will perform an INSERT statement. If you wish to store the record using an UPDATE statement, use executeUpdate(UpdatableRecord) instead.
I CAN, of course, check if I have an id, and then either fetch the record from the database or create a new one
fun save(set: MySet): MySet {
set.description = set.description ?: ""
val record = when (val setId = set.id) {
null -> ctx.newRecord(MY_SET, set)
else -> ctx.selectFrom(MY_SET).where(MY_SET.ID.eq(setId)).fetchSingle()
}
//TOOD: update record manually from `set`
record.store()
// "When store() performs an INSERT statement, jOOQ attempts to load any generated keys from the database back into the record."
// cf. https://www.jooq.org/doc/latest/manual/sql-execution/crud-with-updatablerecords/simple-crud/
return record.into(MySet::class.java)
}
But that kind of is a lot of boilerplate code.
I DO have access to the MySetDao but that one just has insert and update, there's no store or upsert, as far as I can see.
Is there a way to turn a POJO into an UpdatableRecord directly or is this fetch-and-manual-update the way to go?
(Worth noting: the MySet POJO used here was generated by jOOQ.)
I have a list of objects which I want to insert into a collection. The mongoTemplate.insert(list); works fine but now I want to modify it to upsert(); as my list can contain duplicate objects which are already inserted into a collection. So what I want is insert entire list and on the go check if the item is already present in the collection then skip it else insert it.
You can try out continueOnError or ordered flag like this:
db.collection.insert(myArray, {continueOnError: true})
OR,
db.collection.insert(myArray, {ordered: false})
You need to create a unique index field of your object's id(if there is no unique constraint). So that it will make error while you try to insert using same id.
Using the unique constraint you insert array or using BulkInsert
For using insert you can set a flag continueOnError: true which can continue insertion whenever error found in case of error because of unique constraint while inserting existing id of object.
The only way to do a bulk-upsert operation is the method MongoCollection.bulkWrite (or at least: the only way I know... ;-))
To use it, you have to convert your documents to the appropriate WriteModel: for upserts on a per-document basis, this is UpdateOneModel.
List<Document> toUpdate = ...;
MongoCollection coll = ...;
// Convert Document to UpdateOneModel<Document>
List<UpdateOneModel<Document>> bulkOperationList = toUpdate.stream()
.map(doc -> new UpdateOneModel<Document>(
Filters.eq("_id", doc.get("_id")), // identify by same _id
doc,
new UpdateOptions().upsert(true)))
.collect(Collectors.toList());
// Write to DB
coll.bulkWrite(bulkOperationList);
(Disclaimer: I only typed this code, I never ran it)
I am using an hibernate NQL query which fetches me two columns :
SELECT object_name,
object_name_location
FROM dbo.object_stacks
WHERE object_id IN (SELECT thumb_nail_obj_id
FROM dbo.uois
WHERE Upper(NAME) LIKE Upper('%testobj%'))
When I select only one column i.e only object name - everything works fine but with two columns I am getting an error
java.lang.ClassCastException: [Ljava.lang.Object; cannot be cast to
java.lang.String
at run time when I try to display result from the list. I tried using String array in the list as well but it doesn't work. Below are my code snippets which give error :
When I use only List :
List<String> Thumbnailpaths = pathquery.list();
System.out.println(Thumbnailpaths.get(i).replace("\\", "\\\\"));
No error at compile time also nothing if leave it as it is but above line to display gives classcast exception.
When I use List array :
List<String[]> Thumbnailpaths = pathquery.list();
System.out.println(Thumbnailpaths.get(i)[0].replace("\\", "\\\\"));
Here again classcast exception at runtime
Also Criteria.ALIAS_TO_ENTITY_MAP doesn't help at all as it makes logic much more complex. I just need 2 column values from a database table.
Please let me know if there is any solution to fetch multiple column results in NQL and then put in list.
Note : Looks like here generics are not displaying and only List is being written in my code snippet
Yes, hibernate will return the Object arrays (Object[]) for this case - return multiple columns. but you still can using the "Entity queries" to return a entity objects rather then the "raw" value.
Unfortunately, Hibernate doesn't provide a standard way to retrieve the results for columns of table and store directly to Entity Object.You have to manually parse the data fetched by your query.
The hibernate query will return List of Object Array i.e. List<Object[]>.
Object[] will contain data from your columns. List is nothing but rows retrieved by query. In your case you can refer following code :
List<Object[]> Thumbnailpaths = pathquery.list();
for(Object[] objArr : Thumbnailpaths)
{
String objName = (String)objArr[0];
String objNameLocation = (String)objArr[1];
System.out.println(objName + " : " +objNameLocation);
}
Above code will help you parse the object[]
I am writing a select query in hql , my task is to activate the bus. First I will get a messege from client as busId#busStatus, so first I look for this perticular busId is active or inactive So I have to write select query but in hibernate query.list() returns list. Here I think list is unnecessary , a single object is enough .
Here is my code ,
String hql="from BusDetailBean where Busid= :busId and bus_status=:busStatus";
Query query = session.createQuery(hql);
query.setParameter("busId", busId);
query.setParameter("busStatus", busStatus);
List<BusDetailBean> busDetails=(List<BusDetailBean>)query.list();
if(busDetails.isEmpty())
{
//my other stuff
}
else
{
//bus ativation stuff
}
My question is the select query returns only one object if list is not empty I have to use for loop in else part. So how can I optimise this code. can anyone help me in this.
You can use query.getSingleResult()
You can use query.setMaxResults(1);
You can get the object at index 0 in the list:
List l = query.list()
if (l.size()>0) {
return l.get(0)
}
I don't think persitence should be mixed with business logic.
What about returning Optional from the persitence layer and whether result is present/absent do something in higher level?
In persistance layer something like:
return query.list()
.stream()
.findFirst()
This, according to docs, will return first result or empty optional if the collection was empty.
And then:
Optional<Bus> optionalBus = repository.find(busId, busStatus);
if (optionalBus.isPresent()) {
something here
} else {
something else
}
by using query.uniqueResult() you don't ensure that if you have many results , then you will get only one of them.
With uniqueResult() you place a guard/contraint at your result set to be aware that this query should always return a unique result.
For this type of problem, the out of the box solution in Hibernate is to use the uniqueResult() method in the Query class:
public Object uniqueResult()
From the Hibernate JavaDocs:
Convenience method to return a single instance that matches the query,
or null if the query returns no results.
Returns: the single result or null
Throws:
NonUniqueResultException - if there is more than one matching result
HibernateException
I want to update multiple rows in My Collection called "Users". Right now I am updating both the rows seperately but I want to do the same in one query.
My current code:
coll.update(new BasicDBObject().append("key", k1), new BasicDBObject().append("$inc", new BasicDBObject().append("balance", 10)));
coll.update(new BasicDBObject().append("key", k2), new BasicDBObject().append("$inc", new BasicDBObject().append("balance", -10)));
How to make these two seperate updates in one statement?
First let me translate your java code to shell script so people can read it :
db.coll.update({key: k1}, {$inc:{balance:10}})
db.coll.update({key: k2}, {$inc:{balance:-10}})
Now, the reason you will never be able to do this in one update is because there is no way to provide a unique update clause per matching document. You could bulk your updates so that you can do this (pseudoish):
set1 = getAllKeysForBalanceIncrease();
set2 = getAllKeysForBalanceDecrease();
db.coll.update({key:{$in:set1}}, {$inc:{balance:10}}, false, true)
db.coll.update({key:{$in:set2}}, {$inc:{balance:-10}}, false, true)
In other words, you can update multiple documents within one atomic write but the update operation will be static for all documents. So aggregating all documents that require the same update is your only optimization path.
The $in clause can be composed in Java through :
ObjectId[] oidArray = getAllKeysEtc();
query = new BasicDBObject("key", new BasicDBObject("$in", oidArray));
In MongoDB you do not have transactions that span multiple documents. Only writes on a document are atomic.
But you can do updates with:
public WriteResult update(DBObject q,
DBObject o,
boolean upsert,
boolean multi)
But note, this will not be in a transaction.