I am developing a chat system using Spring boot. At this point I am required to display all the users where isAdmin field is set to false in the database.
This is the query for the getByAdmin in DAO class:
public User getByAdmin(boolean isAdmin) {
return (User) getSession().createQuery(
"from User where isAdmin = :isAdmin")
.setParameter("isAdmin", isAdmin);
}
In my controller.java this is the point where I am calling the above method to make the check if the admin is set to true:
#ResponseBody
#RequestMapping(value = "/get-all-users", method = RequestMethod.GET)
public List<User> getAllUsers() {
try {
boolean isAdmin = true;
if(_userDao.getByAdmin(isAdmin).equals(true))
{
return _userDao.getAll();
}
} catch (Exception e) {
logger.error("Exception in fetching users: ", e.getStackTrace());
}
return null;
}
this is my bean definition for the Users.java
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "userId")
private Long id;
#Column(nullable = false)
private String name;
#Column(unique = true, nullable = false)
private String email;
#Column(nullable = false)
private long timestamp;
#Column(nullable = true)
private boolean isAdmin;
this is where I am making the check so nothing was returned
try {
boolean isAdmin = true;
if(_userDao.getByAdmin(isAdmin).equals(true))
{
return _userDao.getAll();
}
if I remove the above check I will be able to see all users
My challenge is that nothing was returned calling getAllUsers method.
Please what could be wrong with my logic?
if(_userDao.getByAdmin(isAdmin).equals(true))
You are comparing User with boolean. Probably you wanted to check something like:
if(_userDao.getByAdmin(isAdmin) != null)
?
Related
This will take some explaining. So, I have an entity called Invoice and a related table called Errors, which is used to store some processing errors.
In a DAO class, I have a query for fetch the errors with some specific criteria:
public Errors loadLastError(Invoice i) {
try (Session session = factory.openSession()) {
Query query = session.createQuery("select er from Errors er" +
" inner join er.invoice i" +
" where er.invoice = :invoice" +
" and i.status <> :code" +
" and i.proccessStatus = :status" +
" order by er.id desc");
query.setParameter("invoice", invoice);
query.setParameter("code", "001");
query.setParameter("status", "form_error");
var result = query.getSingleResult();
return (Errors) result;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
This works fine: will only get results when the conditions match. When they don't, I get the expected null result where this method is called:
this.invoice.setError(loadLastError(this.invoice);
When inspecting the code, I can see that the this.invoice object was updated correctly with a null result.
But, as soon as I pass this object invoice to another class in order to do some proccessing (send notifications basically by JSON), it gets there with a Errors object loaded, as if my original query had actually found something, which it didn't.
The following are a shortened example of my entity classes:
The Invoice:
#Entity
#DynamicUpdate
#Table(name = "data.invoice")
#TypeDef(
name = "pgsql_enum",
typeClass = PostgreSQLEnumType.class
)
public class Invoice implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
#JsonIgnore
#Column(name = "proccessStatus")
private String proccessStatus;
#JsonIgnore
#Column(name = "status")
private String status;
#JsonIgnore
#OneToOne(mappedBy = "invoice", fetch = FetchType.LAZY)
private Errors errors;
public Integer getId() {
return id;
}
public String getProccessStatus() {
return proccessStatus;
}
public void setProccessStatus(String proccessStatus) {
this.proccessStatus= proccessStatus;
}
public String getStatus() {
return status;
}
public void setStatus(String status) {
this.status= status;
}
public Errors getErrosr() {
return errors;
}
public void setErrors(Errorserrors) {
this.errors= errors;
}
The Errors entity:
#Entity
#Table(name = "data.invoice_errors")
public class Errors implements Serializable {
public Errors() {
}
public Errors(Invoice invoice, String error) {
this.invoice= invoice;
this.error = error;
}
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
#OneToOne
#JoinColumn(name = "id_invoice")
private Invoice invoice;
private String error;
#Column(name = "created_at")
private LocalDateTime createdAt;
public Integer getId() {
return id;
}
public Invoice getInvoice() {
return invoice;
}
public void setInvoice(Invoice invoice) {
this.invoice = invoice;
}
public String getError() {
return error;
}
public void setError(String error) {
this.error = error;
}
public LocalDateTime getCreatedAt() {
return createdAt;
}
This behaviour seems very strange and I don't know how to diagnose it and what may be wrong. Any input would be very appreciated.
What I'm expecting is that the entity don't get updated out of nowhere with a result that wasn't found initially because it simply didn't match the search criteria in the first place.
I'm a colossal idiot. The issue was that the notification class was refreshing the model. Changed the database search to go after the refresh and fixed the problem.
JDK 17
SpringBoot latest
JPA latest
MySQL 8.0.31
I am trying to implement a strategy that makes sure that both the name and the email address of each user are unique.
User entity:
#Entity
public class User {
......
#EmbeddedId
protected UserId id;
......
}
User id:
#Embeddable
public class UserId implements Serializable {
#Serial
private static final long serialVersionUID = -622156255674132106L;
#Column(name = "name", nullable = false)
protected String name = "";
#Column(name = "email", nullable = false)
protected String email = "";
public UserId(String name, String email) {
setName(name);
setEmail(email);
}
public UserId() {}
public void setName(String name) {
this.name = name;
}
public String getName() {
return Objects.requireNonNullElse(name, "");
}
public void setEmail(String email) {
this.email = email;
}
public String getEmail() {
return Objects.requireNonNullElse(email, "");
}
}
Now, by default, it is marked as a conflict only if userA.name == userB.name && userA.email == userB.email, which means there can be two users having the same email address as long as they do not share one single name. How to stop this from happening? What I expect is userA.name == userB.name || userA.email == userB.email.
I've tried overriding equals() and hashcode() in the following way.
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof UserId userId)) return false;
if (Objects.equals(name, userId.name)) return true;
return Objects.equals(email, userId.email);
}
#Override
public int hashCode() {
int result = name != null ? name.hashCode() : 0;
result = 31 * result + (email != null ? email.hashCode() : 0);
return result;
}
However, it does not work. Also, breakpoints inside these two functions are not reached.
==========Edited==========
I've solved the original problem. But when it comes to UsersRepository.existsById(), it's still considered not to exist if either of the two columns does not match. How can I fix it?
Whether you do this via annotations and schema generation, or just by creating / modifying the schema directly, the answer is the same.
You will need to create a single unique constraint in the database naming both columns, not two separate constraints.
If you want a schema generation annotation to do this, supply the #UniqueConstraint annotation to the #Table annotation, e.g.
#Table(uniqueConstraints = {
#UniqueConstraint(columnNames = {
"name", "email"
})
})
public class UserId implements Serializable {
#Serial
private static final long serialVersionUID = -622156255674132106L;
#Column(name = "name", nullable = false, unique=true)
protected String name = "";
#Column(name = "email", nullable = false, unique=true)
protected String email = "";
I need that every time I list a portfolio it returns all the images that exist in that specific portfolio
I can list 1 by 1 via ID but when I send my endpoint to list all photos belonging to the ID of a specific portfolio it only returns me null
Photo Class
#Entity
public class Foto {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
private String nomeArquivo;
#ManyToOne(cascade = CascadeType.MERGE)
private Perfil autonomo;
#Column(length = 5_000_000)
private byte[] fotoByte;
private String tipoArquivo;
}//Getters and Setters
AutonomoService
#Autowired
FotosRepository fotosRepository;
#Autowired
PerfisRepository perfisRepository;
public List<byte[]> portfolio(int id){
if (perfisRepository.existsById(id)) {
return fotosRepository.findAllByAutonomoId(id).stream().map(f-> f.getFotoByte()).collect(Collectors.toList());
}
else {
return null;
}
} //Getters and Setters
Controller
#GetMapping("/portfolio/fotos/{id}")
public ResponseEntity<List<byte[]>> getPortfolioAutonomo(#PathVariable int id) throws IOException {
List<byte[]> result = autonomoService.portfolio(id);
return ResponseEntity.status(200).body(result);
}
And this is the way I can get 1 photo by its id
#GetMapping("/portfolio/{id}")
public ResponseEntity getPortfolio(#PathVariable int id){
Optional<Foto> anexoOptional = fotosRepository.findById(id);
if (anexoOptional.isPresent()) {
Foto anexo = anexoOptional.get();
return ResponseEntity.status(200)
.header("content-type", anexo.getTipoArquivo())
.header("content-disposition", "filename=" + anexo.getNomeArquivo())
.body(anexo.getFotoByte());
} else {
return ResponseEntity.status(404).build();
}
}
Instead of
return fotosRepository.findAllByAutonomoId(id).stream().map(f-> f.getFotoByte()).collect(Collectors.toList());
Can you try with
return fotosRepository.findAllById(id).stream().map(f-> f.getFotoByte()).collect(Collectors.toList());
If still this is not working, better to go with #Query implemention.
Have the same DTO object for POST and PUT methods:
class AcmeRequest {
private String id;
#NotEmpty
private String productCode;
private String description;
}
For POST request I always expect to see productCode field, that's why I specified #NotEmpty annotation but when PUT request received productCode should be optional.
Is it possible some how just to skip #NotEmpty when request is PUT?
Every Hibernate Validator annotation has a groups parameter. Through interfaces, you can control which validations are activated. See more at docs.
In controller level, specify which groups must be activated with the #Validated annotation.
Below, there is a small example from one of my demo projects. I once had the same question as you.
Entity:
#Entity
#Table(name = "tasks")
#Getter #Setter
public class Task
{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Null(message = "You can't provide a task ID manually. ID's are automatically assigned by our internal systems.", groups = {TaskInsertValidatorGroup.class})
#NotNull(message = "You must provide an id" , groups = TaskUpdateValidatorGroup.class)
private Integer id;
#NotBlank(message = "Task description cannot be empty")
#Length(max = 255 , message = "Task description length must not exceed 255 characters")
private String description;
#JsonProperty("is_completed")
#Column(name = "is_completed")
private Boolean isCompleted = false;
#CreationTimestamp
#JsonProperty("created_on")
#JsonFormat(pattern="dd-MM-yyyy HH:mm:ss")
#Column(name = "created_on", updatable = false)
private Timestamp creationDate;
#UpdateTimestamp
#JsonProperty("last_modified")
#JsonFormat(pattern="dd-MM-yyyy HH:mm:ss")
#Column(name = "last_modidied")
private Timestamp lastModificationDate;
#Override
public boolean equals(Object o)
{
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Task task = (Task) o;
return id.equals(task.id);
}
#Override
public int hashCode()
{
return Objects.hash(id);
}
}
Interfaces:
public interface TaskInsertValidatorGroup {}
public interface TaskUpdateValidatorGroup{}
Controller:
RestController
#RequestMapping("/api")
public class TaskRestController
{
#Autowired
private TaskService taskService;
#GetMapping("/tasks/{id}")
public ResponseEntity<?> getTask(#PathVariable Integer id)
{
return new ResponseEntity<>(taskService.findTask(id),HttpStatus.OK);
}
#GetMapping("/tasks")
public ResponseEntity<?> getTasks()
{
return new ResponseEntity<>(taskService.findAllTasks(),HttpStatus.OK);
}
#PostMapping("/tasks")
public ResponseEntity<?> addTask(#Validated(TaskInsertValidatorGroup.class) #RequestBody Task task)
{
taskService.saveTask(task);
APISuccessResponse response = APISuccessResponse.builder()
.info("Task added")
.build();
return new ResponseEntity<>(response,HttpStatus.OK);
}
#RequestMapping(value = "/tasks" , method = RequestMethod.PATCH)
public ResponseEntity<?> updateTask(#Validated(TaskUpdateValidatorGroup.class) #RequestBody Task task)
{
taskService.updateTask(task);
APISuccessResponse response = APISuccessResponse.builder()
.info("Task Updated")
.build();
return new ResponseEntity<>(response,HttpStatus.OK);
}
#RequestMapping(value = "/tasks/{id}", method = RequestMethod.DELETE)
public ResponseEntity<?> removeTask(#PathVariable Integer id)
{
taskService.removeTask(id);
APISuccessResponse response = APISuccessResponse.builder()
.info("Task Deleted")
.build();
return new ResponseEntity<>(response,HttpStatus.OK);
}
}
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.