I have created a restful webservice using the "generate web services from database option" in netbeans.
I have deployed this in wildfly and noticed that while GET requests work, POST requests do not.
Here is the part of my java code which accepts the POST connections:
#POST
#Override
#Consumes({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
public void create(XtgpsActivity entity) {
super.create(entity);
}
The create method takes a parameter of type XtgpsActivity. This is the XtgpsActivity class:
//packages and imports list removed
#Entity
#Table(name = "xtgps_Activity")
#XmlRootElement
#NamedQueries({
#NamedQuery(name = "XtgpsActivity.findAll", query = "SELECT x FROM XtgpsActivity x"),
#NamedQuery(name = "XtgpsActivity.findById", query = "SELECT x FROM XtgpsActivity x WHERE x.id = :id")})
public class XtgpsActivity implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Basic(optional = false)
#Column(name = "Id")
private Integer id;
#JoinColumn(name = "BaseItemId", referencedColumnName = "Id")
#ManyToOne
private XtgpsBaseItem baseItemId;
#OneToMany(mappedBy = "activityId")
private Collection<XtgpsNearByActivity> xtgpsNearByActivityCollection;
public XtgpsActivity() {
}
public XtgpsActivity(Integer id) {
this.id = id;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public XtgpsBaseItem getBaseItemId() {
return baseItemId;
}
public void setBaseItemId(XtgpsBaseItem baseItemId) {
this.baseItemId = baseItemId;
}
#XmlTransient
public Collection<XtgpsNearByActivity> getXtgpsNearByActivityCollection() {
return xtgpsNearByActivityCollection;
}
public void setXtgpsNearByActivityCollection(Collection<XtgpsNearByActivity> xtgpsNearByActivityCollection) {
this.xtgpsNearByActivityCollection = xtgpsNearByActivityCollection;
}
#Override
public int hashCode() {
int hash = 0;
hash += (id != null ? id.hashCode() : 0);
return hash;
}
#Override
public boolean equals(Object object) {
// TODO: Warning - this method won't work in the case the id fields are not set
if (!(object instanceof XtgpsActivity)) {
return false;
}
XtgpsActivity other = (XtgpsActivity) object;
if ((this.id == null && other.id != null) || (this.id != null && !this.id.equals(other.id))) {
return false;
}
return true;
}
#Override
public String toString() {
return "au.com.xitech.XtgpsActivity[ id=" + id + " ]";
}
}
This is the create method in the superclass:
public void create(T entity) {
getEntityManager().persist(entity);
}
Since the related table only has two columns id and baseitemid, and id is a primary key, I am sending my POST JSON data as this:
{"baseitemid":"2"}
However, when i post data in this format, I get an error:
http://localhost:8089/xxx/api/activity/ 400 (Bad Request)
I believe this is because of an issue with the JSON data I send. What is the correct way to create a JSON array for the post request?
PS: I have also attached my ajax call below. Note I did this after enabling CORS in the browser.
$('#submit_it').click(function(e) {
//console.log("submit button has been clicked");
e.preventDefault(); //cancel form submit
var jsObj = $post_example.serializeObject()
, ajaxObj = {};
//console.log(jsObj);
alert(JSON.stringify(jsObj));
ajaxObj = {
type: "POST",
url: "http://localhost:8089/xxx/api/activity/",
data: JSON.stringify(jsObj),
contentType:"application/json",
success: function(data) {
//console.log(data);
if(data[0].HTTP_CODE == 200) {
$('#div_ajaxResponse').text( data[0].MSG );
}
},
error: function(jqXHR, textStatus, errorThrown) {
console.log("Error " + jqXHR.getAllResponseHeaders() + " " + errorThrown);
},
complete: function(XMLHttpRequest) {
//console.log( XMLHttpRequest.getAllResponseHeaders() );
},
dataType: "json" //request JSON
};
$.ajax(ajaxObj);
});
The value passed in json is parsed as a numeric value, so sending as (without quotes for the value)
{
"baseitemid":2
}
worked.
Related
I am trying to use the JPA Criteria API to filter the results and aggregate them using simple count, min, avg and max. I am using Spring Boot 2.7.8, so I am trying to use Interface-projections such that these aggregated results look the same as the simpler queries done automatically by the Spring repositories.
My domain entity (simplified for brevity) looks like this:
#Entity
#Table(name = "vehicle_stopped")
#IdClass(VehicleStopped.VehicleStoppedPK.class)
public class VehicleStopped implements Serializable {
#Id
#Column(name = "stopped_session_uuid", nullable = false)
private String stoppedSessionUuid;
#Id
#Column(name = "start_ts", nullable = false)
private OffsetDateTime startTs;
#Column(name = "end_ts", nullable = false)
private OffsetDateTime endTs;
#Column(name = "duration_seconds")
private Double durationSeconds;
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "zone_id")
private CameraZone cameraZone;
#Override
public VehicleStoppedPK getId() {
VehicleStopped.VehicleStoppedPK pk = new VehicleStopped.VehicleStoppedPK();
pk.setStartTs(this.getStartTs());
pk.setStoppedSessionUuid(this.getStoppedSessionUuid());
return pk;
}
public OffsetDateTime getEndTs() {
return endTs;
}
public void setEndTs(OffsetDateTime endTs) {
this.endTs = endTs;
}
public Double getDurationSeconds() {
return durationSeconds;
}
public void setDurationSeconds(Double durationSeconds) {
this.durationSeconds = durationSeconds;
}
public CameraZone getCameraZone() {
return cameraZone;
}
public void setCameraZone(CameraZone cameraZone) {
this.cameraZone = cameraZone;
}
public VehicleType getVehicleType() {
return vehicleType;
}
public void setVehicleType(VehicleType vehicleType) {
this.vehicleType = vehicleType;
}
public String getStoppedSessionUuid() {
return stoppedSessionUuid;
}
public void setStoppedSessionUuid(String stoppedSessionUuid) {
this.stoppedSessionUuid = stoppedSessionUuid;
}
//some details removed for brevity
#Override
public static class VehicleStoppedPK implements Serializable {
private OffsetDateTime startTs;
private String stoppedSessionUuid;
public VehicleStoppedPK() {
}
public OffsetDateTime getStartTs() {
return startTs;
}
public void setStartTs(OffsetDateTime startTs) {
this.startTs = startTs;
}
public String getStoppedSessionUuid() {
return stoppedSessionUuid;
}
public void setStoppedSessionUuid(String stoppedSessionUuid) {
this.stoppedSessionUuid = stoppedSessionUuid;
}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
VehicleStoppedPK that = (VehicleStoppedPK) o;
return Objects.equals(startTs, that.startTs) && Objects.equals(stoppedSessionUuid, that.stoppedSessionUuid);
}
#Override
public int hashCode() {
return Objects.hash(startTs, stoppedSessionUuid);
}
#Override
public String toString() {
return "VehicleStoppedPK{" +
"startTs=" + startTs +
", stoppedSessionUuid='" + stoppedSessionUuid + '\'' +
'}';
}
}
}
#Entity
#Table(name = "camera_zone")
public class CameraZone implements Serializable {
#Id
#SequenceGenerator(name = "camera_zone_id_seq", sequenceName = "camera_zone_id_seq", allocationSize = 1)
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "camera_zone_id_seq")
#Column(name = "id", updatable=false)
private Integer id;
#Column(name = "uuid", unique = true)
private String uuid;
#Column(name = "type")
private String type;
#Column(name = "name")
private String name;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getUuid() {
return uuid;
}
public void setUuid(String uuid) {
this.uuid = uuid;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
CameraZone that = (CameraZone) o;
return Objects.equals(id, that.id) && Objects.equals(uuid, that.uuid) && Objects.equals(camera, that.camera) && Objects.equals(type, that.type) && Objects.equals(name, that.name);
}
#Override
public int hashCode() {
return Objects.hash(id, uuid, camera, type, name);
}
}
The code that I have in my Repository implementation looks like this:
public class SpecificationVehicleStoppedRepositoryImpl
implements SpecificationVehicleStoppedRepository {
#Autowired private EntityManager em;
#Autowired ProjectionFactory projectionFactory;
#Override
public List<VehicleStoppedAggregate> getStoppedVehiclesCount(Specification<VehicleStopped> spec) {
CriteriaBuilder builder = em.getCriteriaBuilder();
CriteriaQuery<Tuple> query = builder.createTupleQuery();
Root<VehicleStopped> root = query.from(VehicleStopped.class);
Predicate predicate = spec.toPredicate(root, query, builder);
if (predicate != null) {
query.where(predicate);
}
Path<Number> duration = root.get("durationSeconds");
Path<CameraZone> zone = root.get("cameraZone");
query
.multiselect(zone,
builder.count(root).alias("totalVehicles"),
builder.min(duration).alias("minDuration"),
builder.avg(duration).alias("avgDuration"),
builder.max(duration).alias("maxDuration"))
.groupBy(zone);
List<Tuple> rawResultList = em.createQuery(query).getResultList();
return project(rawResultList, VehicleStoppedAggregate.class);
}
private <P> List<P> project(List<Tuple> results, Class<P> projectionClass) {
return results.stream()
.map(tuple -> {
Map<String, Object> mappedResult = new HashMap<>(tuple.getElements().size());
for (TupleElement<?> element : tuple.getElements()) {
String name = element.getAlias();
mappedResult.put(name, tuple.get(name));
}
return projectionFactory.createProjection(projectionClass, mappedResult);
})
.collect(Collectors.toList());
}
}
The interface-based projection I am trying to populate (using SpelAwareProxyProjectionFactory) is this:
public interface VehicleStoppedAggregate {
CameraZone getCameraZone();
Integer getTotalVehicles();
Double getMinDuration();
Double getAvgDuration();
Double getMaxDuration();
}
The call to getStoppedVehiclesCount() fails with the following error:
ERROR: column "camerazone1_.id" must appear in the GROUP BY clause or be used in an aggregate function
This error is coming from the PostgreSQL database, and rightly so because the SQL hibernate generates is incorrect:
select
vehiclesto0_.zone_id as col_0_0_,
count(*) as col_1_0_,
min(vehiclesto0_.duration_seconds) as col_2_0_,
avg(vehiclesto0_.duration_seconds) as col_3_0_,
max(vehiclesto0_.duration_seconds) as col_4_0_,
camerazone1_.id as id1_2_,
camerazone1_.name as name2_2_,
camerazone1_.type as type3_2_,
camerazone1_.uuid as uuid4_2_
from
vehicle_stopped vehiclesto0_
inner join
camera_zone camerazone1_
on vehiclesto0_.zone_id=camerazone1_.id cross
where
vehiclesto0_.start_ts>=?
and vehiclesto0_.start_ts<=?
and 1=1
and 1=1
and 1=1
group by
vehiclesto0_.zone_id
It is not grouping by the other fields it is requesting from the joined table.
If I had to use a normal class, instead of a Tuple, it would work, but it would mean I would have to create a class with a huge constructor for all fields for Hibernate to populate it.
Somehow, when I use Interface-based projections with Spring's repositories rather than my criteriaquery, the same scenario works. They manage to populate the one-to-many relationships just fine.
Is there a way to fix this and make Hibernate ask for the right fields?
I am using Hibernate 5.6.14.Final (as bundled with Spring Boot 2.7.8).
I believe the "solution" is two create two "independent" query roots and join them together:
CriteriaBuilder builder = session.getCriteriaBuilder();
CriteriaQuery<Tuple> query = builder.createTupleQuery();
Root<VehicleStopped> root = query.from(VehicleStopped.class);
// instead of Path<CameraZone> zone = root.get("cameraZone")
Root<CameraZone> zone = query.from(CameraZone.class);
query.where(builder.equal(zone, root.get("cameraZone")));
Path<Number> duration = root.get("durationSeconds");
query
.multiselect(zone,
builder.count(root).alias("totalVehicles"),
builder.min(duration).alias("minDuration"),
builder.avg(duration).alias("avgDuration"),
builder.max(duration).alias("maxDuration"))
.groupBy(zone);
session.createQuery(query).getResultList();
In that case Hibernate 5 produces following SQL (which actually looks weird from my perspective due to missing columns in group by clause):
select
naturalidc1_.id as col_0_0_,
count(*) as col_1_0_,
min(naturalidc0_.duration_seconds) as col_2_0_,
avg(naturalidc0_.duration_seconds) as col_3_0_,
max(naturalidc0_.duration_seconds) as col_4_0_,
naturalidc1_.id as id1_0_,
naturalidc1_.name as name2_0_,
naturalidc1_.type as type3_0_,
naturalidc1_.uuid as uuid4_0_
from
vehicle_stopped naturalidc0_ cross
join
camera_zone naturalidc1_
where
naturalidc1_.id=naturalidc0_.zone_id
group by
naturalidc1_.id
FYI. Your initial query does work in Hibernate 6 and produced SQL does look more correct but still weird:
select
c1_0.id,
c1_0.name,
c1_0.type,
c1_0.uuid,
count(*),
min(v1_0.duration_seconds),
avg(v1_0.duration_seconds),
max(v1_0.duration_seconds)
from
vehicle_stopped v1_0
join
camera_zone c1_0
on c1_0.id=v1_0.zone_id
group by
1,
2,
3,
4
I have multi module project (*.war).
It's an entry point in app.
#SpringBootConfiguration
#SpringBootApplication
#EnableJpaRepositories(basePackages = {"....dao.repository"})
#EntityScan(basePackages = {"....dao.model"})
#ComponentScan(basePackages = {"..."})
public class ApsTtsApplication
extends SpringBootServletInitializer
implements WebApplicationInitializer {
private static final Logger LOGGER = LoggerFactory.getLogger( ApsTtsApplication.class );
public static void main(String[] args) {
LOGGER.info("Start an application...");
SpringApplication.run(ApsTtsApplication.class, args);
}
#Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
LOGGER.info("There is building the web application!");
return builder.sources(ApsTtsApplication.class);
}
}
model
IdMainForEntities
#MappedSuperclass
public abstract class IdMainForEntities {
#Id
#GenericGenerator(name="system-uuid", strategy = "uuid")
#GeneratedValue(generator="system-uuid")
#Column(name = "id", nullable = false)
private String ID;
public IdMainForEntities() {
}
public String getID() {
return ID;
}
public void setID(String ID) {
this.ID = ID;
}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
IdMainForEntities that = (IdMainForEntities) o;
return Objects.equals(ID, that.ID);
}
#Override
public int hashCode() {
return Objects.hash(ID);
}
#Override
public String toString() {
return "IdMainEntity{" +
"ID='" + ID + '\'' +
'}';
}
}
MessageLog
#Entity
#Table(name = "MESSAGESLOG")
public class MessageLog extends IdMainForEntities {
...
#Column(name = "MSG_OWNER_ID")
#Size(message = "MSG_OWNER_ID{MessagesLog.size}", max = 32)
private String msgOwnerId;
public MessageLog() {
}
public String getMsgOwnerId() {
return msgOwnerId;
}
public void setMsgOwnerId(String msgOwnerId) {
this.msgOwnerId = msgOwnerId;
}
....
}
MessagesLogReadRepository
public interface MessagesLogReadRepository extends CrudRepository <MessageLog, String> {
Optional <MessageLog> findByMsgOwnerId(String msgOwnerId);
MessageLog findMessageLogByMsgOwnerId(String msgOwnerId);
Optional <MessageLog> findByDocument(String documentId);
}
Only standard methods can be called from this repository : findById (), etc.
But named queries is not found.
18-03-2020 08:49:39.531 DEBUG 8660 o.s.d.j.r.query.JpaQueryFactory
: Looking up query for method findByMsgOwnerId 18-03-2020
08:49:39.547 DEBUG 8660 o.s.d.jpa.repository.query.NamedQuery :
Looking up named query MessageLog.findByMsgOwnerId 18-03-2020
08:49:39.547 DEBUG 8660 o.h.e.t.internal.TransactionImpl :
On TransactionImpl creation,
JpaCompliance#isJpaTransactionComplianceEnabled == false 18-03-2020
08:49:39.547 DEBUG 8660 o.s.d.jpa.repository.query.NamedQuery :
Did not find named query MessageLog.findByMsgOwnerId
#Override
public MessageLogDto getByMsgOwnerId(String msgOwnerId) {
if(msgOwnerId == null) throw new MessagesLogException(paramNotNull);
/*It's null*/
Optional<MessageLog> byMsgOwnerId = this.messagesLogReadRepository.findByMsgOwnerId("8a00844170d829040170d82c670b00");
MessageLog messageLog = byMsgOwnerId.orElse(new MessageLog());
/*It's OK*/
Optional<MessageLog> byId = this.messagesLogReadRepository.findById("8a00844170d829040170d82c670b0003");
return transformEntityToDto(messageLog);
}
Update
public interface MessagesLogReadRepository extends JpaRepository<MessageLog, String> {
#Query(value = "SELECT * from messageslog where MSG_OWNER_ID = ?1", nativeQuery = true )
Optional <MessageLog> findRowByMsgOwnerId(String msgOwnerId);
...
...extends JpaRepository
But, It doesn't solve the problem.
I don't have any mistakes. I only get null, but the requested row is in the table, that 's for sure.
does anyone have any ideas about this?
Why ?
Solution
The field we were working with had a data type CHAR(32 byte). We changed this type to VARCHAR(32 byte) into a table database of Oracle.
Now, Spring can build named query fine.
The project on older versions of Spring worked like this ...(CHAR(32 byte))
I don't understand why so.
I have created a web application and added entity classes from database and then generated restful services from the entity classes. And then i have added a restful java script client and started using it. For GET/PUT method it is working as expected but for create/POST methods throwing error as 'Unable to fulfil the request'
UsersLogin.java
#Entity
#Table(name = "USERS_LOGIN", catalog = "D3Common", schema = "dbo")
#XmlRootElement
#NamedQueries({
#NamedQuery(name = "UsersLogin.findAll", query = "SELECT u FROM UsersLogin u")
, #NamedQuery(name = "UsersLogin.findByUserId", query = "SELECT u FROM UsersLogin u WHERE u.userId = :userId")
, #NamedQuery(name = "UsersLogin.findByUserPassword", query = "SELECT u FROM UsersLogin u WHERE u.userPassword = :userPassword")
, #NamedQuery(name = "UsersLogin.findByUserName", query = "SELECT u FROM UsersLogin u WHERE u.userName = :userName")
, #NamedQuery(name = "UsersLogin.findByFeatureGroup", query = "SELECT u FROM UsersLogin u WHERE u.featureGroup = :featureGroup")})
public class UsersLogin implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#Basic(optional = false)
#NotNull
#Size(min = 1, max = 10)
#Column(name = "USER_ID", nullable = false, length = 10)
private String userId;
#Basic(optional = false)
#NotNull
#Size(min = 1, max = 10)
#Column(name = "USER_PASSWORD", nullable = false, length = 10)
private String userPassword;
#Basic(optional = false)
#NotNull
#Size(min = 1, max = 10)
#Column(name = "USER_NAME", nullable = false, length = 10)
private String userName;
#Size(max = 10)
#Column(name = "FEATURE_GROUP", length = 10)
private String featureGroup;
public UsersLogin() {
}
public UsersLogin(String userId) {
this.userId = userId;
}
public UsersLogin(String userId, String userPassword, String userName) {
this.userId = userId;
this.userPassword = userPassword;
this.userName = userName;
}
public String getUserId() {
return userId;
}
public void setUserId(String userId) {
this.userId = userId;
}
public String getUserPassword() {
return userPassword;
}
public void setUserPassword(String userPassword) {
this.userPassword = userPassword;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getFeatureGroup() {
return featureGroup;
}
public void setFeatureGroup(String featureGroup) {
this.featureGroup = featureGroup;
}
#Override
public int hashCode() {
int hash = 0;
hash += (userId != null ? userId.hashCode() : 0);
return hash;
}
#Override
public boolean equals(Object object) {
// TODO: Warning - this method won't work in the case the id fields are not set
if (!(object instanceof UsersLogin)) {
return false;
}
UsersLogin other = (UsersLogin) object;
if ((this.userId == null && other.userId != null) || (this.userId != null && !this.userId.equals(other.userId))) {
return false;
}
return true;
}
#Override
public String toString() {
return "d.UsersLogin[ userId=" + userId + " ]";
}
}
AbstarctFaced.java
public abstract class AbstractFacade<T> {
private Class<T> entityClass;
public AbstractFacade(Class<T> entityClass) {
this.entityClass = entityClass;
}
protected abstract EntityManager getEntityManager();
public void create(T entity) {
getEntityManager().persist(entity);
}
public void edit(T entity) {
getEntityManager().merge(entity);
}
public void remove(T entity) {
getEntityManager().remove(getEntityManager().merge(entity));
}
public T find(Object id) {
return getEntityManager().find(entityClass, id);
}
public List<T> findAll() {
javax.persistence.criteria.CriteriaQuery cq = getEntityManager().getCriteriaBuilder().createQuery();
cq.select(cq.from(entityClass));
return getEntityManager().createQuery(cq).getResultList();
}
public List<T> findRange(int[] range) {
javax.persistence.criteria.CriteriaQuery cq = getEntityManager().getCriteriaBuilder().createQuery();
cq.select(cq.from(entityClass));
javax.persistence.Query q = getEntityManager().createQuery(cq);
q.setMaxResults(range[1] - range[0] + 1);
q.setFirstResult(range[0]);
return q.getResultList();
}
public int count() {
javax.persistence.criteria.CriteriaQuery cq = getEntityManager().getCriteriaBuilder().createQuery();
javax.persistence.criteria.Root<T> rt = cq.from(entityClass);
cq.select(getEntityManager().getCriteriaBuilder().count(rt));
javax.persistence.Query q = getEntityManager().createQuery(cq);
return ((Long) q.getSingleResult()).intValue();
}
}
ApplicationConfig.java
#javax.ws.rs.ApplicationPath("webresources")
public class ApplicationConfig extends Application {
#Override
public Set<Class<?>> getClasses() {
Set<Class<?>> resources = new java.util.HashSet<>();
addRestResourceClasses(resources);
return resources;
}
/**
* Do not modify addRestResourceClasses() method.
* It is automatically populated with
* all resources defined in the project.
* If required, comment out calling this method in getClasses().
*/
private void addRestResourceClasses(Set<Class<?>> resources) {
resources.add(filt.NewCrossOriginResourceSharingFilter.class);
resources.add(service.UsersLoginFacadeREST.class);
}
}
UsersLoginFacadeREST
#Stateless //**Getting error at this line**
#Path("d.userslogin")
public class UsersLoginFacadeREST extends AbstractFacade<UsersLogin> {
#PersistenceContext(unitName = "dddPU")
private EntityManager em;
public UsersLoginFacadeREST() {
super(UsersLogin.class);
}
#POST
#Override
#Consumes({MediaType.APPLICATION_JSON})
public void create(UsersLogin entity) {
super.create(entity);
}
#PUT
#Path("{id}")
#Consumes({ MediaType.APPLICATION_JSON})
public void edit(#PathParam("id") String id, UsersLogin entity) {
super.edit(entity);
}
#DELETE
#Path("{id}")
public void remove(#PathParam("id") String id) {
super.remove(super.find(id));
}
#GET
#Path("{id}")
#Produces({ MediaType.APPLICATION_JSON})
public UsersLogin find(#PathParam("id") String id) {
return super.find(id);
}
#GET
#Override
#Produces({ MediaType.APPLICATION_JSON})
public List<UsersLogin> findAll() {
return super.findAll();
}
#GET
#Path("{from}/{to}")
#Produces({ MediaType.APPLICATION_JSON})
public List<UsersLogin> findRange(#PathParam("from") Integer from, #PathParam("to") Integer to) {
return super.findRange(new int[]{from, to});
}
#GET
#Path("count")
#Produces(MediaType.TEXT_PLAIN)
public String countREST() {
return String.valueOf(super.count());
}
#Override
protected EntityManager getEntityManager() {
return em;
}
}
RestClient.js
var app = {
// Create this closure to contain the cached modules
module: function () {
// Internal module cache.
var modules = {};
// Create a new module reference scaffold or load an
// existing module.
return function (name) {
// If this module has already been created, return it.
if (modules[name]) {
return modules[name];
}
// Create a module and save it under this name
return modules[name] = {Views: {}};
};
}()
};
(function (models) {
// Model for UsersLogin entity
models.UsersLogin = Backbone.Model.extend({
urlRoot: "http://localhost:8080/ddd/webresources/d.userslogin/",
idAttribute: 'userId',
defaults: {
userPassword: "",
featureGroup: "",
userName: ""
},
toViewJson: function () {
var result = this.toJSON(); // displayName property is used to render item in the list
result.displayName = this.get('userId');
return result;
},
isNew: function () {
// default isNew() method imlementation is
// based on the 'id' initialization which
// sometimes is required to be initialized.
// So isNew() is rediefined here
return this.notSynced;
},
sync: function (method, model, options) {
options || (options = {});
var errorHandler = {
error: function (jqXHR, textStatus, errorThrown) {
// TODO: put your error handling code here
// If you use the JS client from the different domain
// (f.e. locally) then Cross-origin resource sharing
// headers has to be set on the REST server side.
// Otherwise the JS client has to be copied into the
// some (f.e. the same) Web project on the same domain
alert('Unable to fulfil the request');
}
};
if (method === 'create') {
options.url = 'http://localhost:8080/ddd/webresources/d.userslogin/';
}
var result = Backbone.sync(method, model, _.extend(options, errorHandler));
return result;
}
});
// Collection class for UsersLogin entities
models.UsersLoginCollection = Backbone.Collection.extend({
model: models.UsersLogin,
url: "http://localhost:8080/ddd/webresources/d.userslogin/",
sync: function (method, model, options) {
options || (options = {});
var errorHandler = {
error: function (jqXHR, textStatus, errorThrown) {
// TODO: put your error handling code here
// If you use the JS client from the different domain
// (f.e. locally) then Cross-origin resource sharing
// headers has to be set on the REST server side.
// Otherwise the JS client has to be copied into the
// some (f.e. the same) Web project on the same domain
alert('Unable to fulfil the request');
}
};
var result = Backbone.sync(method, model, _.extend(options, errorHandler));
return result;
}
});
})(app.module("models"));
(function (views) {
views.ListView = Backbone.View.extend({
tagName: 'tbody',
initialize: function (options) {
this.options = options || {};
this.model.bind("reset", this.render, this);
var self = this;
this.model.bind("add", function (modelName) {
var row = new views.ListItemView({
model: modelName,
templateName: self.options.templateName
}).render().el;
$(self.el).append($(row));
$(self.el).parent().trigger('addRows', [$(row)]);
});
},
render: function (eventName) {
var self = this;
_.each(this.model.models, function (modelName) {
$(this.el).append(new views.ListItemView({
model: modelName,
templateName: self.options.templateName
}).render().el);
}, this);
return this;
}
});
views.ListItemView = Backbone.View.extend({
tagName: 'tr',
initialize: function (options) {
this.options = options || {};
this.model.bind("change", this.render, this);
this.model.bind("destroy", this.close, this);
},
template: function (json) {
/*
* templateName is element identifier in HTML
* $(this.options.templateName) is element access to the element
* using jQuery
*/
return _.template($(this.options.templateName).html())(json);
},
render: function (eventName) {
$(this.el).html(this.template(this.model.toJSON()));
return this;
},
close: function () {
var table = $(this.el).parent().parent();
table.trigger('disable.pager');
$(this.el).unbind();
$(this.el).remove();
table.trigger('enable.pager');
}
});
views.ModelView = Backbone.View.extend({
initialize: function (options) {
this.options = options || {};
this.model.bind("change", this.render, this);
},
render: function (eventName) {
$(this.el).html(this.template(this.model.toJSON()));
return this;
},
template: function (json) {
/*
* templateName is element identifier in HTML
* $(this.options.templateName) is element access to the element
* using jQuery
*/
return _.template($(this.options.templateName).html())(json);
},
/*
* Classes "save" and "delete" are used on the HTML controls to listen events.
* So it is supposed that HTML has controls with these classes.
*/
events: {
"change input": "change",
"click .save": "save",
"click .delete": "drop"
},
change: function (event) {
var target = event.target;
console.log('changing ' + target.id + ' from: ' + target.defaultValue + ' to: ' + target.value);
},
save: function () {
// TODO : put save code here
var hash = this.options.getHashObject();
this.model.set(hash);
if (this.model.isNew() && this.collection) {
var self = this;
this.collection.create(this.model, {
success: function () {
// see isNew() method implementation in the model
self.model.notSynced = false;
self.options.navigate(self.model.id);
}
});
} else {
this.model.save();
this.model.el.parent().parent().trigger("update");
}
return false;
},
drop: function () {
this.model.destroy({
success: function () {
/*
* TODO : put your code here
* f.e. alert("Model is successfully deleted");
*/
window.history.back();
}
});
return false;
},
close: function () {
$(this.el).unbind();
$(this.el).empty();
}
});
// This view is used to create new model element
views.CreateView = Backbone.View.extend({
initialize: function (options) {
this.options = options || {};
this.render();
},
render: function (eventName) {
$(this.el).html(this.template());
return this;
},
template: function (json) {
/*
* templateName is element identifier in HTML
* $(this.options.templateName) is element access to the element
* using jQuery
*/
return _.template($(this.options.templateName).html())(json);
},
/*
* Class "new" is used on the control to listen events.
* So it is supposed that HTML has a control with "new" class.
*/
events: {
"click .new": "create"
},
create: function (event) {
this.options.navigate();
return false;
}
});
})(app.module("views"));
$(function () {
var models = app.module("models");
var views = app.module("views");
var AppRouter = Backbone.Router.extend({
routes: {
'': 'list',
'new': 'create'
,
':id': 'details'
},
initialize: function () {
var self = this;
$('#create').html(new views.CreateView({
// tpl-create is template identifier for 'create' block
templateName: '#tpl-create',
navigate: function () {
self.navigate('new', true);
}
}).render().el);
},
list: function () {
this.collection = new models.UsersLoginCollection();
var self = this;
this.collection.fetch({
success: function () {
self.listView = new views.ListView({
model: self.collection,
// tpl-userslogin-list-itemis template identifier for item
templateName: '#tpl-userslogin-list-item'
});
$('#datatable').html(self.listView.render().el).append(_.template($('#thead').html())());
if (self.requestedId) {
self.details(self.requestedId);
}
var pagerOptions = {
// target the pager markup
container: $('.pager'),
// output string - default is '{page}/{totalPages}'; possiblevariables: {page}, {totalPages},{startRow}, {endRow} and {totalRows}
output: '{startRow} to {endRow} ({totalRows})',
// starting page of the pager (zero based index)
page: 0,
// Number of visible rows - default is 10
size: 10
};
$('#datatable').tablesorter({widthFixed: true,
widgets: ['zebra']}).
tablesorterPager(pagerOptions);
}
});
},
details: function (id) {
if (this.collection) {
this.userslogin = this.collection.get(id);
if (this.view) {
this.view.close();
}
var self = this;
this.view = new views.ModelView({
model: this.userslogin,
// tpl-userslogin-details is template identifier for chosen model element
templateName: '#tpl-userslogin-details',
getHashObject: function () {
return self.getData();
}
});
$('#details').html(this.view.render().el);
} else {
this.requestedId = id;
this.list();
}
},
create: function () {
if (this.view) {
this.view.close();
}
var self = this;
var dataModel = new models.UsersLogin();
// see isNew() method implementation in the model
dataModel.notSynced = true;
this.view = new views.ModelView({
model: dataModel,
collection: this.collection,
// tpl-userslogin-details is a template identifier for chosen model element
templateName: '#tpl-userslogin-details',
navigate: function (id) {
self.navigate(id, false);
},
getHashObject: function () {
return self.getData();
}
});
$('#details').html(this.view.render().el);
},
getData: function () {
return {
userId: $('#userId').val(),
userPassword: $('#userPassword').val(),
featureGroup: $('#featureGroup').val(),
userName: $('#userName').val()
};
}
});
new AppRouter();
Backbone.history.start();
});
Please help to resolve this issue
I am using Spring REST with Hibernate and i am fetching a particular record from database table passing id into my method. The method is working properly but if there is no record in table then i want false in a variable and if record exist then i want true in the variable in my json object.
Here is my Entity class Subscribe.java
#Entity
#Table(name="subscribe")
#JsonIgnoreProperties({"hibernateLazyInitializer", "handler"})
public class Subscribe implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue
#Column(name="id")
private long id;
#Column(name="subscribed_id")
private String subID;
#Column(name="subscriber_id")
private long subrID;
public long getSubrID() {
return subrID;
}
public void setSubrID(long subrID) {
this.subrID = subrID;
}
public String getSubID() {
return subID;
}
public void setSubID(String subID) {
this.subID = subID;
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
}
Here is my DAO class
#SuppressWarnings({ "unchecked", "rawtypes" })
public List<Subscribe> getSubscribeById(long id) throws Exception {
session = sessionFactory.openSession();
Criteria cr = session.createCriteria(Subscribe.class);
cr.add(Restrictions.eq("subrID", id));
List results = cr.list();
tx = session.getTransaction();
session.beginTransaction();
tx.commit();
return results;
}
And here is my Controller
#RequestMapping(value = "/subscribe/{id}", method = RequestMethod.GET)
public #ResponseBody
List<Subscribe> getSubscriber(#PathVariable("id") long id) {
List<Subscribe> sub = null;
try {
sub = profileService.getSubscribeById(id);
} catch (Exception e) {
e.printStackTrace();
}
return sub;
}
Please suggest me how can can i do this
Given the way your code is structured ( where you effectively pass the database object directly to the REST client ) there is no clean way that you can do this.
I think a more RESTful approach would be to return an HTTP code that indicates that the requested record could not be found. HTTP 404 would be the appropriate code to use.
So, in pseudo-code,
#RequestMapping(value = "/subscribe/{id}", method = RequestMethod.GET)
public #ResponseBody
List<Subscribe> getSubscriber(#PathVariable("id") long id) {
List<Subscribe> sub = null;
try {
sub = profileService.getSubscribeById(id);
} catch (Exception e) {
e.printStackTrace();
}
if ( sub.isEmpty() ){
return HTTP 404 result code
} else {
return sub;
}
}
Your client side code will need to be altered to respond to the HTTP result code rather than a boolean value, but otherwise would remain unchanged.
Dear spring Java professionals
please help me out in this :
I have a custom service in spring and I dont have any errors on my wildfly server when i run it . but when I do the below update request i am getting 400 bad request though im sending the format as specified in my controller
inside my controller :
#RequestMapping(value = "/user/updatefilters/{Id}", method = RequestMethod.POST)
public Response updateFilter(#PathVariable("Id") Long Id, #RequestBody #Valid Filter Filter) {
FilterService.updateFilter(Id, Filter);
HashMap<String, Object> response = new HashMap<>();
response.put("messages", null);
response.put("success", Boolean.valueOf(true));
return Response.instance().friendlyName("filter-updated").object(response).statusCode(HttpStatus.OK);
}
inside my service file :
public void updateFilter(Long Id,Filter Filter) {
List<Filter> currentFilter = FilterRepo.getFilters(Id, Filter.getFilterId().longValue(),null);
currentFilter.get(0).setLabel(Filter.getLabel());
FilterRepo.save(currentFilter.get(0));
for (FilterField FilterField : Filter.getFilterFields()) {
FilterField currentFilterField = FilterFieldRepo.getFilterField(FilterField.getfieldId());
if (currentFilterField != null) {
currentFilterField.setfield(FilterField.getfield());
currentFilterField.setTypeId(FilterField.getTypeId());
FilterFieldRepo.save(currentFilterField);
}
}
}
inside my repository :
public List<Filter> getFilterList(Long Id, String type) {
List<Filter> FilterField = FilterFieldRepo.getFilterFields(Id,type);
return FilterField;
}
public void updateFilter(Long Id,Filter Filter) {
List<Filter> currentFilter = FilterRepo.getFilters(Id, Filter.getFilterId().longValue(),null);
currentFilter.get(0).setLabel(Filter.getLabel());
FilterRepo.save(currentFilter.get(0));
for (FilterField FilterField : Filter.getFilterFields()) {
FilterField currentFilterField = FilterFieldRepo.getFilterField(FilterField.getfieldId());
if (currentFilterField != null) {
currentFilterField.setfield(FilterField.getfield());
currentFilterField.setTypeId(FilterField.getTypeId());
FilterFieldRepo.save(currentFilterField);
}
}
}
Please note that inside my entity I added a transient list like this :
#Transient
private List<FilterField> filterFields;
updated :
this is my Filter class i generated the crud in netbeans but added the transuent list manually:
#Entity
#Table(schema="hitmeister",name = "filters")
#NamedQueries({
#NamedQuery(name = "Filter.findAll", query = "SELECT s FROM Filter s"),
#NamedQuery(name = "Filter.findByFilterId", query = "SELECT s FROM Filter s WHERE s.filterId = :filterId"),
#NamedQuery(name = "Filter.findById", query = "SELECT s FROM Filter s WHERE s.Id = :Id"),
#NamedQuery(name = "Filter.findByLabel", query = "SELECT s FROM Filter s WHERE s.label = :label"),
#NamedQuery(name = "Filter.findByInsertionDate", query = "SELECT s FROM Filter s WHERE s.insertionDate = :insertionDate"),
#NamedQuery(name = "Filter.findByIsActive", query = "SELECT s FROM Filter s WHERE s.isActive = :isActive"),
#NamedQuery(name = "Filter.findByType", query = "SELECT s FROM Filter s WHERE s.type = :type")})
public class Filter implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Basic(optional = false)
#Column(name = "filter_id")
private Integer filterId;
#Basic(optional = false)
#NotNull
#Column(name = "id")
private int Id;
#Basic(optional = false)
#NotNull
#Size(min = 1, max = 500)
#Column(name = "label")
private String label;
#Basic(optional = true)
#Column(name = "insertion_date")
#Temporal(TemporalType.TIMESTAMP)
private Date insertionDate;
#Column(name = "is_active")
private Boolean isActive;
#Basic(optional = false)
#NotNull
#Size(min = 1, max = 20)
#Column(name = "type")
private String type;
#Transient
private List<FilterField> filterFields;
public Filter() {
}
public Filter(Integer filterId) {
this.filterId = filterId;
}
public Filter(Integer filterId, int Id, String label, Date insertionDate, String type) {
this.filterId = filterId;
this.Id = Id;
this.label = label;
this.insertionDate = insertionDate;
this.type = type;
}
public Integer getFilterId() {
return filterId;
}
public void setFilterId(Integer filterId) {
this.filterId = filterId;
}
public int getId() {
return Id;
}
public void setuserId(int Id) {
this.userId = userId;
}
public String getLabel() {
return label;
}
public void setLabel(String label) {
this.label = label;
}
public Date getInsertionDate() {
return insertionDate;
}
public void setInsertionDate(Date insertionDate) {
this.insertionDate = insertionDate;
}
public Boolean getIsActive() {
return isActive;
}
public void setIsActive(Boolean isActive) {
this.isActive = isActive;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
#Override
public int hashCode() {
int hash = 0;
hash += (filterId != null ? filterId.hashCode() : 0);
return hash;
}
#Override
public boolean equals(Object object) {
// TODO: Warning - this method won't work in the case the id fields are not set
if (!(object instanceof Filter)) {
return false;
}
Filter other = (Filter) object;
if ((this.filterId == null && other.filterId != null) || (this.filterId != null && !this.filterId.equals(other.filterId))) {
return false;
}
return true;
}
#Override
public String toString() {
return " Filter #"+ filterId ;
}
public List<FilterField> getFilterFields() {
return filterFields;
}
public void setFilterFields(List<FilterField> filterFields) {
this.filterFields = filterFields;
}
}
If you need my entity code i can post it as well
Thanks In advance !
My first recommendation: (OP tried and it didn't work, she was sending POST request)
Change your mapping as below and I think you should be fine. Request from browser address bar is a GET request.
As you can see below, HTTP 400 comes when server is unable to understand the request client is sending, and in your case you are sending GET but server has nothing for GET but for POST, so 400.
W3C HTTP 400
10.4.1 400 Bad Request
The request could not be understood by the server due to malformed
syntax. The client SHOULD NOT repeat the request without
modifications.
Code fix:
#RequestMapping(value = "/user/updatefilters/{Id}", method = RequestMethod.GET)
My second recommendation:
I am not Spring expert but here are my 2 cents you can try based on the JSON object you have provided and your Filter mapping - (1.) Change userId to Id, (2.) Have insertionDate as NULL, instead of an empty string.
Make sure your JSON string variables are mapped case-sensitively with your Filter class mapping, and their values are compatible with reference types.
Either your request format is not what Spring expects, or one of the Filter validations is failing. Add a org.springframework.validation.Errors argument and dump the values to find out what validations failed.
public Response updateFilter(#PathVariable("Id") Long Id, #RequestBody #Valid Filter Filter, Errors filterErrors) {
You can sniff the actual traffic using curl or a network monitoring tool to make sure the HTTP transaction is really what you think it is.
EDIT: Having looked at the JSON in one of your comments, I think this is going to turn out to be upper/lower case in your JSON field names. Either change "Id" to "id" and "FilterId" to "filterId", or annotate the Filter fields with #XmlElement(name = "Id") and #XmlElement(name = "FilterId"). Java Bean property names are case sensitive.
EDIT 2: Filter.setuserId(int Id) is broken as well. You need a setId() method for deserializing the bean, and you need to change the method so it stores the passed argument instead of just setting userId to itself.