SonarQube (Sonar) + EclipseLink: incorrect error 'Comparison of String parameter using == or !=' - jpa

I have the following class, using EclipseLink JPA:
package my.package;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.SequenceGenerator;
import javax.persistence.Version;
import my.package.api.Address;
#Entity(name = "Address")
#SequenceGenerator(name = "sequence", sequenceName = "seq_address")
public class AddressJpaEntity implements Address {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "sequence")
private Long id;
#Version
private Long version;
private String street;
public AddressJpaEntity() {
}
public AddressJpaEntity(String street) {
this.street = street;
}
#Override
public Long getId() {
return id;
}
public Long getVersion() {
return version;
}
public void setVersion(Long version) {
this.version = version;
}
#Override
public String getStreet() {
return street;
}
#Override
public void setStreet(String street) {
this.street = street;
}
}
When I do a SonarQube-run, I get a lot of the following (incorrect) errors:
Bad practice - Comparison of String parameter using == or !=
This code compares a java.lang.String parameter for reference equality using the == or != operators. Requiring callers to pass only String constants or interned strings to a method is unnecessarily fragile, and rarely leads to measurable performance gains. Consider using the equals(Object) method instead.
findbugs | ES_COMPARING_PARAMETER_STRING_WITH_EQ
Comparison of String parameter using == or != in my.package.AddressJpaEntity._persistence_set(String, Object)
For now, I solved it by setting the issues as false positive, but we will add more similar classes in the future and I don't want to do this each time.
How can I make Sonar not mark these errors, without using 'False positive' all the time?

Please check this post for a solution. It shows how to create an exclusion filter for FindBugs.
In this case, you'll want to ignore ES_COMPARING_PARAMETER_STRING_WITH_EQ warnings.
I still think it's incorrect to use ==, though, but apparently, that's not something you can help.

Related

how to store PostgreSQL jsonb using SpringBoot + JPA?

I'm working on a migration software that will consume unknown data from REST services.
I already think about use MongoDB but I decide to not use it and use PostgreSQL.
After read this I'm trying to implement it in my SpringBoot app using Spring JPA but I don't know to map jsonb in my entity.
Tried this but understood nothing!
Here is where I am:
#Repository
#Transactional
public interface DnitRepository extends JpaRepository<Dnit, Long> {
#Query(value = "insert into dnit(id,data) VALUES (:id,:data)", nativeQuery = true)
void insertdata( #Param("id")Integer id,#Param("data") String data );
}
and ...
#RestController
public class TestController {
#Autowired
DnitRepository dnitRepository;
#RequestMapping(value = "/dnit", method = RequestMethod.GET)
public String testBig() {
dnitRepository.insertdata(2, someJsonDataAsString );
}
}
and the table:
CREATE TABLE public.dnit
(
id integer NOT NULL,
data jsonb,
CONSTRAINT dnit_pkey PRIMARY KEY (id)
)
How can I do this?
Note: I don't want/need an Entity to work on. My JSON will always be String but I need jsonb to query the DB
Tried this but understood nothing!
To fully work with jsonb in Spring Data JPA (Hibernate) project with Vlad Mihalcea's hibernate-types lib you should just do the following:
1) Add this lib to your project:
<dependency>
<groupId>com.vladmihalcea</groupId>
<artifactId>hibernate-types-52</artifactId>
<version>2.2.2</version>
</dependency>
2) Then use its types in your entities, for example:
#Data
#NoArgsConstructor
#Entity
#Table(name = "parents")
#TypeDef(name = "jsonb", typeClass = JsonBinaryType.class)
public class Parent implements Serializable {
#Id
#GeneratedValue(strategy = SEQUENCE)
private Integer id;
#Column(length = 32, nullable = false)
private String name;
#Type(type = "jsonb")
#Column(columnDefinition = "jsonb")
private List<Child> children;
#Type(type = "jsonb")
#Column(columnDefinition = "jsonb")
private Bio bio;
public Parent(String name, List children, Bio bio) {
this.name = name;
this.children = children;
this.bio = bio;
}
}
#Data
#NoArgsConstructor
#AllArgsConstructor
public class Child implements Serializable {
private String name;
}
#Data
#NoArgsConstructor
#AllArgsConstructor
public class Bio implements Serializable {
private String text;
}
Then you will be able to use, for example, a simple JpaRepository to work with your objects:
public interface ParentRepo extends JpaRepository<Parent, Integer> {
}
parentRepo.save(new Parent(
"parent1",
asList(new Child("child1"), new Child("child2")),
new Bio("bio1")
)
);
Parent result = parentRepo.findById(1);
List<Child> children = result.getChildren();
Bio bio = result.getBio();
You are making things overly complex by adding Spring Data JPA just to execute a simple insert statement. You aren't using any of the JPA features. Instead do the following
Replace spring-boot-starter-data-jpa with spring-boot-starter-jdbc
Remove your DnitRepository interface
Inject JdbcTemplate where you where injecting DnitRepository
Replace dnitRepository.insertdata(2, someJsonDataAsString ); with jdbcTemplate.executeUpdate("insert into dnit(id, data) VALUES (?,to_json(?))", id, data);
You were already using plain SQL (in a very convoluted way), if you need plain SQL (and don't have need for JPA) then just use SQL.
Ofcourse instead of directly injecting the JdbcTemplate into your controller you probably want to hide that logic/complexity in a repository or service.
There are already several answers and I am pretty sure they work for several cases. I don't wanted to use any more dependencies I don't know, so I look for another solution.
The important parts are the AttributeConverter it maps the jsonb from the db to your object and the other way around. So you have to annotate the property of the jsonb column in your entity with #Convert and link your AttributeConverter and add #Column(columnDefinition = "jsonb") as well, so JPA knows what type this is in the DB. This should already make it possible to start the spring boot application. But you will have issues, whenever you try to save() with the JpaRepository. I received the message:
PSQLException: ERROR: column "myColumn" is of type jsonb but
expression is of type character varying.
Hint: You will need to rewrite or cast the expression.
This happens because postgres takes the types a little to serious.
You can fix this by a change in your conifg:
datasource.hikari.data-source-properties: stringtype=unspecified
datasource.tomcat.connection-properties: stringtype=unspecified
Afterwards it worked for me like a charm, and here is a minimal example.
I use JpaRepositories:
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
#Repository
public interface MyEntityRepository extends JpaRepository<MyEntity, Integer> {
}
The Entity:
import javax.persistence.Column;
import javax.persistence.Convert;
public class MyEntity {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
protected Integer id;
#Convert(converter = MyConverter.class)
#Column(columnDefinition = "jsonb")
private MyJsonObject jsonContent;
}
The model for the json:
public class MyJsonObject {
protected String name;
protected int age;
}
The converter, I use Gson here, but you can map it however you like:
import javax.persistence.AttributeConverter;
import javax.persistence.Converter;
#Converter(autoApply = true)
public class MyConverter implements AttributeConverter<MyJsonObject, String> {
private final static Gson GSON = new Gson();
#Override
public String convertToDatabaseColumn(MyJsonObject mjo) {
return GSON.toJson(mjo);
}
#Override
public MyJsonObject convertToEntityAttribute(String dbData) {
return GSON.fromJson(dbData, MyJsonObject.class);
}
}
SQL:
create table my_entity
(
id serial primary key,
json_content jsonb
);
And my application.yml (application.properties)
datasource:
hikari:
data-source-properties: stringtype=unspecified
tomcat:
connection-properties: stringtype=unspecified
For this case, I use the above tailored converter class, you are free to add it in your library. It is working with the EclipseLink JPA Provider.
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.log4j.Logger;
import org.postgresql.util.PGobject;
import javax.persistence.AttributeConverter;
import javax.persistence.Converter;
import java.io.IOException;
import java.sql.SQLException;
import java.util.Map;
#Converter
public final class PgJsonbToMapConverter implements AttributeConverter<Map<String, ? extends Object>, PGobject> {
private static final Logger LOGGER = Logger.getLogger(PgJsonbToMapConverter.class);
private static final ObjectMapper MAPPER = new ObjectMapper();
#Override
public PGobject convertToDatabaseColumn(Map<String, ? extends Object> map) {
PGobject po = new PGobject();
po.setType("jsonb");
try {
po.setValue(map == null ? null : MAPPER.writeValueAsString(map));
} catch (SQLException | JsonProcessingException ex) {
LOGGER.error("Cannot convert JsonObject to PGobject.");
throw new IllegalStateException(ex);
}
return po;
}
#Override
public Map<String, ? extends Object> convertToEntityAttribute(PGobject dbData) {
if (dbData == null || dbData.getValue() == null) {
return null;
}
try {
return MAPPER.readValue(dbData.getValue(), new TypeReference<Map<String, Object>>() {
});
} catch (IOException ex) {
LOGGER.error("Cannot convert JsonObject to PGobject.");
return null;
}
}
}
Usage example, for an entity named Customer.
#Entity
#Table(schema = "web", name = "customer")
public class Customer implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
#Convert(converter = PgJsonbToMapConverter.class)
private Map<String, String> info;
public Customer() {
this.id = null;
this.info = null;
}
// Getters and setter omitted.
If you're using R2DBC you can use dependency io.r2dbc:r2dbc-postgresql, and use type io.r2dbc.postgresql.codec.Json in your member attributes of an entity class, e.g.:
public class Rule {
#Id
private String client_id;
private String username;
private String password;
private Json publish_acl;
private Json subscribe_acl;
}

JPA Audit Null CreatedBy

I am using spring MVC with mongodb and using auditing entities to save the creation and last edition user and time. So my AudtingEntity, from which all objects inherit from, is like this:
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
import org.joda.time.DateTime;
import org.springframework.data.annotation.CreatedBy;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedBy;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.mongodb.core.mapping.Field;
/**
* Base abstract class for entities which will hold definitions for created, last modified by and created,
* last modified by date.
*/
public abstract class AbstractAuditingEntity {
#CreatedBy
#Field("created_by")
#JsonIgnore
private String createdBy;
#CreatedDate
#Field("created_date")
#JsonIgnore
private DateTime createdDate = DateTime.now();
#LastModifiedBy
#Field("last_modified_by")
#JsonProperty(access = JsonProperty.Access.READ_ONLY)
private String lastModifiedBy;
#LastModifiedDate
#Field("last_modified_date")
#JsonProperty(access = JsonProperty.Access.READ_ONLY)
private DateTime lastModifiedDate = DateTime.now();
public String getCreatedBy() {
return createdBy;
}
public void setCreatedBy(String createdBy) {
this.createdBy = createdBy;
}
public DateTime getCreatedDate() {
return createdDate;
}
public void setCreatedDate(DateTime createdDate) {
this.createdDate = createdDate;
}
public String getLastModifiedBy() {
return lastModifiedBy;
}
public void setLastModifiedBy(String lastModifiedBy) {
this.lastModifiedBy = lastModifiedBy;
}
public DateTime getLastModifiedDate() {
return lastModifiedDate;
}
public void setLastModifiedDate(DateTime lastModifiedDate) {
this.lastModifiedDate = lastModifiedDate;
}
}
It works perfectly when I first save an object, but when I edit it the createdBy becomes null and the createdDate is updated also with the updating date. It happens because I'm ignoring these properties on front end and they are null when I save. A possible solution is to find the object in DB before save, then copy the properties to the updated object and save. However I don't like this solution because is needed to write this specific code for any object. I think probably there is a smarter way to solve this, configuring somehow to never update these fields in database after creation.
I tried to use the following property on these 2 fields, but didn't work:
#Column(updatable = false)

Netbeans Java Enterprise Web Application: cannot find symbol class Table

so I followed this tutorial video (source)
to create a Java Enterprise Web Application and have ran into a problem. As this is pretty much the first time working with stuff like this for me I am clueless regarding how to solve this.
When trying to run the project I get an error saying "The module has not been deployed. See the server log for details." After some time trying to find out what was wrong I figured it is most likely the error I have in my "Student.java"-class. It says "cannot find symbol classTable. Create Subclass Create Test Class". What may be the problem here? I'm not sure what code would be relevant as I am a complete beginner to this, but all my files are exact copies of the source code-wise. Here is Student.java:
package com.joseph.model;
import java.io.Serializable;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.Table;
/**
*
* #author Joseph
*/
#Entity
#Table
#NamedQueries({#NamedQuery(name="Student.getAll",query="SELECT e FROM Student e")})
public class Student implements Serializable{
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
#Column
private int studentId;
#Column
private String firstname;
#Column
private String lastname;
#Column
private int yearLevel;
public int getStudentId() {
return studentId;
}
public void setStudentId(int studentId) {
this.studentId = studentId;
}
public String getFirstname() {
return firstname;
}
public void setFirstname(String firstname) {
this.firstname = firstname;
}
public String getLastname() {
return lastname;
}
public void setLastname(String lastname) {
this.lastname = lastname;
}
public int getYearLevel() {
return yearLevel;
}
public void setYearLevel(int yearLevel) {
this.yearLevel = yearLevel;
}
public Student(int studentId, String firstname, String lastname, int yearLevel) {
this.studentId = studentId;
this.firstname = firstname;
this.lastname = lastname;
this.yearLevel = yearLevel;
}
public Student(){}
}

JPA Transparent Indirection and Container Policies

Suppose I have the following simple Customer/Order implementation:
A record of customers defined by a Customer class.
Each customer can have multiple orders defined by an Order class.
Drawing on the explanation of Transparent Indirection from here and Container Policies from here my understanding of these concepts EclipseLink is as follows:
Transparent Indirection allows me to say
Customer customer = Customer.getCustomerById(1);
Set<Order> orders = customer.getOrders();
Two points to note are:
Indirection allows lazy loading of attributes so a customer's orders are only fetched from the DB on line 2, not line 1.
I can treat the orders of a customer as a Set (or Collection or List or Map) of objects of type Order.
The Container Policy tells to EclipseLink which actual class should be used for the Set and it should therefore implement Set in the example above.
That is my understanding of Transparent Indirection and Container Policies in EclipseLink.
I am seeing the following error when I try to access the database:
Exception [EclipseLink-148] (Eclipse Persistence Services - 2.3.0.v20110604-r9504): org.eclipse.persistence.exceptions.DescriptorException
Exception Description: The container policy [CollectionContainerPolicy(class org.eclipse.persistence.indirection.IndirectSet)] is not compatible with transparent indirection.
Mapping: org.eclipse.persistence.mappings.OneToManyMapping[orders]
Descriptor: RelationalDescriptor(my.model.Customer --> [DatabaseTable(Customer)])
I'm sure I have an error in my code somewhere which I am trying to debug but I didn't specify the CollectionContainerPolicy mentioned in the error so I assume org.eclipse.persistence.indirection.IndirectSet is the default. But if I'm using the default policy then I'm not sure what the cause of this error may be or which policy I should be using.
For now, I'd just like to know if my understanding of Transparent Indirection and Container Policies as I mentioned above is correct.
If it is correct I'm probably missing something relatively small in my code (an invocation or configuration option etc.) but if I'm not understanding the concepts then clearly I need to do more research first.
Customer model
package my.model;
import java.io.Serializable;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.OneToMany;
import javax.persistence.Table;
/**
* The persistent class for the customer database table.
*
*/
#Entity
#Table(name=Customer.TBL_NAME)
#NamedQueries({
#NamedQuery(name=Customer.QRY_BY_NAME,query="Select object(a) from Customer a where " +
"a.name=:" + Customer.PRM_NAME),
#NamedQuery(name=Customer.QRY_ALL, query="select object(a) from Customer a")
})
public class Customer implements Serializable {
private static final long serialVersionUID = 1L;
// Table specific onstants
public static final String TBL_NAME = "Customer";
public static final String QRY_BY_NAME = TBL_NAME + ".byName";
public static final String QRY_ALL = TBL_NAME + ".all";
public static final String PRM_NAME = "name";
private int id;
private String name;
private Set<Order> orders;
public Customer() {
}
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
public int getId() {
return this.id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
//bi-directional many-to-one association to Order
#OneToMany(mappedBy="customer")
public Set<Order> getOrders() {
return this.orders;
}
public void setOrders(Set<Order> orders) {
this.orders = orders;
}
}
Order model
package my.model;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.ManyToOne;
import javax.persistence.Table;
/**
* The persistent class for the order database table.
*
*/
#Entity
#Table(name=Order.TBL_NAME)
public class Order implements Serializable {
private static final long serialVersionUID = 1L;
// Table constants
public static final String TBL_NAME = "Order";
private int id;
private Customer customer;
public Order() {
}
#Id
public int getId() {
return this.id;
}
public void setId(int id) {
this.id = id;
}
//bi-directional many-to-one association to Customer
#ManyToOne
public Customer getCustomer() {
return this.customer;
}
public void setCustomer(Customer customer) {
this.customer = customer;
}
}
Your understanding is correct, but shouldn't be needed as this isn't something you need to configure when using JPA. EclipseLink will determine the collection policy and implementation to use based on the type of the property and the lazy/eager setting, and it seems to be doing so correctly. The exception is thrown in error, probably due to classloader issues so that the classloader used for init isn't the one used to validate against, but I don't know how that could happen. You will need to look at the environment this is running in as the exception itself is just a symptom

how to make #id fields editable in forge crud?

I want to generate a crud for an entity with an editable primary key but forge doesnt generate the #id field this is my entity note that the id field is an string it is not a an auto increment id field.
package com.samples.model;
// Generated 14/01/2013 11:23:23 AM by Hibernate Tools 3.4.0.CR1
import java.util.Date;
import java.util.HashSet;
import java.util.Set;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
/**
* Pais generated by hbm2java
*/
#Entity
#Table(name = "pais", schema = "public")
public class Pais implements java.io.Serializable
{
/**
*
*/
private static final long serialVersionUID = -8369022633869576848L;
private String paisId;
private Date fechaRegistro;
private String descripcion;
private short estatus;
private Set<Estado> estados = new HashSet<Estado>(0);
public Pais()
{
}
public Pais(String paisId, Date fechaRegistro, short estatus)
{
this.paisId = paisId;
this.fechaRegistro = fechaRegistro;
this.estatus = estatus;
}
public Pais(String paisId, Date fechaRegistro, String descripcion, short estatus, Set<Estado> estados)
{
this.paisId = paisId;
this.fechaRegistro = fechaRegistro;
this.descripcion = descripcion;
this.estatus = estatus;
this.estados = estados;
}
#Id
#Column(name = "pais_id", unique = true, nullable = false, length = 5)
public String getPaisId()
{
return this.paisId;
}
public void setPaisId(String paisId)
{
this.paisId = paisId;
}
#Temporal(TemporalType.TIMESTAMP)
#Column(name = "fecha_registro", nullable = false, length = 29)
public Date getFechaRegistro()
{
return this.fechaRegistro;
}
public void setFechaRegistro(Date fechaRegistro)
{
this.fechaRegistro = fechaRegistro;
}
#Column(name = "descripcion", length = 30)
public String getDescripcion()
{
return this.descripcion;
}
public void setDescripcion(String descripcion)
{
this.descripcion = descripcion;
}
#Column(name = "estatus", nullable = false)
public short getEstatus()
{
return this.estatus;
}
public void setEstatus(short estatus)
{
this.estatus = estatus;
}
#OneToMany(fetch = FetchType.LAZY, mappedBy = "pais")
public Set<Estado> getEstados()
{
return this.estados;
}
public void setEstados(Set<Estado> estados)
{
this.estados = estados;
}
}
JBoss Forge uses Metawidget for its UI generation. So to accomplish this you must modify Forge's metawidget.xml configuration file.
Forge actually has 3 Metawidget configuration files: metawidget-entity.xml for viewing/editing screens, metawidget-search.xml for search filters, and metawidget-qbe.xml for generating Java code.
For your purposes, you need to modify metawidget-entity.xml. Specifically you must find the JpaInspector entry...
<jpaInspector xmlns="java:org.metawidget.inspector.jpa" config="JpaInspectorConfig">
...and add a child node...
<hideIds><boolean>false</boolean></hideIds>
That bit should be straightforward. However actually getting your modified metawidget-entity.xml back into Forge is clumsy. At the moment the easiest way is probably to unzip and modify the forge-scaffold-faces.jar itself. Hopefully this will be made cleaner in a future Forge release.