Still fairly new to Spring (and StackOverflow) so please forgive me if there's an obvious answer here.
Program I'm working on has 3 model classes: Star, Location, Observation. I've got the first two wired up to MySQL and working just fine. I'm testing each one before moving on so the third isn't yet and is using an ArrayList to store the Observations for the time being. However an Observation needs to retrieve info from the Location. But when I try to get that data out through the LocationDao, it keeps returning NULL. However, in one of my controllers where I need to access the Location data to display it, it works just fine. I'm not seeing a difference between the two.
Relevant code:
Location Model Class
package StarCatalog.models;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
#Entity
public class Location {
#Id
#GeneratedValue
private int locationId;
#NotNull
#Size(min=3, max=25, message="Name out of bounds")
private String locationName;
#NotNull(message = "Cannot be blank")
private Double latitude;
#NotNull(message = "Cannot be blank")
private Double longitude;
// Constructors
public Location() { }
public Location(String aLocation, Double aLatitude, Double aLongitude) {
locationName = aLocation;
latitude = aLatitude;
longitude = aLongitude;
}
// Getters & Setters
public int getLocationId() {
return locationId;
}
public String getLocationName() {
return locationName;
}
public void setLocationName(String location) {
this.locationName = location;
}
public Double getLatitude() {
return latitude;
}
public void setLatitude(Double latitude) {
this.latitude = latitude;
}
public Double getLongitude() {
return longitude;
}
public void setLongitude(Double longitude) {
this.longitude = longitude;
}
}
Location Dao
package StarCatalog.models.data;
import StarCatalog.models.Location;
import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;
import javax.transaction.Transactional;
#Repository
#Transactional
public interface LocationDao extends CrudRepository<Location, Integer> {
}
Observation Model Class
package StarCatalog.models;
import StarCatalog.models.data.LocationDao;
import org.springframework.beans.factory.annotation.Autowired;
import javax.validation.constraints.NotNull;
public class Observation {
#Autowired
private LocationDao locationDao;
// Primary Key
private int observationId;
private static int nextId = 0;
// Altitude
#NotNull
private Double altitude;
// Azimuth
#NotNull
private Double azimuth;
// ST
#NotNull
private int siderealTimeH;
#NotNull
private int siderealTimeM;
private Double siderealTimeDeg;
// Lat
private Double latitude;
// RA
private Double rightAscension;
// Dec
private Double declination;
// Foreign key of Location
#NotNull
private int locationId;
// Foreign key of Star
#NotNull
private Integer objectId;
// Constructor
public Observation() {
observationId = nextId;
nextId = nextId + 1;
}
// Calculators
public void setLatitude() {
latitude = locationDao.findOne(locationId).getLatitude(); // Error here - locationDao = null
}
// Cut non-applicable code
StarController (Where it works)
package StarCatalog.controllers;
import StarCatalog.models.*;
import StarCatalog.models.data.LocationDao;
import StarCatalog.models.data.StarDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.Errors;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import javax.validation.Valid;
import java.util.ArrayList;
#Controller
#RequestMapping("star")
public class StarController {
#Autowired
private StarDao starDao;
#Autowired
private LocationDao locationDao;
// Index
#RequestMapping("")
public String index(Model model) {
Iterable<Star> stars = starDao.findAll();
ArrayList<Observation> observations = ObservationData.getAll();
Iterable<Location> locations = locationDao.findAll(); //This one works just fine.
model.addAttribute("stars", stars);
model.addAttribute("observations", observations);
model.addAttribute("locations", locations);
model.addAttribute("title", "Uranometria 2.0");
return "star/index";
}
I've tried swapping out the findOne in the Observation for findAll just in case that was the problem, but since it's locationDao that's coming back null, it doesn't matter which I use.
If it helps, here's the error it throws:
java.lang.NullPointerException: null
at StarCatalog.models.Observation.setLatitude(Observation.java:58) ~[main/:na]
First of all your whole design is a bit off.
You seem to use a table named Location which maps to a respective
database table and then a class named Observation which seems to be
a super table of the Location table storing data regarding this and
having a reference to the location table.
What you have in place is not a valid table hierarchy desing.
Ideally, the Observation table should be the top level entity,
having a one to many relation with the Location table. You can
check online on how to map this relation very easily.
You do not need an extra ObservationData class just to wrap an
arraylist in there to retrieve the locations. With a one to many
design, your Observation class will containg a list or better yet a
set that will contain the location information for each observation.
The reason why you LocationDao is null in Observation has to do
with the fact, that the class in question is not a Spring managed
bean. Hence Spring does not know how to inject the bean in question
LocationDao. You can get around this, by annotating the Observation
class with #Component which will bring this into Spring context
thus allowing you to do so, but I would recommend against doing this
(see above on how to properly map a relation between them).
LocationDao this is obviously not a DAO, hence the naming
convention is wrong. Also it does not need the #Transactional
annotation.
Avoid injecting beans via field injection and prefer to do this via
constructor.
I hope all the above help.
Related
I'm building a Spring Boot app using CosmosDB as my database. All functions work (creating an item, updating one, get all, get by id,...), apart from delete functions. They don't do anything and since their output is void, it doesn't give me any logs either.
The DAO class:
package projects.trashcanapplication.trashcan.dao;
import com.azure.spring.data.cosmos.core.mapping.Container;
import com.azure.spring.data.cosmos.core.mapping.GeneratedValue;
import com.azure.spring.data.cosmos.core.mapping.PartitionKey;
import org.springframework.data.annotation.Id;
import projects.trashcanapplication.trashcan.models.Address;
import projects.trashcanapplication.trashcan.models.FillStatus;
#Container(containerName = "trashcanData")
public class TrashcanDao{
#GeneratedValue
private String attachments;
private FillStatus fillStatus;
#GeneratedValue
private String rid;
private Address address;
#Id
#PartitionKey
#GeneratedValue
private String id;
#GeneratedValue
private String self;
#GeneratedValue
private String etag;
#GeneratedValue
private int ts;
public TrashcanDao(Address address, FillStatus fillStatus) {
this.fillStatus = fillStatus;
this.address = address;
}
public String getAttachments(){
return attachments;
}
public FillStatus getFillStatus(){
return fillStatus;
}
public String getRid(){
return rid;
}
public Address getAddress(){
return address;
}
public String getId(){
return id;
}
public String getSelf(){
return self;
}
public String getEtag(){
return etag;
}
public int getTs(){
return ts;
}
}
The repository
package projects.trashcanapplication.trashcan.repositories;
import com.azure.spring.data.cosmos.repository.ReactiveCosmosRepository;
import projects.trashcanapplication.trashcan.dao.TrashcanDao;
public interface TrashcanRepository extends ReactiveCosmosRepository<TrashcanDao, String> {
}
The service calling the repository
package projects.trashcanapplication.trashcan.services;
import com.azure.cosmos.models.PartitionKey;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import projects.trashcanapplication.trashcan.dao.TrashcanDao;
import projects.trashcanapplication.trashcan.models.Trashcan;
import projects.trashcanapplication.trashcan.repositories.TrashcanRepository;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
#Slf4j
#Service
#AllArgsConstructor
public class TrashcanServiceImpl implements TrashcanService {
private final TrashcanRepository trashcanRepository;
private final TrashcanMapper trashcanMapper;
public Flux<Trashcan> getAllTrashcans() {
return trashcanRepository.findAll().map(trashcanMapper::fromDaoToTrashcan);
}
public Mono<Trashcan> getTrashcanById(String id) {
return trashcanRepository.findById(id).map(trashcanMapper::fromDaoToTrashcan);
}
public String createTrashcan(Trashcan trashcan) {
TrashcanDao saveTrashcan = trashcanMapper.fromTrashcanToDao(trashcan);
trashcanRepository.save(saveTrashcan).subscribe();
return saveTrashcan.getId();
}
public void deleteTrashcan(String id) {
trashcanRepository.deleteById(id, new PartitionKey(id));
log.info(String.format("Deleted trashcan %s", id));
}
}
I have a dataloader temporarily set up to populate my DB with an item upon running the app. The deleteAll() function doesn't work here either.
package projects.trashcanapplication.trashcan.services;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import projects.trashcanapplication.trashcan.dao.TrashcanDao;
import projects.trashcanapplication.trashcan.models.Address;
import projects.trashcanapplication.trashcan.models.FillStatus;
import projects.trashcanapplication.trashcan.repositories.TrashcanRepository;
import javax.annotation.PostConstruct;
#Slf4j
#Component
#AllArgsConstructor
public class DataLoader {
private final TrashcanRepository trashcanRepository;
#PostConstruct
void loadData() {
Address address1 = new Address("Begijnendijk", "3130", "Liersesteenweg", "181");
trashcanRepository.deleteAll();
trashcanRepository.save(new TrashcanDao(address1, FillStatus.EMPTY))
.flatMap(trashcanRepository::save)
.thenMany(trashcanRepository.findAll())
.subscribe(trashcan -> log.info(trashcan.getId().toString()))
;
}
}
You're not subscribing anywhere, so the reactive stream isn't executed.
You could solve that by subscribing manually:
trashcanRepository.deleteAll().subscribe()
However, this is not a good practice, and certainly not in your DataLoader as you can't guarantee the order in which the save/delete-logic is executed (maybe the TrashcanDao is saved before you delete everything).
To solve this, you should create a proper reactive stream:
trashcanRepository
.deleteAll()
.then(trashcanRepository.save(new TrashcanDao(address1, FillStatus.EMPTY)))
.thenMany(trashcanRepository.findAll())
// Your previous subscribe() shouldn't compile since it should contain List<TrashcanDao>
.subscribe(trashcans -> log.info(trashcans.size()));
I am trying my hands at Hibernate Relation Mapping(OneToOne, etc) exercises using Spring Boot. Before you ask, I have already consulted this link : [https://stackoverflow.com/questions/11104897/hibernate-attempted-to-assign-id-from-null-one-to-one-property-employee]. I understand that the weak entity needs to have a ref to the parent entity, but I am not able to figure out, why I need to do that explicitly in Person class constructor?
The Codes are as follows.
SpringApplication:
package com.OneToOne.OneToOneMappingPractice;
import java.util.Arrays;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
#SpringBootApplication
public class App
{
public static void main( String[] args )
{
ApplicationContext applContext = SpringApplication.run(App.class, args);
String[] beanNames = applContext.getBeanDefinitionNames();
Arrays.sort(beanNames);
for(String beanName : beanNames)
System.out.println(beanName);
}
}
CRUDController.java:
package com.OneToOne.OneToOneMappingPractice;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
#RestController
#CrossOrigin
public class CRUDController
{
private static int randomNumber=(int) Math.random();
#Autowired
private CRUDControllerRepository repository;
#GetMapping(value="/add")
public void add()
{
Person person = new Person("Person"+randomNumber, "Street"+randomNumber, randomNumber);
repository.save(person);
randomNumber+=1;
}
#GetMapping(value="/getAll")
public List<Person> getAll()
{
return repository.findAll();
}
#DeleteMapping(value="/deleteAll")
public void deleteAll()
{
repository.deleteAll();
}
}
Person.java:
package com.OneToOne.OneToOneMappingPractice;
import javax.persistence.Id;
import javax.persistence.OneToOne;
import javax.persistence.PrimaryKeyJoinColumn;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
#Entity
public class Person
{
private String name;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "user_id")
private int Id;
#OneToOne(mappedBy="person", cascade = CascadeType.ALL)
#PrimaryKeyJoinColumn
private Address address;
public Person() {}
public Person(String name, String streetName, int house_number)
{
super();
this.name = name;
this.address=new Address();
this.address.setStreetName(streetName);
this.address.setHouse_number(house_number);
//this.address.setPerson(this);
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getId() {
return Id;
}
public void setId(int id) {
Id = id;
}
public Address getAddress() {
return address;
}
public void setAddress(Address address) {
this.address = address;
}
}
Address.java:
package com.OneToOne.OneToOneMappingPractice;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.MapsId;
import javax.persistence.OneToOne;
#Entity
public class Address {
#Id
#Column(name="user_id")
private int Id;
private int house_number;
private String streetName;
#OneToOne
#MapsId
#JoinColumn(name = "user_id")
private Person person;
public Address(){}
public int getHouse_number() {
return house_number;
}
public void setHouse_number(int house_number) {
this.house_number = house_number;
}
public String getStreetName() {
return streetName;
}
public void setStreetName(String streetName) {
this.streetName = streetName;
}
// public Person getPerson() {
// return person;
// }
public void setPerson(Person person) {
this.person = person;
}
}
CRUDControllerRepository.java:
package com.OneToOne.OneToOneMappingPractice;
import java.util.List;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;
#Repository
#Transactional
public interface CRUDControllerRepository extends JpaRepository<Person, Integer>
{
Person save(Person person);
void deleteAll();
List<Person> findAll();
}
Following are my questions :
As you can see, in the Person class parameterized constructor, I have commented out the line : this.address.setPerson(this);. If I keep this line commented out, I get the exception : "attempted to assign id from null one-to-one property [com.OneToOne.OneToOneMappingPractice.Address.person]; nested exception is org.hibernate.id.IdentifierGenerationException: attempted to assign id from null one-to-one property [com.OneToOne.OneToOneMappingPractice.Address.person]". If I remove the comment syntax, it works perfectly. Why do I need to explicitly do this? Isn't the #OneToOne annotation supposed to take care of these references by itself?
2.If I enable the Person getPerson() method in the Address class, it recursively goes on, until the stack explodes: "Cannot render error page for request [/getAll] and exception [Could not write JSON: Infinite recursion (StackOverflowError); nested exception is com.fasterxml.jackson.databind.JsonMappingException".
Why cant Hibernate itself determine that it needs to stop at that boundary itself, instead of fetching the Parent Object again?
Am I missing something here about these mapping annotations, or anything else?
1- As you can see, in the Person class parameterized constructor, I
have commented out the line : this.address.setPerson(this);. If I keep
this line commented out, I get the exception : "attempted to assign id
from null one-to-one property
Hibernate will not set it explicitly because it does not know to which person this address belongs to you need to specify that explicitly.
The purpose of #OneToOne is to tell hibernate where to get the rest of the data when it is already mapped.
2.If I enable the Person getPerson() method in the Address class, it recursively goes on, until the stack explodes: "Cannot render error
page for request [/getAll] and exception [Could not write JSON:
Infinite recursion (StackOverflowError); nested exception is
com.fasterxml.jackson.databind.JsonMappingException". Why cant
Hibernate itself determine that it needs to stop at that boundary
itself, instead of fetching the Parent Object again?
The exception is caused by Jackson serializer and not from hibernate.
you can look at the examples here to see how it is fixed https://www.baeldung.com/jackson-bidirectional-relationships-and-infinite-recursion.
Note: This is a project which has a connection with database on other tables. I just made a new table, but i must have something wrong in my codes, because i cant get what i want.
I have a City table, and this table has 3 columns, named id, name, city_id. And i imported a csv file, so when i query, I can see some data.
I wrote Entity, Repository, Controller, and Service, in Java on Eclipse
What should I do? For example, when i search like localhost:8181/mfc/city/getAllCities that should give me all the cities as json
Could you tell me what i should add?
City.java
package com.mfc.entity;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
#Entity
#Table(name="city")
public class City{
#Id
#Column(name="id")
#GeneratedValue(strategy=GenerationType.IDENTITY)
int id;
#Column(name="city_name")
String cityName;
#Column(name="city_id")
int cityId;
public City() {
super();
}
public City(int id, String cityName, int cityId) {
super();
this.id = id;
this.cityName = cityName;
this.cityId = cityId;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getCityName() {
return cityName;
}
public void setCityName(String cityName) {
this.cityName = cityName;
}
public int getCityId() {
return cityId;
}
public void setCityId(int cityId) {
this.cityId = cityId;
}
}
CityController.java
package com.mfc.admin.controller;
import java.util.List;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import com.mfc.admin.service.CityService;
import com.mfc.entity.City;
#RestController
#RequestMapping("/city")
public class CityController {
private static final Logger logger = LogManager.getLogger(CityController.class);
#Autowired
CityService cityService;
#RequestMapping(value="/getAllCities", method=RequestMethod.GET, headers = "Accept=application/json")
public List getCities() {
logger.trace("CityController: getAllCities begins");
List listOfCities = cityService.getAllCities();
logger.trace("CityController: getAllCities ends");
return listOfCities;
}
#RequestMapping(value="/getCity/{id}", method=RequestMethod.GET, headers = "Accept=application/json")
public City getCityById(#PathVariable int id) {
return cityService.getCity(id);
}
}
CityService.java
package com.mfc.admin.service;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.mfc.entity.City;
import com.mfc.repository.CityRepository;
#Service("cityService")
public class CityService {
#Autowired
CityRepository cityDTO;
#Transactional
public List getAllCities() {
return cityDTO.getAllCities();
}
#Transactional
public City getCity(int id) {
return cityDTO.getCity(id); // getCity is red here, there is mistake i guess
}
}
CityRepository.java
package com.mfc.repository;
import java.util.List;
import org.springframework.data.jpa.repository.JpaRepository;
import com.mfc.entity.City;
public interface CityRepository extends JpaRepository<City, Integer>{
List getAllCities();
City getCity();
}
In the CityService you call the CityRepository like this
return cityDTO.getCity(id); // getCity is red here, there is mistake i guess
But no such method is defined in the CityRepository. Try using this line return cityDTO.findById(id).get();
You can't see the method findById(Integer id) in the CityRepository, but it is there, because the CityRepository extends JpaRepository<City, Integer>. Find some Spring Data tutorial to know what's really going on in here, long story short the Spring Data is able to generate a lot of standard methods for you.
The method cityDTO.findById(id) returns Optional<City>, not City. To get the instance of City, just add '.get()' method, as it is in the example. It should work for you if city exists in the database. For proper work with Optional find some tutorial. It is a wrapper of an object that may or may not be present, detailed explanation is out of the scope of this answer.
maybe you can try to set up message converter manualy, google MappingJackson2HttpMessageConverter and you'll know what to do.
I'm working on an example project in which I have a model class Movie and I want this movie to have a UUID as the identifier.
I tried the following code.
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import com.fasterxml.jackson.annotation.JsonIdentityInfo;
import com.fasterxml.jackson.annotation.ObjectIdGenerators;
import org.hibernate.annotations.GenericGenerator;
import org.neo4j.ogm.annotation.GraphId;
import org.neo4j.ogm.annotation.NodeEntity;
import org.neo4j.ogm.annotation.Relationship;
import org.springframework.data.annotation.Id;
import javax.persistence.GeneratedValue;
#JsonIdentityInfo(generator=ObjectIdGenerators.PropertyGenerator.class, property="id")
#NodeEntity
public class Movie {
#Id #GeneratedValue(generator = "uuid")
#GenericGenerator(name="uuid", strategy="uuid2")
private String id;
private String title;
private int released;
private String tagline;
#Relationship(type = "ACTED_IN", direction = Relationship.INCOMING)
private List<Role> roles = new ArrayList<>();
public Movie() {
}
public Movie(String title, int released, String tagline) {
this.title = title;
this.released = released;
this.tagline = tagline;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getTitle() {
return title;
}
public int getReleased() {
return released;
}
public String getTagline() {
return tagline;
}
public Collection<Role> getRoles() {
return roles;
}
public void addRole(Role role) {
this.roles.add(role);
}
}
This code is generating an error.
org.neo4j.ogm.exception.MappingException: No identity field found for class: movies.spring.data.neo4j.domain.Movie
here is the reference I used to write this code
I am using neo4j-ogm-core:2.1.3
I didn't understand what was in the reference fully.
but I needed to import Hibernate to use #GenericGenerator
Question:
How can I use UUID inside my model. knowing that I'm using Spring Data?
Note: I am new to Neo4j and Spring. I am having difficulties asking the proper questions.
Alternatively, you can use the GraphAware Neo4j UUID. According the library REDME file:
GraphAware UUID is a simple library that transparently assigns a UUID
to newly created nodes and relationships in the graph and makes sure
nobody can (accidentally or intentionally) change or delete them.
Simply download GraphAware Neo4j Framework and GraphAware Neo4j UUID .jar files to /plugins directory, modify few lines in neo4j.conf file and restart Neo4j. After it, UUIDs will be assigned to each node / relationships created in the Neo4j graph.
This approach does not depends on Spring.
I just start playing with GWT I'm having a really hard time to make GWT + JAVA + JDO + Google AppEngine working with DataStore.
I was trying to follow different tutorial but had no luck. For example I wend to these tutorials: TUT1 TUT2
I was not able to figure out how and what i need to do in order to make this work.
Please look at my simple code and tell me what do i need to do so i can persist it to the datastore:
1. ADDRESS ENTITY
package com.example.rpccalls.client;
import java.io.Serializable;
import javax.jdo.annotations.IdGeneratorStrategy;
import javax.jdo.annotations.Persistent;
import javax.jdo.annotations.PrimaryKey;
public class Address implements Serializable{
#PrimaryKey
#Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
private int addressID;
#Persistent private String address1;
#Persistent private String address2;
#Persistent private String city;
#Persistent private String state;
#Persistent private String zip;
public Address(){}
public Address(String a1, String a2, String city, String state, String zip){
this.address1 = a1;
this.address2 = a2;
this.city = city;
this.state = state;
this.zip = zip;
}
/* Setters and Getters */
}
2. PERSON ENTITY
package com.example.rpccalls.client;
import java.io.Serializable;
import java.util.ArrayList;
import javax.jdo.annotations.IdGeneratorStrategy;
import javax.jdo.annotations.PersistenceCapable;
import javax.jdo.annotations.Persistent;
import javax.jdo.annotations.PrimaryKey;
import com.google.appengine.api.datastore.Key;
#PersistenceCapable
public class Person implements Serializable{
#PrimaryKey
#Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
private Key key;
#Persistent private String name;
#Persistent private int age;
#Persistent private char gender;
#Persistent ArrayList<Address> addresses;
public Person(){}
public Person(String name, int age, char gender){
this.name = name;
this.age = age;
this.gender = gender;
}
/* Getters and Setters */
}
3. RPCCalls
package com.example.rpccalls.client;
import java.util.ArrayList;
import com.google.gwt.core.client.EntryPoint;
import com.google.gwt.core.client.GWT;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.RootPanel;
import com.google.gwt.user.client.ui.TextBox;
public class RPCCalls implements EntryPoint {
private static final String SERVER_ERROR = "An error occurred while attempting to contact the server. Please check your network connection and try again.";
private final RPCCallsServiceAsync rpccallService = GWT.create(RPCCallsService.class);
TextBox nameTxt = new TextBox();
Button btnSave = getBtnSave();
public void onModuleLoad() {
RootPanel.get("inputName").add(nameTxt);
RootPanel.get("btnSave").add(btnSave);
}
private Button getBtnSave(){
Button btnSave = new Button("SAVE");
btnSave.addClickHandler(
new ClickHandler(){
public void onClick(ClickEvent event){
saveData2DB(nameTxt.getText());
}
}
);
return btnSave;
}
void saveData2DB(String name){
AsyncCallback<String> callback = new AsyncCallback<String>() {
public void onFailure(Throwable caught) {
Window.alert("WOOOHOOO, ERROR: " + SERVER_ERROR);
// TODO: Do something with errors.
}
public void onSuccess(String result) {
Window.alert("Server is saying: ' " + result + "'");
}
};
ArrayList<Address> aa = new ArrayList<Address>();
aa.add(new Address("123 sasdf","", "Some City", "AZ", "93923-2321"));
aa.add(new Address("23432 asdf", "Appt 34", "Another City", "AZ", "43434-4432"));
Person p = new Person();
p.setName(name);
p.setAge(23);
p.setGender('m');
p.setAddresses(aa);
// !!!!!!!!!!!!!!!!!! SERVER CALL !!!!!!!!!!!!!!!!!!
rpccallService.saveName(p, callback);
// !!!!!!!!!!!!!!!!!! SERVER CALL !!!!!!!!!!!!!!!!!!
}
}
4. RPCCallsService
package com.example.rpccalls.client;
import com.google.gwt.user.client.rpc.RemoteService;
import com.google.gwt.user.client.rpc.RemoteServiceRelativePath;
#RemoteServiceRelativePath("calls")
public interface RPCCallsService extends RemoteService {
String saveName(Person p);
}
5. RPCCallsServiceAsync
package com.example.rpccalls.client;
import com.google.gwt.user.client.rpc.AsyncCallback;
public interface RPCCallsServiceAsync {
void saveName(Person p, AsyncCallback<String> callback);
}
6. **RPCCalls.gwt.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE module PUBLIC "-//Google Inc.//DTD Google Web Toolkit 1.6.4//EN" "http://google-web-toolkit.googlecode.com/svn/tags/1.6.4/distro-source/core/src/gwt-module.dtd">
<module rename-to='rpccalls'>
<inherits name='com.google.gwt.user.User'/>
<inherits name='com.google.gwt.user.theme.standard.Standard'/>
<entry-point class='com.example.rpccalls.client.RPCCalls'/>
</module>
I tried to add Key class and everything else in those tutorials but it looks like i'm missing something.
Here is my error:
alt text http://vasura.s3.amazonaws.com/Picture2.png
or before i was getting this error:
Key cannot be resolved to a type
What is the best solution to make this working?
Sriram Narayan says to String-encode the Key to get it to pass through GWT's RPC mechanism:
#PersistenceCapable(identityType = IdentityType.APPLICATION)
public class SomeDomainClass implements Serializable {
#PrimaryKey
#Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
#Extension(vendorName = "datanucleus", key = "gae.encoded-pk", value = "true")
String id;
The second tutorial you've referenced has a section on shadowing the com.google.appengine.api.datastore.Key class, since it's not available to GWT:
Since I'm not doing anything with the Key class on the client I'm going to stub it out. This actually requires a few steps and involves the super-src feature of GWT XML module files.
You might want to take a look at the GWT documentation, which states that
The heart of GWT is a compiler that converts Java source into JavaScript
, therefore you need to have the source code available to use a given class in the client code.
Once you're fed up with JDO, take a look at objectify. I've found it to be a lot nicer to work with, and it has full GWT interop without DTOs.
You can use the Key class in GWT code by adding these additional jar files:
http://www.resmarksystems.com/code/
appengine-utils-client-1.0.jar
appengine-utils-server-1.0.jar
This basically gives the GWT compiler a GWT-friendly version of the Key and other AppEngine classes. (like Text, Blob and User..)
To use:
Add the appengine-utils-client-1.0.jar anywhere in your build path.
Put the appengine-utils-server-1.0.jar in your WEB-INF/lib folder.
Add the following to your GWT module:
< inherits name="com.resmarksystems.AppEngineDataTypes"/>
Another option would be to implement a DTO ( Data Transfer Object ) that you are using in the client instead of using the persistent objects directly.
Or, you could go to JPA instead of JDO. In the example data class in the appengine JPA docs the Id is a Long instead of that Key implementation http://code.google.com/appengine/docs/java/datastore/usingjpa.html
Could it be that you forgot to create the implementation for the RPCCallsService? I can't see it from the list of files that you have.
You should have a file called RPCCallsServiceImpl.java in RPCCalls/src/com/example/rpccalls/server/, it is the implementation file for the interface RPCCallsService.java.
It will look something like this:
import javax.jdo.JDOHelper;
import javax.jdo.PersistenceManager;
import javax.jdo.PersistenceManagerFactory;
import com.google.gwt.user.server.rpc.RemoteServiceServlet;
import com.example.rpccalls.client.RPCCallsService;
public class RPCCallsServiceImpl extends RemoteServiceServlet implements RPCCallsService {
// Factory to get persistence manager object later
private static final PersistenceManagerFactory PMF = JDOHelper.getPersistenceManagerFactory("transactional-optional");
public String saveName(Person p) {
// Data Store need persistence manager object for writing to it
PersistenceManager pm = PMF.getPersistenceManager();
// Recommended way to save an object to the data store
try {
pm.makePersistent(p);
} finally {
pm.close();
}
// You want it to return string
return p.getName();
}
}
Hopefully this help you to solve the problem. Cheers :)