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.
Related
Sorry for the general question, but is there an approach for still using JPA lazy loading of entities, when developing a restful AngularJS application.
In the old JSF days, it would all just work when a backing bean accessed the list.
I am using EclipseLink and Spring Data, with Jersey for the restful end points.
Regards
I
Generally you'd have to trigger the lazy loading of the entities prior the EntityManager being closed during the lifecycle of the request.
To do so, you can use the "Open EntityManager in View" pattern. Spring provides a Filter you can apply: OpenEntityManagerInViewFilter (read the docs here: http://docs.spring.io/spring/docs/4.1.0.RELEASE/javadoc-api/org/springframework/orm/jpa/support/OpenEntityManagerInViewFilter.html).
Alternatively, you can manually call getMyLazyCollection() on your JPA entity(ies) prior to serializing them to JSON.
I think the best course depends on following.
Are you able to retrieve the fully resolved entity i.e. all of its
components without adversely affecting performance ?
If the answer is Yes then go for resolving the full entity using JPA fetch option=eager.
I the answer is No. I would go for the following approach:-
1) Expose every lazy JPA component/association explicitly as a sub-resource.
e.g.
#Entity
public class Employee {
#Id
private long id;
...
#OneToOne(fetch=FetchType.LAZY)
#JoinColumn(name="ADDR_ID")
private Address homeAddress;
...
}
#Entity
public class Address{
}
2) Expose service as controller(although you can have them separated but I don't recommend)
#RequestMapping(value="/api/employee")
#Controller
public class EmployeeSvc
public Employee getEmployee(#PathVariable empID){
.....
}
#RequestMapping(value="{empID}/homeaddress")
public Address getHomeAddress(#PathVariable empID){
// will serve http://localhost:8080/api/employee/11111/homeaddress
return this.getEmployee(empID).getHomeAddress();
//make sure you are using 2nd Level cache - as you retrieve object twice
}
}
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.
Hello brothers in code!
First question here so I'll try my best to respect all the standards. Correct me if I skip anything and I'll fix it right away.
I'm kind of confused about the approach I should take with my application. I have several EJB projects and JSF projects under the same EAR and, of course, I'd like to define some local interfaces for all of the EJB projects. I have a persistence layer with a couple of modules insipierd by the EAO pattern and an access point to the bussiness layer through a Session Façade.
My intention is to make a "SharedInterfaces" Jar that contains all the Client interfaces (All EJB Client jars in one, if I must say) and all the Interfaces that the entities will implement so I can abstract the projects between themselves (no dependencies, just common interfaces to work together).
How can I turn this "SharedInterfaces" project into a common EJB CLient Jar to be used by all the modules? On the other hand, I can make some interface extension so I don't have to configure a project... still I'm not sure if this common project is on the "best practices" approach.
Well, I pretty much figured it out myself.
The SharedInterfaces project defines the interfaces to be commonly used and when I want to make a LocalInterface for an EJB I simply leave that interface blank and extend the one I defined on SharedInterfaces. The container seems to handle it allright because the interface is a local interface after all (sort of).
Just for the sake of clarity I'll add a simple example of what I did. This is the local interface I create for an EJB:
package org.myapp.managers;
import javax.ejb.Local;
#Local
public interface UserManagerLI extends IUserManager{
}
Then, on SharedInterfaces I simply add the interface IUserManager:
public interface IUserManager {
public IUser newUser();
public void saveOrUpdate(IUser u, boolean hashPass);
public void deleteUser(IUser u);
public boolean checkUserAvailability(String username);
public IUser getUser(String username);
}
Then, to use it I simply made the injection as usual:
#ManagedBean
#SessionScoped
public class LogInBean {
#EJB
private IUserManager userManager;
// Attributes, Setters, Getters and methods
}
Of course, one should ALWAYS be careful about what does he expose. Thinking of the interfaces as contracts of service, one should not be able to access functions he is not supossed to access.
I've been looking for it, for quite and while, and I didn't really find anything that cover how to pull data trough duplex connection for Silverlight (pollingHttpDuplex).
I have setup basic sub/pub application with duplex.
Now I wanted to get list of topics, that are users are connected to. My first thought, was to setup simple DataContract (with only one field TopicName), then get data from Dicionary that I've been using to store current topics.
So I end up with something like this:
[OperationContract]
public List<Topic> GetTopicList()
{
List<Topic> topicList;
topicList = (from p in _sessionIDTopic
select new Topic
{
TopicName = p.Value
}).ToList<Topic>();
return topicList;
}
[DataContract]
public class Topic
{
[DataMember]
public string TopicName { get; set; }
}
I'm doing something wrong here. Because big question is how to send this to the client (Silverlight 4), and bind it to controls ?
Also duplex is essential for application. Changing it for anything else is not an option.
Using a duplex connection here doesn't have any effect on how you get the data to the client. The example you specified looks like simple one way communication. To do that, your client project needs a service reference to the server, and the generated proxy will provide the methods the client needs to access the operations on the server.
I think you should start by looking at a simple WCF example project to see what I mean. If this is not your problem, please rephrase the question.
I have a Silverlight client and a WCF service that I want to have share a class:
[DataContract]
public class DatesAreFun
{
[DataMember]
private readonly DateTime _date;
[DataMember]
private readonly bool _isFun;
public DateTime DateTime { get { return _date; } }
public bool IsFun { get { return _isFun; } }
public DatesAreFun(DateTime date, bool isFun)
{
_date = date;
_isFun = fun;
}
}
The WCF side seems to send the appropriate data across the wire just fine, but the Silverlight side doesn't like it one bit. It is treating the WCF service DatesAreFun class as a different class than my DatesAreFun class.
Any suggestions on how best to remedy this? Thanks!
This is a common issue and has been covered here more than a few times.
When you add your service reference, make sure you click the Advanced button, then ensure you have ticked the Reuse types in referenced assemblies checkbox, and selected the Reuse types in all referenced assemblies option.
You also have to create a new class library assembly that targets the Silverlight runtime. This is because the class library referenced by the WCF services will target the full (or maybe the client profile) version of the .Net framework, which a Silverlight assembly cannot do (in fact a Silverlight assembly can only reference other Silverlight targeted assemblies). In your new class library you can reference the same physical files that the full version of the class library is using, this is detailed more here (i had the same question once upon a time...). You could also pick your way through this bunch of search results for related questions.
Depending on how you do things you may find you also have to trawl through the Reference.cs file of the Service Reference, and change the namespaces of the named data entities. (This file will get regenerated if you update or reconfigure the service reference).