Note that there are some explanatory texts on larger screens.

plurals
  1. POGoogle App Engine: Different behavior locally vs deployed, error: can't operate on multiple entity groups in a single transaction
    primarykey
    data
    text
    <p>I have a Java Google App Engine web application that allows user upload of images. Locally, it works great. However, once I deploy it to "the cloud", and I upload an image, I get the following error:</p> <p>java.lang.IllegalArgumentException: can't operate on multiple entity groups in a single transaction.</p> <p>I use the blobstore to store the images (<a href="http://code.google.com/appengine/docs/java/blobstore/overview.html#Writing_Files_to_the_Blobstore" rel="nofollow">Blobstore Reference</a>). My method is below:</p> <pre><code> @RequestMapping(value = "/ajax/uploadWelcomeImage", method = RequestMethod.POST) @ResponseBody public String uploadWelcomeImage(@RequestParam("id") long id, HttpServletRequest request) throws IOException, ServletException { byte[] bytes = IOUtils.toByteArray(request.getInputStream()); Key parentKey = KeyFactory.createKey(ParentClass.class.getSimpleName(), id); String blobKey = imageService.saveImageToBlobStore(bytes); imageService.save(blobKey, parentKey); return "{success:true, id:\"" + blobKey + "\"}"; } </code></pre> <p>You'll notice that this method first calls "imageService.saveImageToBlobStore". This is what actually saves the bytes of the image. The method "imageService.save" takes the generated blobKey and wraps it in an ImageFile object, which is an object that contains a String blobKey. My website references imageFile.blobKey to get the correct image to display. The "saveImageToBlobStore" looks like this:</p> <pre><code>@Transactional public String saveImageToBlobStore(byte[] bytes) { // Get a file service FileService fileService = FileServiceFactory.getFileService(); // Create a new Blob file with mime-type "text/plain" AppEngineFile file = null; try { file = fileService.createNewBlobFile("image/jpeg"); } catch (IOException e) { e.printStackTrace(); } // Open a channel to write to it boolean lock = true; FileWriteChannel writeChannel = null; try { writeChannel = fileService.openWriteChannel(file, lock); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (FinalizationException e) { e.printStackTrace(); } catch (LockException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } // This time we write to the channel using standard Java try { writeChannel.write(ByteBuffer.wrap(bytes)); } catch (IOException e) { e.printStackTrace(); } // Now finalize try { writeChannel.closeFinally(); } catch (IllegalStateException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } // Now read from the file using the Blobstore API BlobKey blobKey = fileService.getBlobKey(file); while (blobKey == null) { //this is hacky, but necessary as sometimes the blobkey isn't available right away try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } blobKey = fileService.getBlobKey(file); } // return return blobKey.getKeyString(); } </code></pre> <p>My other save method looks like this:</p> <pre><code>public void save(String imageFileBlobKey, Key parentKey) { DatastoreService datastore = DatastoreServiceFactory .getDatastoreService(); Entity imageFileEntity = new Entity("ImageFile", parentKey); imageFileEntity.setProperty("blobKey", imageFileBlobKey); datastore.put(imageFileEntity); } </code></pre> <p>Like I said before, it works locally, but not deployed. The error is on the call to saveImageToBlobstore, specifically on the "fileservice.getBlobKey(file)". Commenting out this line eliminates the error, but I need this line to save the bytes of the image to the blob store. </p> <p>I also tried commenting other lines (see below), with no luck. Same error for this:</p> <pre><code>@RequestMapping(value = "/ajax/uploadWelcomeImage", method = RequestMethod.POST) @ResponseBody public String uploadWelcomeImage(@RequestParam("id") long id, HttpServletRequest request) throws IOException, ServletException { //byte[] bytes = IOUtils.toByteArray(request.getInputStream()); //Key parentKey= KeyFactory.createKey(ParentClass.class.getSimpleName(), //id); byte[] bytes = {0,1,0}; String blobKey = imageService.saveImageToBlobStore(bytes); //imageService.save(blobKey, parentKey); return "{success:true, id:\"" + blobKey + "\"}"; } </code></pre> <p>Any ideas? I am using GAE 1.5.2. Thank you!</p> <p><strong>UPDATE, SOLUTION FOUND:</strong> I took some code out of the transactional "saveImageToBlobStore" and moved it up a level. See below:</p> <pre><code>@RequestMapping(value = "/ajax/uploadWelcomeImage", method = RequestMethod.POST) @ResponseBody public String uploadWelcomeImage(@RequestParam("id") long id, HttpServletRequest request) throws IOException, ServletException { byte[] bytes = IOUtils.toByteArray(request.getInputStream()); Key parentKey = KeyFactory.createKey(ParentClass.class.getSimpleName(), id); //pulled the following out of transactional method: AppEngineFile file = imageService.saveImageToBlobStore(bytes); FileService fileService = FileServiceFactory.getFileService(); //code below is similar to before////////////// BlobKey key = fileService.getBlobKey(file); String keyString = key.getKeyString(); imageService.save(keyString, parentKey); return "{success:true, id:\"" + keyString + "\"}"; </code></pre>
    singulars
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload