HashMap cannot be cast to Entity object in Restful API netbeans+Apacheplume7 - java

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

Related

JPA Criteria Tuple query fails with missing columns from join in group by clause (Spring Boot 2.7.8 and hibernate 5.6.14.Final)

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

How to find data in database in Spring Boot by my variable?

I have this code:
#GetMapping("/notes/{id}")
public ResponseEntity<Note> getNoteById(#PathVariable(value = "id") Long id) {
Note note = noteRepository.findOne(id);
if(note == null) {
return ResponseEntity.notFound().build();
}
return ResponseEntity.ok().body(note);
}
So this method just finds information by the sent id.
The method noteRepository.findOne() accepts only Long or class extends org.springframework.data.domain.Example
I want to retrive data by my own variable "secretkey" (String). How can I do this?
Use this method :
#GetMapping("/notes/byKey/{secretKey}")
public ResponseEntity<Note> getNoteById(#PathVariable(value = "secretKey") String secretKey) {
Note note = noteRepository.findBySecretKey(secretKey);
if(note == null) {
return ResponseEntity.notFound().build();
}
return ResponseEntity.ok().body(note);
}
in your NoteRepositoryclass add this method:
Note findBySecretKey(String secretKey);
note that I also changed the request path.
Assume that you are using Spring Data:
#Entity
#Table(name="note")
public class Note {
#Id
#GeneratedAuto
private Long id;
#Column(name="secretKey")
private String secretKey;
// getters, setters
}
//Repository
public interface NoteRepository extends JpaRepository<Note, Long> {
List<Note> findBySecretKey(String secretKey);
}
// Controller
#GetMapping(path = "/notes/secretKey/{secretKey}", produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<Note> getNoteBySecretKey(
#PathVariable(value = "secretKey") String secretKey) {
Note note = noteRepository.findBySecretKey(secretKey);
if(note == null) {
return ResponseEntity.notFound().build();
}
return ResponseEntity.ok().body(note);
}

Correctly posting JSON data to restful webservice netbeans

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.

java service 400 bad request error

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.

When I select a row in MySQL using hibernate classes, it makes an update automatically

I'm trying to develop a blacklist for my users including several variables. So when a user sign up in my application, I check if some parameters are blacklisted or not.
The problem is that when I perform a select and the database find something that fits with my search, it automatically perform an update an clean that row.
This is the MySQL log:
86 Query select * from blacklist where mobile_token = 'b'
86 Query SHOW WARNINGS
86 Query select ##session.tx_read_only
86 Query update mydatabase.blacklist set email=null, iban=null, mobile_token=null, nif=null where blacklist_id=1
86 Query SHOW WARNINGS
86 Query commit
86 Query SET autocommit=1
86 Query SET autocommit=1
86 Query set session transaction read write
This is my table:
My model:
package models.classes_hibernate;
import javax.persistence.*;
import static javax.persistence.GenerationType.IDENTITY;
#Entity
#Table(name="blacklist"
,catalog="mydatabase"
)
public class Blacklist implements java.io.Serializable {
private Integer blacklistId;
private String mobileToken;
private String iban;
private String nif;
private String email;
public Blacklist() {
}
#Id #GeneratedValue(strategy=IDENTITY)
#Column(name="blacklist_id", unique=true, nullable=false)
public Integer getBlacklistId() {
return this.blacklistId;
}
public void setBlacklistId(Integer blacklistId) {
this.blacklistId = blacklistId;
}
#Column(name="mobile_token", nullable = false)
public String getMobileToken() {
return this.mobileToken;
}
public void setMobileToken(String name) {
this.mobileToken = mobileToken;
}
#Column(name="iban", nullable = false)
public String getIban() {
return this.iban;
}
public void setIban(String name) {
this.iban = iban;
}
#Column(name="nif", nullable = false)
public String getNif() {
return this.nif;
}
public void setNif(String name) {
this.nif = nif;
}
#Column(name="email", nullable = false)
public String getEmail() {
return this.email;
}
public void setEmail(String name) {
this.email = email;
}
}
And my DAO:
package models.dao;
import com.google.common.collect.Lists;
import models.classes_hibernate.Blacklist;
import models.pages.Page;
import org.hibernate.Criteria;
import org.hibernate.Session;
import org.hibernate.criterion.Restrictions;
import org.hibernate.type.StringType;
import play.Logger;
import play.db.jpa.JPA;
import play.db.jpa.Transactional;
import javax.persistence.NoResultException;
import javax.persistence.Query;
import java.util.List;
public class BlacklistDAOImpl implements MyAppCRUDDAOInterface<Blacklist> {
#Override
public void create(Blacklist entity) {
JPA.em().persist(entity);
}
#Override
public Blacklist read(Integer id) {
return JPA.em().find(Blacklist.class, id);
}
public Page<Blacklist> readAll(String orientation,int pageSize, int beginElementId)
{
Query query = null;
List<Blacklist> blacklists = null;
boolean areThereMore = false;
Page<Blacklist> allBlacklists = null;
int size = 0;
if(orientation.equals("all")) {
query = JPA.em().createNativeQuery("select * from blacklist",Blacklist.class);
}
if(orientation.equals("lt")) {
query = JPA.em().createNativeQuery("select * from blacklist where blacklist_id < ? ORDER BY blacklist_id DESC",Blacklist.class);
query.setParameter(1, beginElementId);
size =query.getResultList().size();
query.setMaxResults(pageSize);
}
if(orientation.equals("gt")) {
query = JPA.em().createNativeQuery("select * from blacklist blacklist_id > ? ORDER BY blacklist_id ASC",Blacklist.class);
query.setParameter(1, beginElementId);
size =query.getResultList().size();
query.setMaxResults(pageSize);
}
if (size>pageSize)
areThereMore = true;
try {
blacklists = query.getResultList();
if (orientation.equals("gt")) {
List<Blacklist> reverseList = Lists.reverse(blacklists);
blacklists = reverseList;
}
allBlacklists = new Page<Blacklist>(blacklists, areThereMore, "Blacklist");
return allBlacklists;
}
catch(NoResultException nre){
allBlacklists=null;
return allBlacklists;
}
}
#Override
public void update(Blacklist entity) {
JPA.em().merge(entity);
}
#Override
public void delete(Blacklist entity) {
JPA.em().remove(entity);
}
#Override
public boolean isManaged(Blacklist entity) {
return JPA.em().contains(entity);
}
#Override
public void close() {
JPA.em().close();
}
public Boolean isMobileTokenBlacklisted(String mobileToken) {
Query query = JPA.em().createNativeQuery("select * from blacklist where mobile_token = ?",Blacklist.class);
query.setParameter(1, mobileToken);
Blacklist blacklist;
try {
Logger.debug("Voy a comprobar");
blacklist = (Blacklist)query.getSingleResult();
} catch (NoResultException nre){
blacklist=null;
}
return blacklist != null;
}
isMobileTokenBlacklisted call:
#POST
#Path("/api/user")
#ApiOperation(position = 3, nickname ="user", value = "Sign up new user",notes = "Minimum JSON required: ",
response = AppUserJSON.class, httpMethod = "POST")
#BodyParser.Of(BodyParser.Json.class)
#Transactional
public static Result signup() {
AppUserDAOImpl appUserDAO = new AppUserDAOImpl();
AppUserJSON user = null;
AppUser appUser = null;
BlacklistDAOImpl blacklistDAO = new BlacklistDAOImpl();
try {
user = parse();
String encrypt_nif = user.nif;
String encrypt_authorization = user.parental_authorization;
String encrypt_password = user.password;
try {
encrypt_password= EncryptUtils.encrypt(config1.getString("key"),user.password);
if(user.nif!= null)
encrypt_nif = EncryptUtils.encrypt(config1.getString("key"),user.nif);
if(user.parental_authorization!= null)
encrypt_authorization = EncryptUtils.encrypt(config1.getString("key"),user.parental_authorization);
} catch (Exception e) {
e.printStackTrace();
}
appUser = new AppUser(new Date(), new Date(),user.email.toLowerCase(), encrypt_password, user.mobile_token,
user.mobile_device, 0, 0, 0, 0, encrypt_nif,
false,"NOT_LOCKED", encrypt_authorization, 0, false);
if (user.email == null) {
return status (200, "email missing");
} else if (blacklistDAO.isEmailBlacklisted(user.email)){
return status(401, "Email is blacklisted");
}
if (user.password == null)
return status(201, "password missing");
if (user.mobile_token == null) {
return status (206, "mobileToken missing");
} else if (blacklistDAO.isMobileTokenBlacklisted(user.mobile_token)){
Logger.debug("MobileToken blacklisted");
return status(401, "Mobile token is blacklisted");
}
if (user.mobile_device== null)
return status(207, "mobileDevice missing");
else{
appUserDAO.create(appUser);
user.app_user_id= appUser.getAppUserId();
return ok(Json.toJson(user));
}
} catch (IncompleteJSONException e) {
return badRequest("IncompleteJSONException");
} catch (DuplicateJSONException e) {
return badRequest("DuplicateJSONException");
}
}
Thanks!
I don't know where it comes from but we can find a way to correct some thing to improve your code and exclude definitely some queries.
Be sure to use bracket around your if. It's not compulsory but is a way to make the code clearer
In the signup method, the else is not logical. It only depends on the last if (mobiledevice test). You probably want to create your user if all test are wrong.
Here you just want to test if you have any blacklisted element corresponding to your research. You can use COUNT function or even EXISTS which can be more efficient maybe.
You can use Debug mode to see where your update is done too.

Categories

Resources