I have controller with method that blocks the Play server thread due to very slow Database query. I need to implement controller method in a way that it don't block the thread.
I have read documentation: http://www.playframework.org/documentation/1.2.4/asynchronous
There's absolutely no examples anywhere on how to do this. The only thing that I found close is this https://github.com/playframework/play/blob/master/samples-and-tests/chat/app/controllers/LongPolling.java
It simply wraps result in await();
When I try to do that it doesn't work.
routes:
GET /blog Controller.blog
Controller (this is not an actual slow query but everything else is identical):
public static void blog() {
String queryStr = "SELECT b FROM Blog b ORDER BY createTime DESC";
JPAQuery q = Blog.find(queryStr);
List<Blog> bList = q.fetch(100);
List<BlogDTO> list = new ArrayList<BlogDTO>(bList.size());
for (Blog b : bList) {
BlogDTO obj = new BlogDTO(b);
list.add(obj);
}
renderJSON(list);
}
I tried List<Blog> bList = await(q.fetch(100)); but that doesn't work.
I have not worked with Future and promises before.
Can anyone give me any pointers on how to approach this?
For me the best way to do this is to use a Job that returns a List object. Then in your controller you can await for the job termination :
public static void blog() {
List<BlogDTO> list = await(new BlogPostJob().now());
renderJSON(list);
}
and you put your jpa code in your job
Because JDBC uses blocking IO, any slow database query will always block a Thread.
The only way seems to be using Job for that purpose.
Related
I'm totally new to coding in general, so this is really my first attempt, so don't shoot me if I ask stupid questions ;) Right now I'm having trouble even understanding the the vast world of backend.
So I'm having some problems in my service, and even deciding which way is the best to go, scanning, querying... what?
So I -think- the way to go for me is scanning... I'm having trouble to retrieve an item from the database, based on the id of that item. Retrieving all items works like a charm, and I need something similar for getting one item. I'm getting confused when searching the web, and not really even understanding the difference for example scanfilter, scanexpression? That's why I haven't even come up with a good attempt... but what I need is scan the table and retrieve the item with the matching id. I tried looking at my method for retrieving all searchCases, and implement it for retrieving one it, as it should look quite the same, but no success...
EDITED method a bit: Method I need help with:
public SearchCase getSearchCase(String id){
//this is obviously for a list, but how do I do it for ONE item?
HashMap<String, AttributeValue> sc = new HashMap<String, AttributeValue>();
sc.put("scId", new AttributeValue().withS(id));
ScanRequest scanRequest = new ScanRequest()
.withTableName(searchCaseTableName)
.withFilterExpression("id = scId");
ScanResult scanResult = client.scan(scanRequest);
?????
return searchCase;
}
As a reference here is the method for retrieving all items, that does work:
public List<SearchCase> getSearchCases() {
final List<SearchCase> cases = new ArrayList<SearchCase>();
ScanRequest scanRequest = new ScanRequest()
.withTableName(searchCaseTableName);
ScanResult result = client.scan(scanRequest);
try {
for (Map<String, AttributeValue> item : result.getItems()) {
SearchCase searchCase = mapper.readValue(item.get("payload").getS(), SearchCase.class);
cases.add(searchCase);
}
} catch (Exception e) {
throw new RuntimeException(e);
}
return cases;
}
It has been forever, but thought I'd post the correct answer I fought with for a long time back in June. So this was the solution that worked for me for retrieving a single item:
public SearchCase getSearchCase(String id) throws Exception {
Table t = db.getTable(searchCaseTableName);
GetItemSpec gis = new GetItemSpec()
.withPrimaryKey("id", id);
Item item = t.getItem(gis);
SearchCase searchCase = mapper.readValue(StringEscapeUtils.unescapeJson(item.getJSON("payload").substring(1)), SearchCase.class);
return searchCase;
}
This method actually took a whole another approach then the way I originally thought I would solve this. So no Scanrequest, but using GetItemSpec and Item instead. This thus caused some funky backslashes in the JSON, so my frontend wouldn't accept before I ran it through StringEscapeUtils.unescapeJson, otherwise worked like a charm.
I'm having trouble to retrieve an item from the database, based on the id of that item
If you want to retrieve an item from DynamoDB based on some unique ID, then use load, which "Loads an object with the hash key given".
Let's say I have the following method in a service:
private void deleteItems(List<Item> itemsToDelete) {
def sql = new Sql(dataSource)
itemsToDelete?.each { Item item ->
sql.execute("DELETE FROM owner_item WHERE item_id = ${item.id}")
item.delete(flush: true, failOnError: true)
flushDatabaseSession();
}
}
How do I create a test for this method in the ItemServiceSpec? When I try it, I get either a DataSource "Must specify a non-null Connection" error or a nullPointerException on sql.
This is my existing test.
#TestFor(ItemService)
#Mock([Item])
#Build([Item])
class SubjectServiceSpec extends Specification {
...
def "delete items"() {
given:
Item item1 = Item.build().save(flush: true)
Item item2 = Item.build().save(flush: true)
Item.count() == 2
DataSource mockDataSource = Mock()
service.dataSource = mockDataSource
1 * deleteItems
when:
service.deleteItems([item1, item2])
then:
Item.count() == 0
}
}
What you are trying to do here, is to mock a dependency (DataSource) of a dependency (Sql). This normally leads to a situation, where you a not 100% aware of how the Sql interacts with the DataSource Object. If Sql changes private interaction with the Datasource in a Version Update, you have to deal with the situation.
Instead of mocking a dependency of a dependency you should the Sql Class directly. For this, the sql has to be some kind of explicit dependency that you can receive via DI or a method parameter. In this case you can just mock the execute call like so (choosen the way of a Expando-Mock, but you could also use Map or the Mock Stuff from Spock):
given:
def sqlMock = new Expando()
sqlMock.execute = { return 'what ever you want or nothing, because you mock a delete operation' }
service.sql = sqlMock
when:
service.deleteItems([item1, item2])
then:
assertItemsAreDeletedAndTheOwnerAsWell()
Thinking about the whole testcase, there a two major problems in my opinion.
The first one is, when you ask yourself what kind of certainty do you really get here by mocking out the whole sql stuff. In this case, the only thing that you are doing here is to interact with the db. When you mock this thing out, then there is nothing anymore that you could test. There is not many conditional stuff or anything that should be backed up by a unit test. Due to this, I would suggest to write only integration spec for this test-case where you have something like a H2DB for testing purposes inplace.
The second thing is, that you actually don't need the Sql Manipulation at all. You can configure GORM and Hibernate in a way do a automatic and transparent deletion of the owner of the item, if the item is deleted. For this, look at the docs (especially the cascade part) from GORM or directly in the Hibernate docs.
To sum it up: use cascade: 'delete' together with a proper integration test and you have a high amount of certainty and less boilerplate code.
I have been using the "jms/serializer": "0.13.*#dev" in order to serialize my objects.
I am using it in a Zend Framework (2) and a Doctrine project.
This is my code:
use JMS\Serializer\SerializerBuilder as SerializerBuilder;
(....)
public function getList() {
$em = $this->getEntityManager();
$repo = $em->getRepository('MyApp\Entity\Product');
$hydrator = new DoctrineHydrator($em);
$data = array();
foreach ($repo->findAll() as $found) {
$data[] = $hydrator->extract($found);
}
$serializer = SerializerBuilder::create()->build();
$jsonContent = $serializer->serialize($data, 'json');
$this->getResponseWithHeader()->setStatusCode(self::OK_200);
return new JsonModel($jsonContent);
}
But I am getting this error:
Resources are not supported in serialized data. Path: MyApp\Entity\FOO -> Doctrine\ORM\PersistentCollection -> MyApp\Entity\Product -> Doctrine\ORM\PersistentCollection -> DoctrineORMModule\Proxy__CG__\MyApp\MyApp\Brand
Apparently you can't serialize persistent collections.
I have googled around and found this Symfony related question. But how can I solve this problem within the stand alone Serializer library?
Thanks very much.
EDIT
Can this have anything to do with JMS annotations? Should I use certain annotations to get this working?
I'm guessing the issue here is that the PersistentCollection may contain a reference to a database connection so JMS can't handle the Resource type.
If you only want the first level serialised then I guess you could hack at it like:
foreach ($repo->findAll() as $found) {
// Set these to basically empty arrays
$found->setRelation1(new \Doctrine\Common\Collections\ArrayCollection);
$found->setRelation2(new \Doctrine\Common\Collections\ArrayCollection);
$data[] = $hydrator->extract($found);
}
Or you could extend the JMS Serialize function to just ignore Resource collections rather than throwing an Exception.
Consider the following schema:
[Work]
id
tags ManyToMany(targetEntity="Tag", inversedBy="works", cascade={"persist"})
[Tag]
id
works_count
works ManyToMany(targetEntity="Work", mappedBy="tags")
works_count is a counter cache for Tag::works.
I have a onFlush listener on Work that checks if Work::tags has changed, and updates each of the tags' works_count.
public function onFlush(OnFlushEventArgs $args)
{
foreach ($uow->getScheduledEntityUpdates() as $work) {
$changedTags = /* update relevant tags and return all the changed ones */
$metadata = $em->getClassMetadata('Acme\Entity\Tag');
foreach ($changedTags as $tag) {
$uow->recomputeSingleEntityChangeSet($metadata, $tag);
}
}
}
Now if I read the changesets of the updated tags, the changes of works_count appears correctly, but they don't get updated in the database..
If I replace recomputeSingleEntityChangeSet() with computeChangeSet() then everything works as expected and the DB is updated, but computeChangeSet() has an #internal Don't call from the outside. annotation on it, so I'm not sure what the consequences are..
Every source on the internet says to use recomputeSingleEntityChangeSet so why doesn't it work in this case?
P.S
The tags are managed by the EntityManager ($em->contains($tag) returns true)
This problem was related with a bug in UnitOfWork and finally it's fixed with the release of Doctrine ORM 2.4.3 on September 11, 2014. See DDC-2996 for details.
It seems that Doctrine 2.2 can merge change sets or generate new change sets, but it needs to know which. If you get it wrong, it will either replace your existing change sets or do nothing at all. I'd be very interested to know if there is a better option than this, or if this is even right.
if($uow->getEntityChangeSet($entity)) {
/** If the entity has pending changes, we need to recompute/merge. */
$uow->recomputeSingleEntityChangeSet($meta, $contact);
} else {
/** If there are no changes, we compute from scratch? */
$uow->computeChangeSet($meta, $entity);
}
In doctrine 2.4.1, use recomputeSingleEntityChangeSet only if you are changing tag in the event listener AND UOW contain tag ChangeSet (Change that happen outside of the event listener). Basically recomputeSingleEntityChangeSet is a function to merge ChangeSet for an entity.
Doc from the function
The passed entity must be a managed entity. If the entity already has a change set because this method is invoked during a commit cycle then the change sets are added whereby changes detected in this method prevail.
NOTE: You need to make sure UOW already have ChangeSet for the entity, otherwise it will not merge.
For future readers, at all cost try to avoid the listeners. Those are hardly testable, your domain should not rely on magic. Consider OP's test case how to achieve the same without Doctrine events:
Work class:
public function addTag(Tag $tag): void
{
if (!$this->tags->contains($tag)) {
$this->tags->add($tag);
$tag->addWork($this);
}
}
Tag class:
public function addWork(Work $work): void
{
if (!$this->works->contains($work)) {
$work->addTag($this);
$this->works->add($work);
$this->worksCount = count($this->works);
}
}
TagTest class:
public function testItUpdatesWorksCountWhenWorkIsAdded()
{
$tag = new Tag();
$tag->addWork(new Work());
$tag->addWork(new Work());
$this->assertSame(2, $tag->getWorkCount());
}
public function testItDoesNotUpdateWorksCountIfWorkIsAlreadyInCollection()
{
$tag = new Tag();
$work = new Work();
$tag->addWork($work);
$tag->addWork($work);
$this->assertSame(1, $tag->getWorkCount());
}
I am creating items on the fly via Sitecore Web Service. So far I can create the items from this function:
AddFromTemplate
And I also tried this link: http://blog.hansmelis.be/2012/05/29/sitecore-web-service-pitfalls/
But I am finding it hard to access the fields. So far here is my code:
public void CreateItemInSitecore(string getDayGuid, Oracle.DataAccess.Client.OracleDataReader reader)
{
if (getDayGuid != null)
{
var sitecoreService = new EverBankCMS.VisualSitecoreService();
var addItem = sitecoreService.AddFromTemplate(getDayGuid, templateIdRTT, "Testing", database, myCred);
var getChildren = sitecoreService.GetChildren(getDayGuid, database, myCred);
for (int i = 0; i < getChildren.ChildNodes.Count; i++)
{
if (getChildren.ChildNodes[i].InnerText.ToString() == "Testing")
{
var getItem = sitecoreService.GetItemFields(getChildren.ChildNodes[i].Attributes[0].Value, "en", "1", true, database, myCred);
string p = getChildren.ChildNodes[i].Attributes[0].Value;
}
}
}
}
So as you can see I am creating an Item and I want to access the Fields for that item.
I thought that GetItemFields will give me some value, but finding it hard to get it. Any clue?
My advice would be to not use the VSS (Visual Sitecore Service), but write your own service specifically for the thing you want it to do.
This way is usually more efficient because you can do exactly the thing you want, directly inside the service, instead of making a lot of calls to the VSS and handle your logic on the clientside.
For me, this has always been a better solution than using the VSS.
I am assuming you are looking to find out what the fields looks like and what the field IDs are.
You can call GetXml with the ID, it returns the item and all the versions and fields set in it, it won't show fields you haven't set.