Spring JPA MYSQL Character Encoding Case Sensitivity - spring

I am using a JPA repository and specified the findByEmail(email) method within. When I call this method it works fine, only problem is I want case sensitivity on it. So, "noemail#noemail" would not match "nOeMaIl#NoEmAiL.cOm". This is not happening as the `findByEmail(email)' method is returning this as true.
When I look at the MYSQL database table, I see the character set as "utf8md4" for each of the fields listed as a string in my entity class. through some reading it looks like case sensitivity works with UTF-8 variations such as "utf8md4_0900_as_cs", not sure how to change this if that is the case.
How can I change things to make sure the findByEmail(email) method returns false when case does not match?
#Entity
#Table(name="users", uniqueConstraints={#UniqueConstraint(columnNames="username"})})
#Data
public class user{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#NotBlank(message = "Can't be blank")
#Size(max = 250, message = "Can't exceed 60 characters")
#Email
private String email;
public user() {}
public user(String email) {
this.email = email;
}
}
#Repository
public interface UserRepository extends JpaRepository<user, Long> {
List<user> findByEmail(String email);
}
#PostMapping("/v1/endpoint")
public ResponseEntity<?> forgotusername(#RequestBody String email){
List<user> users = userRepository.findByEmail(email);
return new ResponseEntity<>(users, HttpStatus.OK);
}
spring.datasource.url=jdbc:mysql://${MYSQL_HOST:AWS?useUnicode=true&characterEncoding=UTF-8
spring.datasource.username=username
spring.datasource.password=password

If you need case sensitive collation for utf8 fields, you should use utf8_bin. You can extend the MySQL dialect to achieve this. Take a look here.

Related

How to solve Spring Boot findBy method with underscore variable

When I run the below project, I receive the following error. How can I fix it?
Caused by: java.lang.IllegalArgumentException: Failed to create query for method public abstract com.example.pharmanic.model.Rdhs_Hospital_Current_Stock com.example.pharmanic.repositories.Rdhs_Hospital_Current_StockRepository.findBysr_no(java.lang.String)! No property sr found for type Rdhs_Hospital_Current_Stock!
This is my Rdhs_Hospital_Current_Stock model class.
#Entity
#Data
#Table(name = "Rdhs_Hospital_Current_Stock")
public class Rdhs_Hospital_Current_Stock {
#Id
private Long batchId;
private int quantity;
private String expiredate;
#ManyToOne
private Hospital_By_Rdhs hospital_by_rdhs;
#ManyToOne
#JoinColumn(name = "sr_no", nullable = false, referencedColumnName = "sr_no")
private Medicine medicine;
}
sr_no is the foreign key of the Medicine table.
This is my Medicine entity:
#Data
#Entity
public class Medicine {
private #Id String sr_no;
private String name;
private String side_effect;
private String description;
public Medicine() {
}
public Medicine(String sr_no, String name, String side_effect, String description) {
this.sr_no = sr_no;
this.name = name;
this.side_effect = side_effect;
this.description = description;
}
}
When I use sr_no with my findBy() function:
#GetMapping("/rhstock/{id}")
ResponseEntity<?> getMedicine(#PathVariable String id){
Optional<Rdhs_Hospital_Current_Stock> rdhs_hospital_current_stock = Optional.ofNullable(rdhs_hospital_current_stockRepository.findBysr_no(id));
return rdhs_hospital_current_stock.map(response->ResponseEntity.ok().body(response)).orElse(new ResponseEntity<>(HttpStatus.NOT_FOUND));
}
This is my repository:
public interface Rdhs_Hospital_Current_StockRepository extends JpaRepository<Rdhs_Hospital_Current_Stock,Long> {
Rdhs_Hospital_Current_Stock findBysr_no(String id);
}
Inspired from: Spring-Data-Jpa Repository - Underscore on Entity Column Name
The underscore _ is a reserved character in Spring Data query derivation (see the reference docs for details) to potentially allow manual property path description.
Stick to the Java naming conventions of using camel-case for member variable names and everything will work as expected.
Change sr_no to srNo.
Update repository function
Rdhs_Hospital_Current_Stock findBymedicine_srNo(String id);
I solve this error. I change in Reposity Interface & Controller class like as below
Repository Interface -:
#Query(value="select * from Rdhs_Hospital_Current_Stock h where h.sr_no = :sr_no",nativeQuery=true)
List<Rdhs_Hospital_Current_Stock> findBySr_no(#Param("sr_no")String sr_no);
Controller class -:
#RequestMapping(value = "/rhstocksr/{sr_no}", method = RequestMethod.GET)
List<Rdhs_Hospital_Current_Stock> getBatchByMedicine(#PathVariable("sr_no") String sr_no) {
return rdhs_hospital_current_stockRepository.findBySr_no(sr_no);
}
To keep using a derived query you may change sr_no to srNo.
You could solve this by starting using a native query, but my advice is to use derived querys everytime as it is possible to use it.

How to change method names from jpa-repository?

There is application on spring+jpa(repositories)
Entity:
#Entity
#Getter
#Setter
#NoArgsConstructor
public class User{
private int id;
private String name;
private String surname;
private int age;
}
So, my repository looks like this:
#Repository
public interface UserRepository extends CrudRepository<User, Integer>{
public List<User> findUsersByNameAndAge(String name, int age);
public List<User> findUsersByNameAndSurname(String name, String surname);
}
In my real prod code, there too much params, so this method-name is too long and not comfortable.
Is there a way to make from long method names comfortable options, like just find?
Use default Java 8 feature for wrapping :
for example :
interface UserRepository extends JpaRepository<User, Long> {
// don't use that crazy long method! use getByEmail instead
User findFirstByEmailContainsIgnoreCaseAndField1NotNullAndField2NotNullAndField3NotNullAndField4NotNullAndField5NotNullAndField6NotNull(final String email);
default User getByEmail(final String email) {
return findFirstByEmailContainsIgnoreCaseAndField1NotNullAndField2NotNullAndField3NotNullAndField4NotNullAndField5NotNullAndField6NotNull(email);
}
}
You can find complete reference here : https://github.com/daggerok/spring-data-examples/blob/master/shadov/src/main/java/daggerok/ShadovApplication.java
Or else You can specify any name for a method and add an annotation #Query with parameter value which holds desired query to database like this:
#Query(value="select u from User u where u.deleted=false and u.email=:email")
User findOneByEmail(#Param("email")String email);
or, with native sql query:
#Query(value="SELECT * FROM users WHERE deleted=false AND email=?1", nativeQuery=true)
User findOneByEmail(String email);
You can also use names that follow the naming convention for queries since #Query annotation will take precedence over query from method name.

How to fetch only selected attributes of an entity using Spring JPA?

I'm using Spring Boot (1.3.3.RELEASE) and Hibernate JPA in my project. My entity looks like this:
#Data
#NoArgsConstructor
#Entity
#Table(name = "rule")
public class RuleVO {
#Id
#GeneratedValue
private Long id;
#Column(name = "name", length = 128, nullable = false, unique = true)
private String name;
#Column(name = "tag", length = 256)
private String tag;
#OneToMany(mappedBy = "rule", cascade = CascadeType.ALL, orphanRemoval = true)
private List<RuleOutputArticleVO> outputArticles;
#OneToMany(mappedBy = "rule", cascade = CascadeType.ALL, orphanRemoval = true)
private List<RuleInputArticleVO> inputArticles;
}
My repository looks like this:
#Repository
public interface RuleRepository extends JpaRepository<RuleVO, Long> {
}
In some cases I need to fetch only id and name attributes of entity RuleVO. How can I achieve this? I found a notice it should be doable using Criteria API and Projections but how? Many thanks in advance. Vojtech
UPDATE:
As has been pointed out to me, I'm lazy and this can very well be done hence I'm updating my answer after having looked around the web for a proper one.
Here's an example of how to get only the id's and only the names:
#Repository
public interface RuleRepository extends JpaRepository<RuleVO, Long> {
#Query("SELECT r.id FROM RuleVo r where r.name = :name")
List<Long> findIdByName(#Param("name") String name);
#Query("SELECT r.name FROM RuleVo r where r.id = :id")
String findNameById(#Param("id") Long id);
}
Hopefully this update proves helpful
Old Answer:
Only retrieving the specific attributes name/id is not possible as this is not how spring was designed or any SQL database for that matter as you always select a row which is an entity.
What you CAN do is query over the variables in the entity, for instance:
#Repository
public interface RuleRepository extends JpaRepository<RuleVO, Long> {
public RuleVo findOneByName(String name);
public RuleVo findOneByNameOrId(String name, Long id);
public List<RuleVo> findAllByName(String name);
// etc, depending on what you want
}
You can modify these however you want w.r.t. your needs. You can call these methods directly via the autowired repository
See http://docs.spring.io/spring-data/jpa/docs/current/reference/html/ Section 5.3 for more options and examples
interface IdOnly{
String getId();
}
#Repository
public interface RuleRepository extends JpaRepository<RuleVO, Long> {
public List<IdOnly> findAllByName(String name);
}
I notice that this is a very old post, but if someone is still looking for an answer, try this. It worked for me.
You can also define custom constructor to fetch specific columns using JPQL.
Example:
Replace {javaPackagePath} with complete java package path of the class
use as a constructor in JPQL.
public class RuleVO {
public RuleVO(Long id, String name) {
this.id = id;
this.name = name;
}
}
#Repository
public interface RuleRepository extends JpaRepository<RuleVO, Long> {
#Query("SELECT new {javaPackagePath}.RuleVO(r.id, r.name) FROM RuleVo r where r.name = :name")
List<RuleVO> findIdByName(#Param("name") String name);
}
Yes, you can achieve it with projections. You have many ways to apply them:
If you could upgrade to Spring Data Hopper, it provides an easy to use support for projections. See how to use them in the reference documentation.
Otherwise, first of all create a DTO with the attributes you want to load, something like:
package org.example;
public class RuleProjection {
private final Long id;
private final String name;
public RuleProjection(Long id, String name) {
this.id = id;
this.name = name;
}
public Long getId() {
return id;
}
public String getName() {
return name;
}
}
Of course, you could use Lombok annotations also.
Then, you can use in the JPQL queries like this:
select new org.example.RuleProjection(rule.id, rule.name) from RuleVO rule order by rule.name
Another option, if you want to avoid using DTO class names in your queries, is to implement your own query method using QueryDSL. With Spring Data JPA, you have to:
Create a new interface with the new method. Ex:
public interface RuleRepositoryCustom {
public List<RuleProjection> findAllWithProjection();
}
Change your repository to extend the new interface. Ex:
public interface RuleRepository extends JpaRepository<RuleVO, Long>, RuleRepositoryCustom {
...
Create an implementation of the Custom repository using the Spring Data JPA QueryDSL support. You have to previously generate the Q clases of QueryDSL, using its Maven plugin. Ex:
public class RuleRepositoryImpl {
public List<RuleProjection> findAllWithProjection() {
QRuleVO rule = QRuleVO.ruleVO;
JPQLQuery query = getQueryFrom(rule);
query.orderBy(rule.name.asc());
return query.list(ConstructorExpression.create(RuleProjection.class, rule.id, rule.name));
}
}
You can do it by using #Query annotation(HQL).
Please refer to the Spring docs below:
http://docs.spring.io/spring-data/jpa/docs/current/reference/html/#jpa.query-methods.at-query
(search for #Query in spring document)

Spring Security UserDetails and username

In my current implementation I have a User entity that implements org.springframework.security.core.userdetails.UserDetails interface.
#Entity
#Table(name = "users")
public class User extends BaseEntity implements UserDetails {
private static final long serialVersionUID = 8884184875433252086L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Integer id;
private String username;
private String password;
....
During the OAuth2 authorization I manually create a new User object, populate its fields and store in my database.
According to the UserDetails contract - UserDetails.getUsername() method can't return null but I have no values retrieved from Social Networks that can be used as username.
What value in this case should be returned in the User.getUsername() method ?
Is it okay to return something like this ?
#Override
public String getUsername() {
return String.valueOf(id);
}
If you need to save this entity before you have a valid value for the name then I think that's a problem with the design. Having said that User.getUsername() is mainly used for display purposes, so I doubt it matters what the actual value is, as long as it can be matched to something in an authentication.

why I can't use string as id

I am trying to create a user model with a CrudRepository:
#Entity
public class User {
#Id
#GeneratedValue
private String username;
private String password;
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
}
public interface UserRepository extends CrudRepository<User, String> {
}
However I got an 500 error every time I call findOne():
#Controller
public class UserController {
#Autowired
private UserRepository users;
#Override
#RequestMapping(value="/register", method=RequestMethod.POST)
public #ResponseBody User register(#RequestBody User userToRegister) {
String username = userToRegister.getUsername();
User user = users.findOne(id);
if (user != null) {
return null;
}
User registeredUser = users.save(userToRegister);
return registeredUser;
}
}
However if I just switch to an long type id instead of username itself then everything works. I think it's common to use string as id. So how to make this work?
I use the embedded hsql database. I didn't wrote any sql code.
The problem is that String username; is annotated with both #Id and #GeneratedValue. #Id means that is should be a primary key, fine it can be a String. But #GeneratedValue means that you want the system to automatically generate a new key when you create a new record. That's easy when the primary key is integer, all databases have a notion of sequence (even if the syntax is not always the same). But if you want String automatically generated keys, you will have do define your own custom generator.
Or if you have no reason for the #GeneratedValue annotation, simply remove it as suggested by Bohuslav Burghardt
Use column annotation like below by putting nullable False.
#Id
#GeneratedValue
#Column(name = "username", nullable = false)
private String username;

Resources