i'm developing my firts RestFul webServices in javaEE6.
This is my Entity Bean
#XmlRootElement
#Entity
public class MyEntity implements Serializable {
#Id
#GeneratedValue
private long idEntity;
private String name;
private String description;
#OneToMany(mappedBy = "entity" , fetch = FetchType.EAGER)
private List<EntityB> list;
//Get and set
}
#Entity
public class EntityB {
#Id
#GeneratedValue
private long idCategoria;
#ManyToOne
private MyEntity myEntity;
}
this is my webServices
#Path("myentity")
#Produces( {MediaType.APPLICATION_XML , MediaType.APPLICATION_JSON })
#Consumes( {MediaType.APPLICATION_XML , MediaType.APPLICATION_JSON })
#Stateless
public class MyEntityService {
#Inject
MyEntityDao entityDao;
#GET
#Path("{id}/")
public MyEntity findById(#PathParam("id") Long id){
return entityDao.findById(id);
}
}
Finally i configured Jersey
#ApplicationPath("ws")
public class ApplicationConfig extends Application {
}
Now , if i try a invoke my web services (localhost:8080/xxxx/ws/myentity) i get this error:
HTTP Status 500 - javax.xml.bind.MarshalException - with linked exception: [com.sun.istack.SAXException2: A cycle is detected in the object graph. This will cause infinitely deep XML
You have a cyclic Graph of objects, which is not allowed , as it would result in an "infinite" XML.
MyEntity Holds a reference to EntityB , which holds a reference that goes back to MyEntity.
The marshaller will try to marshall MyEntity > EntityB > MyEntity > EntityB and so on.
You can mark MyEntity in EntityB as #XmlTransient, to avoid this.
However, It might not be a good idea to try to use the same Classes of objects across all your project (From persistence layers to communication layers).
Related
I'm having an issue with serializing #RelationshipEntities to JSON via Spring Data Rest. Whenever I create the #RelationshipEntity, I run into an infinite recursion on serializing the graph to JSON.
Using JSOG to try to render the graph only results in a different, malformed JSON response.
While I can avoid the issue by using #JsonManagedReference, it doesn't solve the problem as I would like to expose the relationship from both nodes.
I've created a simple application that exhibits the issue. It can be found here: https://github.com/cyclomaniac/neo4j-spring-data-rest-cyclic
It implements very basic Team and Player NodeEntities with a single RelationshipEntity, PlayerPosition.
Player:
#NodeEntity
#JsonIdentityInfo(generator= JSOGGenerator.class)
public class Player {
#GraphId
#JsonProperty("id")
private Long id;
private String name;
private String number;
#Relationship(type = "PLAYS_ON")
private PlayerPosition position;
...
Team:
#NodeEntity
#JsonIdentityInfo(generator= JSOGGenerator.class)
public class Team {
#GraphId
#JsonProperty("id")
private Long id;
private String name;
#Relationship(type = "PLAYS_ON", direction = Relationship.INCOMING)
Set<PlayerPosition> teamPlayers;
...
PlayerPosition:
#RelationshipEntity(type="PLAYS_ON")
#JsonIdentityInfo(generator= JSOGGenerator.class)
public class PlayerPosition {
#GraphId
#JsonProperty("id")
private Long id;
private String position;
#StartNode
private Player player;
#EndNode
private Team team;
...
When wired up to a GraphRepository, hitting the /teams endpoint results in the following output with JSOG in place:
{
"_embedded" : {
"teams" : [ {
"#id" : "1",
"name" : "Cubs",
"teamPlayers" : [ {
"#id" : "2",
"position" : "Catcher",
"player" : {
"#id" : "3"
Notice that the JSON ends prematurely. The server throws an exception:
2016-11-04 15:48:03.495 WARN 12287 --- [nio-8080-exec-1] .w.s.m.s.DefaultHandlerExceptionResolver : Failed to write HTTP message:
org.springframework.http.converter.HttpMessageNotWritableException:
Could not write content: Can not start an object,
expecting field name; nested exception is
com.fasterxml.jackson.core.JsonGenerationException:
Can not start an object, expecting field name
My assumption is that I've chosen a poor way to implement the relationship, though it feels fairly straightforward. I'd appreciate any tips on how I can properly expose this relationship, ideally via Spring Data Rest, from both the Team and Player nodes.
Try to annotate with #JsonIgnore or pair: #JsonBackReference and #JsonManagedReference
In my JavaEE7-project, I am using spring-data-neo4j standalone in "Advanced Mapping" mode (using spring-aspects). Everything works fine so far: CRUD on entities within a transaction, where the transaction is started manually or via #Transactional-annotation.
In my usecase, my view accesses an entity "directly":
// User
#NodeEntity
public class User {
private String firstName;
// getter, setter, ...
}
// SessionBean
#SessionScoped
#Named
public class SessionBean {
#Transactional
public User getUser() {
User user = ...;
System.out.println(user.getFirstName()); // (1) gives firstName-value.
return user;
}
}
// sometpl.xhtml
${sessionBean.user.firstName} // (2) gives "null".
Somehow, this behavior (difference between (1) and (2)) is wanted, as spring-data-neo4j supposes read-access only within a transaction.
But I want to have my usecase(2) working (returning the user's firstName, not "null"). Is there any way to achieve this? So let's say, starting transaction automatically in read-access-case? Implicit read-transactions-support?
My workaround:
Use a RequestScoped bean to start a transaction in "preRenderView" and to close this tx when the bean is destroyed.
This does not work on ajax-calls!
#Named
#RequestScoped
public class SpringDataNeo4jHelperBean {
#Inject
#Named
private Neo4jTemplate neoTemplate;
private Transaction tx;
#PreDestroy
public void finishTransaction() {
if (this.tx != null) {
this.tx.success();
this.tx.finish();
}
}
public void startReadOnlyTransaction() {
if (!this.neoTemplate.getGraphDatabase().transactionIsRunning()) {
this.tx = this.neoTemplate.getGraphDatabaseService().beginTx();
}
}
}
In some template, for example s.th. like a central layout.xhtml:
<f:metadata>
<f:event type="preRenderView" listener="#{springDataNeo4jHelperBean.startReadOnlyTransaction()}" />
</f:metadata>
I'm relatively new to JavaEE and web services, however, I'm using netbeans to generate my client and webservice resources. I have a resource "CustomerData" that represents a mysql database table and a value "rewardsPoints" representing a column in that table, however, I am unable to update the value due to a ConstraintViolationException, specifically:
javax.validation.ConstraintViolationException: Bean Validation constraint(s) violated while executing Automatic Bean Validation on callback event:'preUpdate'. Please refer to embedded ConstraintViolations for details.
I'm not familiar with the 'preUpdate' callback event, is it something I need to override? I can't seem to figure out exactly why this exception is being thrown, but, as I said, i'm very new to web service programming. Here are my classes:
#Stateless
#Path("customers")
public class CustomerDataFacadeREST extends AbstractFacade<CustomerData> {
#PersistenceContext(unitName = "CustomerPortalPU")
private EntityManager em;
public CustomerDataFacadeREST() {
super(CustomerData.class);
}
#PUT
#Path("{id}")
#Consumes({"application/xml", "application/json"})
public void edit(#PathParam("id") Integer id, CustomerData entity) {
super.edit(entity);
}
#GET
#Path("{id}")
#Produces({"application/xml", "application/json"})
public CustomerData find(#PathParam("id") Integer id) {
return super.find(id);
}
#GET
#Path("addPoints/{id}/{amount}")
#Produces({"text/plain"})
public String addPoints(#PathParam("id") Integer id, #PathParam("amount") int amount) {
CustomerData customer = find(id);
customer.getRewardsPoints(customer.getRewardsPoints() + amount);
em.persist(customer);
edit(customer);
return customer.getRewardsPoints();
}
#Override
protected EntityManager getEntityManager() {
return em;
}
}
And the CustomerData entity class:
#Entity
#Table(name = "tbl_customer_data")
#XmlRootElement
public class CustomerData implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Basic(optional = false)
#Column(name = "id")
private Integer id;
#Basic(optional = false)
#NotNull
#Column(name = "rewards_points")
private int rewardsPoints;
public CustomerData(Integer id, int rewardsPoints) {
this.id = id;
this.rewardsPoints = rewardsPoints;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public int getRewardsPoints() {
return rewardsPoints;
}
public void setRewardsPoints(int rewardsPoints) {
this.rewardsPoints = rewardsPoints;
}
}
When I try to access the URI:
http://localhost:8080/CustomerPortal/ws/customers/addPoints/1/5
to add 5 points to user with id 1 i get an HTTP 500 error and in the glassfish logs it says
[2013-11-05T03:28:11.733-0500] [glassfish 4.0] [WARNING] [ejb.system_exception] [javax.enterprise.system.container.ejb.com.sun.ejb.containers] [tid: _ThreadID=21 _ThreadName=http-listener-1(3)] [timeMillis: 1383640091733] [levelValue: 900] [[
EJB5184:A system exception occurred during an invocation on EJB CustomerDataFacadeREST, method: public java.lang.String com.webservice.entities.CustomerDataFacadeREST.addPoints(java.lang.Integer,int)]]
[2013-11-05T03:28:11.741-0500] [glassfish 4.0] [WARNING] [] [javax.enterprise.web] [tid: _ThreadID=21 _ThreadName=http-listener-1(3)] [timeMillis: 1383640091741] [levelValue: 900] [[
StandardWrapperValve[com.webservice.entities.ApplicationConfig]: Servlet.service() for servlet com.webservice.entities.ApplicationConfig threw exception
javax.validation.ConstraintViolationException: Bean Validation constraint(s) violated while executing Automatic Bean Validation on callback event:'preUpdate'. Please refer to embedded ConstraintViolations for details.
Any resources, insight or information regarding this issue would be extremely helpful.
The exception has little to do with web services: it has to do with Bean Validation instead.
In this case, since the Validation fails inside method addPoints (look at the stack trace) the only line that can cause it is when persisting or editing an Entity of type CustomerData. The only constraint you have in that class is that rewardsPoints should not be null. So, that's the cause of the exception.
However there some things that won't work in addPoints method:
You should check that find() method doesn't return a null object.
customer.getRewardsPoints(customer.getRewardsPoints() + amount) never sets the property (does it compile?)
EntityManager.persist() throws exception if the entity already exists. You probably want to remove that line if you only want to edit (update) the entity.
Note: I am not sure that the code you have posted is really compiling and causing that exception. That's probably caused by another version.
What is the best way to implement "/schema" function on custom controllers (explained in rest-bucks ) similar to "[repository]/schema" function with Spring Data Rest?
If entity classes are persisted through Spring Repositories, the solution is to inject PersistentEntityToJsonSchemaConverter into the controller and invoke convert() method.
Below is a code sample using Mongo as Repository
Entity class
#Document
public class Project implements Identifiable<String> {
#Id
private String id;
#NotNull
private String name;
//getters setters
}
Repository
public interface ProjectRepository extends
PagingAndSortingRepository<Project, String> {
}
Controller
#Controller
#ExposesResourceFor(Project.class)
#RequestMapping("/projects")
public class ProjectController {
#Autowired
private PersistentEntityToJsonSchemaConverter jsonSchemaConverter;
#RequestMapping(value = "/schema", method = RequestMethod.GET, produces = { "application/schema+json" })
#ResponseBody
public JsonSchema schema() {
return jsonSchemaConverter.convert(Project.class);
}
// implement rest of SDR mechanics
}
I am using JPA 2.0 in Netbeans. I am using entities. If my database has no table then it should create table from entities. Here is my code
public class BankServlet extends HttpServlet {
#EJB
private BankServiceBeanRemote bankServiceBean;
protected void processRequest(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
int custId = Integer.parseInt(request.getParameter("id"));
bankServiceBean.createCustomers();
Customer cust = bankServiceBean.findCustomer(custId);
response.setContentType("text/html;charset=UTF-8");
....
} //end of processRequest
} //end of class BankServlet
Here is my bean
#Stateless
public class BankServiceBean implements BankServiceBeanRemote {
#PersistenceContext(unitName = "Bank_JPA-ejbPU")
private EntityManager em;
#Override
public void createCustomers() {
Referee r1 = new Referee();
r1.setId(1);
r1.setName("SIR JOHN DEED");
r1.setComments("JUDGE");
em.persist(r1);
Customer c1 = new Customer();
c1.setId(1);
c1.setFirstName("SIMON");
c1.setLastName("KING");
c1.setReferee(r1);
......
}
} //end of class BankServiceBean
Here is my referee entity
#Entity
public class Referee implements Serializable {
private static final long serialVersionUID = 1L;
private int id;
private String name;
private String comments;
public Referee() {
}
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
// other getter setters
} //end of class Referee
When i run the code i get following exception
Exception [EclipseLink-4002] (Eclipse Persistence Services - 2.0.1.v20100213-r6600): org.eclipse.persistence.exceptions.DatabaseException
Internal Exception: com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: Table 'dbbank.referee' doesn't exist
Error Code: 1146
Call: INSERT INTO REFEREE (ID, NAME, COMMENTS) VALUES (?, ?, ?)
bind => [3, MICHAEL ELLIS, MAJOR SHAREHOLDER OF THIS BANK]
Query: InsertObjectQuery(pk.mazars.basitMahmood.entity.Referee[id=3])
at org.eclipse.persistence.exceptions.DatabaseException.sqlException(DatabaseException.java:324)
If table doesnot exist then it should create table automatically. Am i doing something wrong?
Thank you
Your assumptions are wrong. Tables are not magically created if they don't exist. Most of the applications use existing databases.
EclipseLink can create the schema for you, but it's not the default. Read this page to know how to enable this feature:
EclipseLink can be used to automatically generate the tables and
database schema for a persistence unit. This is done through the
"eclipselink.ddl-generation" persistence unit property, set to either
"create-tables" or "drop-and-create-tables". The tables and
constraints will be generated for all of the classes defined in that
persistence unit.
You should change your table generation strategy in persistence.xml.
just add these rows to the persistence.xml file :
<properties>
<property name="eclipselink.ddl-generation" value="create-tables"/>
</properties>
Here is a snippet to add to your persistence.xml to make openjpa create tables for you. This comes in handy when doing in container unit tests using openejb with an in memory database like HyperSQL.
<properties>
<property name="openjpa.jdbc.SynchronizeMappings" value="buildSchema(ForeignKeys=true)"/>
</properties>