Having a hibernate mapping a legacy database I want to use EnumTypes to map certain columns that contain string constants with whitespace to some Enum class.
The mapping:
#Entity
#Table(name = "OPERATOR")
public class Operator {
#Id
#Column(name = "ID")
private Long id;
...
#Enumerated(EnumType.STRING)
#Column(name = "STATUS")
private Status status;
...
}
public enum Status {
OPERATOR_CREATED("Operator created"),
ACTIVE("Active"),
END_DATED("End dated");
private String name;
Status(String status) {
name = status;
}
}
As you can see we can't us the database values straight as the enum names, as there are white spaces in them.
I was wondering if it is possible to use enums for this?
Look at GenericEnumUserType described at hibernate.org (Under "Flexible solution")
Modify Status as follows:
public enum Status
{
OPERATOR_CREATED("Operator created"),
ACTIVE("Active"),
END_DATED("End dated");
private String name;
Status(String status)
{
name = status;
}
public String toString()
{
return name;
}
public Status fromString( String value )
{
if ( "Operator created".equals( value )
{
return OPERATOR_CREATED;
}
//etc
}
}
Now use the #Type annotation on your entity.
#Entity
#Table(name = "OPERATOR")
public class Operator {
#Id
#Column(name = "ID")
private Long id;
...
#Column(name = "STATUS", columnDefinition = "VARCHAR(31)", nullable = false )
#Type( type = "my.package.GenericEnumUserType",
parameters = {
#Parameter( name = "enumClass", value = "my.package.Status" ),
#Parameter( name = "identifierMethod", value = "toString" ),
#Parameter( name = "valueOfMethod", value = "fromString" ) } )
private Status status;
...
}
I had the same exact situation, try using replace function, something like this:
#ColumnTransformer(read = "replace(status::varchar, ' ', '')", write = "replace(?, 'End', 'End ')::status")
You have underscores so need to modify it a bit.
regexp_replace can also be handy if there are several enum values with spaces.
Note, I am using postgresql 9.6
Related
I'm writing a program that changes a member's password, I fetched the user by id from the database when I test the endpoint on postman it returns 200 OK, but fails to update the password in the database to the new password, What is the right logic to use for this task? my code is below.
Member
#Getter
#Setter
#AllArgsConstructor
#NoArgsConstructor
#Entity
#Table(name ="member",
indexes = {
#Index(
columnList = "email_address",
name = "email_address_idx",
unique = true
),
},
uniqueConstraints = {
#UniqueConstraint(
columnNames = {"email_address", "phone_number"},
name = "email_address_phone_number_uq"
)
}
)
public class Member {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(name = "first_name", nullable = false)
private String firstName;
#Column(name = "last_name", nullable = false)
private String lastName;
#ManyToOne(fetch = FetchType.EAGER, optional = false)
#JoinColumn(name = "nationality_id")
private Country nationality;
#ManyToOne(fetch = FetchType.EAGER, optional = false)
#JoinColumn(name = "country_of_residence_id")
private Country countryOfResidence;
#Temporal(TemporalType.DATE)
#Column(name ="date_of_birth")
private Date dateOfBirth = new Date();
#Column(name ="current_job_title")
private String currentJobTitle;
#Column(name = "email_address", nullable = false)
private String emailAddress;
#Column(name = "username")
private String username;
#Column(name ="phone_number")
private String phoneNumber;
#Column(name ="city")
private String city;
#Column(name ="state")
private String state;
#Column(name ="password", nullable = false)
private String password;
}
PasswordDto
#Data
public class ChangePasswordDto {
private String password;
private String oldPassword;
private String newPassword;
private String reNewPassword;
PasswordService
#Slf4j
#Service
public class ChangePasswordServiceImpl implements ChangePasswordService {
#Autowired
private ModelMapper modelMapper;
#Autowired
private PasswordEncoder passwordEncoder;
private final PasswordJpaRepository jpaRepository;
public ChangePasswordServiceImpl(PasswordJpaRepository jpaRepository) {
this.jpaRepository = jpaRepository;
}
#Override
#Transactional
public Member changePassword(Long id, ChangePasswordDto password) {
final Member member = jpaRepository.findById(id);
Member getPassword = new Member();
getPassword = modelMapper.map(id, Member.class);
Member updatedPassword = new Member();
if (member.getPassword().equals(checkIfValidOldPassword(member, password.getOldPassword()))){
if (password.getNewPassword().equals(password.getReNewPassword())) {
updatedPassword = changPassword(member, password.getNewPassword());
}
}else{
return null;
}
return updatedPassword;
}
#Override
#Transactional
public boolean checkIfValidOldPassword(Member member, String oldPassword) {
return matches(oldPassword, member.getPassword());
}
#Override
#Transactional
public Member changPassword(Member member, String password) {
member.setPassword(password);
jpaRepository.save(member);
return member;
}
}
PasswordController
#RestController
#RequestMapping(
value = "password",
produces = { MediaType.APPLICATION_JSON_VALUE }
)
public class ChangePasswordController {
private ChangePasswordService service;
public ChangePasswordController(ChangePasswordService passwordService) {
this.service = passwordService;
}
#PostMapping("/change-password/{id}")
public Member changePassword(#Validated #RequestBody ChangePasswordDto password, #PathVariable(name = "id") Long id){
return service.changePassword(id, password);
}
}
Troubleshooting and Debugging
In the future, it would be helpful for you to post the request as a cURL command as well as the Catalina logs.
Your bug is in the following statement
if (member.getPassword().equals(checkIfValidOldPassword(member, password.getOldPassword()))){
// The above expression is always evaluating false
}
The member.getPassword() accessory method returns a String. However checkIfValidOldPassword method returns a boolean. Let's refactor the code for demonstration.
String pwd = member.getPassword();
String opwd = password.getOldPassword();
boolean isValud = checkIfValidOldPassword(member, opwd);
assert pwd.equals(isValid);
You are attempting to evaluate the equality of a String and a primitive boolean ( autoboxed to the Boolean wrapper class object ). Likely this statement always evaluates false thus you are returning null and not invoking the code that actually makes the update.
Autoboxing Explained
The reason this did not throw a compile time exception is due to a feature known as Autoboxing. Autoboxing is the automatic conversion that the Java compiler makes between the primitive types and their corresponding object wrapper classes.
In your example, the equals method has a single parameter of type Object. So although you passed a primitive boolean as the first parameter in the equals method, the Java compiler converted it to an Object of type Boolean. Because Boolean is an object, and all objects inherit from Object, no exception is thrown.
Most likely you are comparing the response of ‘toString’ method on your Boolean object which returns the string “true” when the primitive boolean value corresponds with true and “false” otherwise.
Security Concerns
Please be extremely careful when you are attempting to roll your own authentication or authorization features. For the most part, a password should be salted and encrypted before storing the information at-rest. Therefore, you should only ever be able to compare one salted/encrypted string with another salted/encrypted string
I'm trying to create findBy JpaRepo it's about returning only the data where isDeleted attribute is false.
this is my Service :
public List<Customer> getAllCustomers() {
List<Customer> customers = cutomerRepository.findByIsDeletedFalse();
return customers;
}
and this is my Controller :
#GetMapping("/viewList")
#CrossOrigin("http://localhost:4200/")
public ResponseEntity<List<Customer>> getAllCustomers() {
List<Customer> customers = new ArrayList<>();
customers = customerService.getAllCustomers();
if (customers.isEmpty()) {
LOGGER.error("no content ");
return new ResponseEntity<>(HttpStatus.NO_CONTENT);
}
LOGGER.info("calling list of customers");
return new ResponseEntity<>(customers, HttpStatus.OK);
}
and this is customer model :
public class Customer {
#Id
#GeneratedValue(strategy= GenerationType.IDENTITY)
private int id;
#Column(name = "serial_number")
private long serialNumber;
#Column(name = "first_name")
private String firstName;
#Column(name = "last_name")
private String lastName;
#Column(name = "email")
private String email;
#Column(name = "mobile_number")
private String mobileNumber;
#Column(name = "is_deleted")
private boolean isDeleted;
}
but when I run it in postman it's not working and return an error :
Caused by: org.postgresql.util.PSQLException: ERROR: operator does not
exist: boolean = integer Hint: No operator matches the given name
and argument types. You might need to add explicit type casts.
Position: 315
How could I solve this issue?
Looks like the name for your query isn't created right.
However, in this case, the usage of #Query will be much clearer.
Code snippet:
public interface CustomerRepo extends JpaRepository<Customer, Integer> {
List<Customer> findAllByIsDeletedIsFalse();
#Query("from Customer c where c.isDeleted=false")
List<Customer> getAllCustomers();
}
Iinstead of:
cutomerRepository.findByIsDeletedFalse()
You missed one more Is at the name of the method.
Update your Domain:
public class Customer implements Serializable {
private final static long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy= GenerationType.IDENTITY)
private Integer id;
#Column(name = "serial_number")
private Long serialNumber;
// ...
#Column(name = "is_deleted")
private Boolean isDeleted;
}
JPA fields should be Objects instead of primitives. And entity class should implement Serializable as well.
If the exception will be the same you could try to update #Query:
#Query("from Customer c where c.isDeleted=0")
If pure SQL works for your DB you could use native query:
#Query(
value = "select * from Customer where is_deleted = false",
nativeQuery = true)
List<Customer> getAllCustomers();
It's not working because it doesn't follow the naming conventions for a boolean field. Usually in Java the primitive booleans are named without is prefix and the getter would be using this is prefix.
So in your case your entity class should look like that:
public class Customer {
// ...
#Column(name = "is_deleted")
private boolean deleted;
public boolean isDeleted() {
return deleted;
}
public void setDeleted(boolean deleted) {
this.deleted = deleted;
}
}
Also the naming of the spring repository method should be:
List<Customer> findAllByDeletedIsFalse();
In case you want to use a Boolean reference type you can name your field isDeleted, but then the class would look like that:
public class Customer {
// ...
#Column(name = "is_deleted")
private Boolean isDeleted;
public Boolean getIsDeleted() {
return isDeleted;
}
public void setIsDeleted(Boolean isDeleted) {
this.isDeleted = isDeleted;
}
}
and the repository method:
List<Customer> findAllByIsDeletedIsFalse();
Boolean Java maps a bit datatype column. You are probably using int as datatype in your database.
I have service which is getting values from api and mapping it by model mapper to the unified entity.
The problem is that UUID is not working. I am getting null instead of any string id (I had to change name of if for "uniqueIdentifier" becouse objects from api had "id" field).
My entity:
#Entity
#Getter
#Setter
#Table(name = "unifiedOffers")
#AllArgsConstructor
#NoArgsConstructor
public class UnifiedOfferEntity {
#Id
#GeneratedValue(generator = "system-uuid")
#GenericGenerator(name = "system-uuid", strategy = "uuid")
#Column(name = "id", updatable = false, nullable = false)
private String uniqueIdentifier;
private String companyName;
private String city;
private String street;
private String title;
private LocalDateTime posted;
private String url;
}
Endpoint for tests:
#GetMapping("/test")
public String getOffers() throws ExecutionException, InterruptedException {
List<UnifiedOfferEntity> result = jobFinderService.getAllOffers();
for (UnifiedOfferEntity unifiedOfferEntity : result) {
System.out.println(unifiedOfferEntity.getUniqueIdentifier() + " " + unifiedOfferEntity.getTitle() + " " + unifiedOfferEntity.getUrl());
}
unifedOfferRepository.saveAll(result);
return String.valueOf(result.size());
}
In that foreach I am getting values like for example:
null JavaDeveloper anyLinkUrl
so only UUID is not generating ids.
have you defined the underline class to implement uuid generator?
in a similar case I used the follwing code that accepts a parameter "rangeName" you can remove
#GeneratedValue(generator="intRange")
#GenericGenerator(name="intRange",
strategy = "imp.framework.jerpBridge.IntRangeGenerator" ,
parameters = {
#Parameter( name = "rangeName", value="DOCMA01")
})
public String nrDocumento;
where
public class IntRangeGenerator implements IdentifierGenerator, Configurable {
public IntRangeGenerator(String rangeName) {
this.rangeName = rangeName;
}
#Override
public Serializable generate(SharedSessionContractImplementor session, Object object) throws HibernateException {
return "generated ID";
}
There are two entities and first entity is referred into second entity.
Entity 1:
#Indexed
public abstract class Yesh implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#Basic(optional = false)
#Column(name = "ID")
private Long id;
#Fields({ #Field(index = Index.YES, store = Store.NO), #Field(name = "YeshName_for_sort", index = Index.YES, analyzer = #Analyzer(definition = "customanalyzer")) })
#Column(name = "NAME", length = 100)
private String name;
public Yesh () {
}
public Yesh (Long id) {
this.id = id;
}
public Yesh (Long id) {
this.id = id;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
#Override
public String toString() {
return "com.Prac.Yesh[ id=" + id + " ]";
}
}
Entity 2:
public class Kash implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#Basic(optional = false)
#Column(name = "ID")
private Long id;
#IndexedEmbedded
#ManyToOne
Yesh yes; //Contain reference to first entity
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public Yesh getYes() {
return yes;
}
public void setId(Yesh yes) {
this.yes = yes;
}
}
There is no annotation in Entity 2 on reference Yesh; Entity 1 have field annotated with name "YeshName_for_sort". But when i try to access above field as given in following example:
Main Class:
FullTextEntityManager ftem = Search.getFullTextEntityManager(factory.createEntityManager());
QueryBuilder qb = ftem.getSearchFactory().buildQueryBuilder().forEntity( Kash.class ).get();
org.apache.lucene.search.Query query = qb.all().getQuery();
FullTextQuery fullTextQuery = ftem.createFullTextQuery(query, Kash.class);
//fullTextQuery.setSort(new Sort(new SortField("YeshName_for_sort", SortField.STRING, true)));
The above statement is not working and i have also tried to replace YeshName_for_sort with 'yes' reference but it is not working.
fullTextQuery.setFirstResult(0).setMaxResults(150);
int size = fullTextQuery.getResultSize();
List<Yesh> result = fullTextQuery.getResultList();
for (Yeshuser : result) {
logger.info("Yesh Name:" + user.getName());
}
Sorting does not work. I also tried to change statements like:
ftem.createFullTextQuery(query, Kash.class, Yesh.class); //added entity 1
or
fullTextQuery.setSort(new Sort(new SortField("yes.name", SortField.STRING, true))); // Added property name for Yesh yes;
but it is not working.
What annotations need to be implemented in entities or any changes in main program to access the field for sorting?
You are using #IndexedEmbedded so you need to reference the field with its full path namely yes.YeshName_for_sort (or yesh if the yes was a typo).
When you use #IndexedEmbedded, you include the nested fields in your Lucene document with a dotted notation.
Thus:
FullTextQuery fullTextQuery = ftem.createFullTextQuery(query, Kash.class);
fullTextQuery.setSort(new Sort(new SortField("yes.YeshName_for_sort", SortField.STRING, true)));
should work.
Note that your code is not really consistent because you start by searching for Kash objects then you manipulate Yesh objects. I suppose it's a copy/pasto.
I recommend you to read a bit about how the indexes are built by Hibernate Search: it will then be easier for you to understand this sort of things.
I m not sure that this is going to work for you but let s give it a try :
add the following annotation to property name in the class Yesh :
#Fields( {
#Field,
#Field(name = "name_sort", analyze = Analyze.NO, store = Store.YES)
} )
private String name;
sorting a query by field requires the field to be un-analyzed. If one wants to search by words in this property and still sort it, one need to index it twice - once analyzed and once un-analyzed.
2nd in your query apply the sort you want:
ftem.createFullTextQuery(query, Kash.class, Yesh.class);
fullTextQuery.setSort(new Sort(new SortField("name_sort", SortField.STRING, true)));
I have a enum of few status value
NEW, REVIEWD, PUBLISHED, PENDING, UPDATED, SPAM, DUPLICATE, IRRELEVANT, UNPUBLISHED
I don't want to use them as enumerated so created one entity for that. For convenient I want to keep a column in entity to initialize status from enum and convert that enumerated value to a Object of status entity. for this..
I have two entity. I want to refer a column with value from another entity.
Basically I want to initialize a object with formula.
Entities are
#Entity
#Table(name = "event_status")
public class EventStatus {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name="eventStatusId")
private Integer eventStatusId;
#Enumerated(EnumType.STRING)
#Column(unique = true,name="eventStatusType")
private EventStatusType eventStatusType;
public EventStatus() {
this(EventStatusType.NEW);
}
public EventStatus(EventStatusType eventStatusType) {
super();
this.eventStatusType = eventStatusType;
}
public Integer getEventStatusId() {
return eventStatusId;
}
public EventStatusType getEventStatusType() {
return eventStatusType;
}
public void setEventStatusId(Integer eventStatusId) {
this.eventStatusId = eventStatusId;
}
public void setEventStatusType(EventStatusType eventStatusType) {
this.eventStatusType = eventStatusType;
}
}
I have another entity in which I am referring object of this entity
#Entity
#Table(name = "event_")
#Inheritance(strategy = InheritanceType.JOINED)
public abstract class Event implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#Column(name = "id_")
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#Transient
public EventStatusType eventStatusType = EventStatusType.NEW;
#ManyToOne(fetch = FetchType.EAGER, targetEntity = EventStatus.class)
#Formula("select * from event_status where eventStatusId= 1")
private EventStatus status;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public EventStatus getStatus() {
System.out.println("Event.getStatus() " + status);
return status;
}
public void setStatus(EventStatus status) {
System.out.println("Event.setStatus()");
this.status = status;
}
}
This is not giving any exception but not initializing this value.
Is it possible to initialize this EntityStatus with value of eventStatusType in Event entity
I would like to explain that based on the documentation:
5.1.4.1.5. Formula
Sometimes, you want the Database to do some computation for you rather than in the JVM, you might also create some kind of virtual column. You can use a SQL fragment (aka formula) instead of mapping a property into a column. This kind of property is read only (its value is calculated by your formula fragment).
#Formula("obj_length * obj_height * obj_width")
public long getObjectVolume()
The SQL fragment can be as complex as you want and even include subselects.
...
5.1.7.1. Using a foreign key or an association table
...
Note
You can use a SQL fragment to simulate a physical join column using the #JoinColumnOrFormula / #JoinColumnOrformulas annotations (just like you can use a SQL fragment to simulate a property column via the #Formula annotation).
#Entity
public class Ticket implements Serializable {
#ManyToOne
#JoinColumnOrFormula(formula="(firstname + ' ' + lastname)")
public Person getOwner() {
return person;
}
...
}
Also, we should use insertable = false, updatable = false, because such mapping is not editable