I have an Employee class
#PersistenceCapable(identityType = IdentityType.APPLICATION)
public class Employee {
#PrimaryKey
#Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
private Key key;
#Persistent
private String firstName;
#Persistent
private String lastName;
#Persistent
private Date hireDate;
public Employee(String firstName, String lastName, Date hireDate) {
this.firstName = firstName;
this.lastName = lastName;
this.hireDate = hireDate;
}
// Accessors for the fields. JDO doesn't use these, but your application does.
public Key getKey() {
return key;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public Date getHireDate() {
return hireDate;
}
public void setHireDate(Date hireDate) {
this.hireDate = hireDate;
}
}
I have used the JDO for the app engine. Now I want to share this code between server and client. In which package should I keep this. In fact I have tried both way. Neither worked out. Please share if you have already done this type of codes.
If what you are looking for is instantiating of your entities in both client and server, putting the classes under the "client" package will do the trick.
But if you are trying to pass your persistent entities through RPC, that probably wont work out of the box. DataNucleus "enhaces" the bytecode, and RPC can't serialize then. Hibernate has a similar problem, please take a look at this article, it explains the problem very well and presents alternatives.
I am creating DTOs to workaround this problem. It is a little more work, but it really depends on how many Entities you have.
I've done this before, but just in a small test app. Assuming you're using GWT-RPC, it should work pretty smoothly. You'll have to do two things:
Put the code in a 'client' namespace, i.e. in a directory that's getting compiled by GWT. You can still use this code on the server.
Hit compile and start fixing errors. The main one you'll find is that the 'Key' type isn't available in GWT land. You can use a string-encoded key instead. See the "key as encoded string" section in the relevant documentation.
If you're not using GWT-RPC, you're on your own. JSON is attractive for this purpose but requires significant legwork. This should be better in GWT 2.0 but won't entirely go away.
We probably need more detail, as you could be hitting a number of problems, but here's some tips:
The package doesn't matter so much as long as both the GWT compiler and javac can see it. I keep shared code in a package appropriately named... "shared". :)
Key is not available in GWT, so use an encoded string Key.
JDO is tricky, but workable. Newer versions of GWT (after Java AppEngine was released) have been able to handle DataNucleus' JDO enhancement. I'd make sure you are working off of trunk or a recent snapshot, in case DataNucleus is your problem.
Make sure you detach your objects before sending them to the client.
That's why I am using the low-level api. I wrote a helper class that converts an entity to a pojo and back. This way, I get the Entity which gets converted into my desired POJO that then goes to the client. From the client, the same POJO goes back to the server gets converted into an entity by my helper class and then a simple "put" call does the trick. You don't need to dettach/attach anything... I can share some code if you want.
Related
I tried to inject with #Autowired annotation a repository into changelog
and it doesn't get injected.
Config uses spring application context
#Bean
public SpringBootMongock mongock(ApplicationContext springContext, MongoClient mongoClient) {
return new SpringBootMongockBuilder(mongoClient, "yourDbName", "com.package.to.be.scanned.for.changesets")
.setApplicationContext(springContext)
.setLockQuickConfig()
.build();
}
And the changelog
#ChangeLog(order = "001")
public class MyMigration {
#Autowired
private MyRepository repo;
#ChangeSet(order = "001", id = "someChangeId", author = "testAuthor")
public void importantWorkToDo(DB db){
repo.findAll() // here null pointer
}
}
firstly, notice that if you are using repositories in your changelogs, it's a bad practice to use it for writes, as it won't be covered by the lock mechanism(this is feature is coming soon), only for reads.
To inject your repository(or any other dependency) you simply need to inject it in your changeSet method signature, like this:
#ChangeLog(order = "001")
public class MyMigration {
#ChangeSet(order = "001", id = "someChangeId", author = "testAuthor")
public void importantWorkToDo(MongoTemplate template, MyRepository repo){
repo.findAll(); this should work
}
}
Notice that you should use the last version(at this moment 3.2.4) and DB class is not supported anymore. Please use MongoDatabase or MongoTemplate(preferred).
Documentation to Mongock
we have recently released the version 4.0.7.alpha, which among other things allows you to use Spring repositories(and any other custom bean you wish) in your changeSets with no problem. You can insert, update, delete and read. It will be safely covered by the lock.
The only restriction is that it needs to be an interface, which should be the common case for Spring repositories.
Please take a look to this example
Say this is my classes
#Entity
public class Library{
...
}
#Entity
public class Book{
#Load
#Parent
#ApiResourceProperty(ignored = AnnotationBoolean.TRUE)
private Ref<Library> libraryRef;
#Ignore
private Library library;
}
I want to send List<Book> to the "android" client: I don't want the android client to get libraryRef but I want the client to get library
Here is the data access method I have now
public static List< Book > getAllBooks(){
return OfyService.ofy().load().type(Book.class).list();
}
My endpoint will just return List<Book> to android. I believe I have accomplished the first part: make sure datastore does not store library but libraryRef. But how do I accomplish the second part: make sure the client gets library?
I am sure it is not yet loaded. How do I make sure it is loaded? Do I have to use my own for-loop for iteration?
My advice for anyone working with code shared between client and server is to make a clean separation between your API objects and your domain objects. It's a little more work up front to make DTOs but it makes your whole system more flexible - if you want to change your domain objects, you don't risk breaking a zillion mobile phone apps that are on a slow (or nonexistant) upgrade cycle.
I am using appengine cloud endpoints and objectify. I have previously deployed these endpoints before and now I am updating them and it is not working with Objectify. I have moved to a new machine and running latest appengine 1.8.6. Have tried putting objectify in the classpath and that did not work. I know this can work, what am I missing??
When running endpoints.sh:
Error: Parameterized type
com.googlecode.objectify.Key<MyClass> not supported.
UPDATE:
I went back to my old computer and ran endpoints.sh on same endpoint and it worked fine. Old machine has 1.8.3. I am using objectify 3.1.
UPDATE 2:
Updated my old machine to 1.8.6 and get same error as other machine. Leaves 2 possibilities:
1) Endpoints no longer support objectify 3.1
or
2) Endpoints have a bug in most recent version
Most likely #1...I've been meaning to update to 4.0 anyways...
Because of the popularity of Objectify, a workaround was added in prior releases to support the Key type, until a more general solution was available. Because the new solution is available, the workaround has been removed. There are two ways you can now approach the issue with the property.
Add an #ApiResourceProperty annotation that causes the key to be omitted from your object during serialization. Use this approach if you want a simple solution and don't need access to the key in your clients.
Add an #ApiTransformer annotation that provides a compatible mechanism to serialize/deserialize the field. Use this approach if need access to the key (or a representation of it) in your clients. As this requires writing a transformer class, it is more work than the first option.
I came up with the following solution for my project:
#Entity
public class Car {
#Id Long id;
#ApiResourceProperty(ignored = AnnotationBoolean.TRUE)
Key<Driver> driver;
public Key<Driver> getDriver() {
return driver;
}
public void setDriver(Key<Driver> driver) {
this.driver = driver;
}
public Long getDriverId() {
return driver == null ? null : driver.getId();
}
public void setDriverId(Long driverId) {
driver = Key.create(Driver.class, driverId);
}
}
#Entity
public class Driver {
#Id Long id;
}
I know, it's a little bit boilerplate, but hey - it works and adds some handy shortcut methods.
At first, I did not understand the answer given by Flori, and how useful it really is. Because others may benefit, I will give a short explanation.
As explained earlier, you can use #ApiTransformer to define a transformer for your class. This would transform an unserializable field, like those of type Key<myClass> into something else, like a Long.
It turns out that when a class is processed by GCE, methods called get{fieldName} and set{FieldName} are automatically used to transform the field {fieldName}. I have not been able to find this anywhere in Google's documentation.
Here is how I use it for the Key{Machine} property in my Exercise class:
public class Exercise {
#ApiResourceProperty(ignored = AnnotationBoolean.TRUE)
public Key<Machine> machine;
// ... more properties
public Long getMachineId() {
return this.machine.getId();
}
public void setMachineId(Long machineId) {
this.machine = new Key<Machine>(Machine.class, machineId);
}
// ...
}
Others already mentioned how to approach this with #ApiResourceProperty and #ApiTransformer. But I do need the key available in client-side, and I don't wanna transform the whole entity for every one. I tried replacing the Objectify Key with com.google.appengine.api.datastore.Key, and it looks like it worked just fine as well in my case, since the problem here is mainly due to that endpoint does not support parameterized types.
I tried to solve by myself, but... Looks like I need help from people.
I have Business Silverlight application with WCF RIA and EntityFramework. Access to Database I get via LinqToEntites.
Common loading data from database I making by this:
return DbContext.Customers
This code returns full Customers table from DataBase. But sometimes I do not need to show all data. Easy way is use linq filters in client side by next code:
public LoadInfo()
{
...
var LO1 = PublicDomainContext.Load(PublicDomainContext.GetCustomersQuery());
LO1.Completed += LO1Completed;
...
}
private void LO1Completed(object sender, EventArgs eventArgs)
{
...
DatatViewGrid.ItemsSource = null;
DatatViewGrid.ItemsSource = loadOperation.Entities.Where(c=>c ...filtering...);
//or PublicDomainContext.Customers.Where(c=>c ...filtering...)
...
}
However this way has very and very important flaw: all data passing from server to client side via DomainService may be viewed by applications like Fiddler. So I need to come up with another way.
Task: filter recieving data in server side and return this data.
Way #1: LinqToEntites has a beautiful projection method:
//MSDN Example
var query =
contacts.SelectMany(
contact => orders.Where(order =>
(contact.ContactID == order.Contact.ContactID)
&& order.TotalDue < totalDue)
.Select(order => new
{
ContactID = contact.ContactID,
LastName = contact.LastName,
FirstName = contact.FirstName,
OrderID = order.SalesOrderID,
Total = order.TotalDue
}));
But, unfortunately, DomainServices cannot return undefined types, so this way won't work.
Way #2: I found next solution - make separate DTO classes (DataTransferObject). I just read some samples and made on the server side next class:
[DataContract]
public partial class CustomerDTO
{
[DataMember]
public int ISN { get; set; }
[DataMember]
public string FIO { get; set; }
[DataMember]
public string Listeners { get; set; }
}
And based this class I made a row of methods which return filtered data:
[OperationContract]
public List<CustomerDTO> Customers_Common()
{
return DbContext.Customers....Select(c => new CustomerDTO { ISN = c.ISN, FIO = c.FIO, Listeners = c.Listeners }).ToList();
}
And this works fine, all good...
But, there is strange problem: running application locally does not affect any troubles, but after publishing project on the Web Site, DomainService returns per each method HTTP 500 Error ("Not Found" exception). Of course, I cannot even LogIn into my application. DomainService is dead. If I delete last class and new methods from application and republish - all works fine, but without speacial filtering...
The Question: what I do wrong, why Service is dying with new classes, or tell me another way to solve my trouble. Please.
U P D A T E :
Hey, finally I solved this!
There is an answer: Dynamic query with WCF RIA Services
Your best shot is to find out what is causing the error. For that, override the OnError method on the DomainService like this:
protected override void OnError(DomainServiceErrorInfo errorInfo)
{
/* Log the error info to a file. Don't forget inner exceptions.
*/
base.OnError(errorInfo);
}
This is useful, because only two exceptions will be passed to the client, so if there are a lot of nested inner exceptions, you should still be able to see what actually causes the error.
In addition, you can inspect the error by attaching the debugger to the browser instance you are opening the site with. In VS2010 this is done by doing [Debug] -> [Attach to Process] in the menu-bar.
I am struggling while handling sessions in GAE. I am trying to store a two classes and a string in session. Although on DEV environment it runs fine, on production a class and a string are not being persisted in session. The class that is not getting saved as a session attribute is as follows:
#PersistenceCapable(detachable="true")
public class Agent implements Serializable{
#PrimaryKey
#Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
private Long id;
#Persistent private String name; //Name of the Agency
#Element(dependent = "true")
private List<Contact> contacts = new ArrayList<Contact>();
#Element(dependent = "true")
private List<Agency> agencies = new ArrayList<Agency>();
#Persistent private List<Long> subAgents = new ArrayList<Long>();
#Persistent private Date createdOn = new Date();
}
I would like to mention again that it works fine on DEV Environment but on production I get values as null. As you can see I have made the class implement Serializable. But I think it is not the problem because I am setting one more attribute as a simple string and that also is failing (I get the attribute value as null). Session however is created as I can see it at the backend and also there is one more class which is persisted in session.
Anybody have suggestions? Thanks in advance.
Your problem is probably related to either:
GAE often serializes sessions almost immediately, dev environment doesn't. So all objects in your graph must implement Serializable.
BUT EVEN MORE LIKELY is that after you modify a session variable, you must do something like req.getSession().setAttribute(myKey,myObj) - it WILL NOT see changes in your object and automatically write them back to the session... so the session attributes will have the value of whatever they had when they were last set.
Problem #2 above cost me countless time and pain until I tripped over (via a lengthy process of elimination).
Have you enabled sessions in your configuration file?
http://code.google.com/intl/en/appengine/docs/java/config/appconfig.html#Enabling_Sessions
Making classes Agency and Contact Serializable solves the problem. That mean each and every object (be it nested or otherwise) which is present inside a session attribute should be serializable.