Spring Boot multiple data source with SpringPhysicalNamingStrategy - spring-boot

Require Spring boot application with multiple data source configured.
Multiple data source configuration is working with separate datasource, entityManager and transactionManager.
Also hibernate naming configuration is working with single data source with below configuration.
spring.jpa.hibernate.naming.physical-strategy=org.springframework.boot.orm.jpa.hibernate.SpringPhysicalNamingStrategy
The above configuration can be provided with JPA properties to entityManager. But not able to add dynamic table name (from application.properties) in SpringPhysicalNamingStrategy.

I have created a custom physical naming strategy. It can change names with environment variables. Also, it can change column names too. Name identifier will be decided by the database catalog name. You can change selection criteria with using jdbcEnvironment. If you put any criteria option text will be the property value.
Application Properties
spring.datasource.url=jdbc:h2:mem:testdb
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=password
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
spring.jpa.show-sql=true
spring.jpa.hibernate.naming.physical-strategy=com.example.demo.DynamicPhysicalNamingStrategy
# dynamic
user.table.name=TESTDB:TestUser,TESTDB2:TestUser2
user.column.name=username
User Entity
#Table(name = "${user.table.name}")
#Entity
#Data
public class User {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private int id;
#Column(name = "${user.column.name}")
private String name;
private String email;
}
DynamicPhysicalNamingStrategy
package com.example.demo;
import org.hibernate.boot.model.naming.Identifier;
import org.hibernate.boot.model.naming.PhysicalNamingStrategy;
import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;
import java.util.Locale;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* It is copied from {#link org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl}
*/
#Component
public class DynamicPhysicalNamingStrategy implements PhysicalNamingStrategy, ApplicationContextAware {
private final Pattern VALUE_PATTERN = Pattern.compile("^\\$\\{([\\w.]+)}$");
private Environment environment;
#Override
public Identifier toPhysicalCatalogName(Identifier name, JdbcEnvironment jdbcEnvironment) {
return apply(name, jdbcEnvironment);
}
#Override
public Identifier toPhysicalSchemaName(Identifier name, JdbcEnvironment jdbcEnvironment) {
return apply(name, jdbcEnvironment);
}
#Override
public Identifier toPhysicalTableName(Identifier name, JdbcEnvironment jdbcEnvironment) {
return apply(name, jdbcEnvironment);
}
#Override
public Identifier toPhysicalSequenceName(Identifier name, JdbcEnvironment jdbcEnvironment) {
return apply(name, jdbcEnvironment);
}
#Override
public Identifier toPhysicalColumnName(Identifier name, JdbcEnvironment jdbcEnvironment) {
return apply(name, jdbcEnvironment);
}
private Identifier apply(Identifier name, JdbcEnvironment jdbcEnvironment) {
if (name == null) {
return null;
}
// Custom Implementation Start
String text = name.getText();
Matcher matcher = VALUE_PATTERN.matcher(text);
if (matcher.matches()) {
String propertyKey = matcher.group(1);
text = environment.getProperty(propertyKey);
Assert.notNull(text, "Property is not found '" + propertyKey + "'");
// extract catalog selection part
// Example:
// Current Catalog: TESTDB
// Property: TESTDB:TestUser, TESTDB2:TestUser
// Text will be TestUser
Pattern catalogPattern = Pattern.compile(jdbcEnvironment.getCurrentCatalog().getText() + ":([^,]+)");
Matcher catalogMatcher = catalogPattern.matcher(text);
if (catalogMatcher.find()) {
text = catalogMatcher.group(1);
}
// Caution: You can remove below return function, if so text will be transformed with spring advice
return getIdentifier(text, name.isQuoted(), jdbcEnvironment);
}
// Custom Implementation End
StringBuilder builder = new StringBuilder(text.replace('.', '_'));
for (int i = 1; i < builder.length() - 1; i++) {
if (isUnderscoreRequired(builder.charAt(i - 1), builder.charAt(i), builder.charAt(i + 1))) {
builder.insert(i++, '_');
}
}
return getIdentifier(builder.toString(), name.isQuoted(), jdbcEnvironment);
}
/**
* Get an identifier for the specified details. By default this method will return an
* identifier with the name adapted based on the result of
* {#link #isCaseInsensitive(JdbcEnvironment)}
*
* #param name the name of the identifier
* #param quoted if the identifier is quoted
* #param jdbcEnvironment the JDBC environment
* #return an identifier instance
*/
protected Identifier getIdentifier(String name, boolean quoted, JdbcEnvironment jdbcEnvironment) {
if (isCaseInsensitive(jdbcEnvironment)) {
name = name.toLowerCase(Locale.ROOT);
}
return new Identifier(name, quoted);
}
/**
* Specify whether the database is case sensitive.
*
* #param jdbcEnvironment the JDBC environment which can be used to determine case
* #return true if the database is case insensitive sensitivity
*/
protected boolean isCaseInsensitive(JdbcEnvironment jdbcEnvironment) {
return true;
}
private boolean isUnderscoreRequired(char before, char current, char after) {
return Character.isLowerCase(before) && Character.isUpperCase(current) && Character.isLowerCase(after);
}
#Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
environment = applicationContext.getBean(Environment.class);
}
}
Test
package com.example.demo;
import com.example.demo.entity.User;
import com.example.demo.repository.UserRepository;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
#SpringBootTest
class DemoApplicationTests {
#Autowired
private UserRepository userRepository;
#Test
void contextLoads() {
User user = new User();
user.setName("test");
user.setEmail("test#test.com");
userRepository.save(user);
userRepository.findAll();
}
}
Hibernate: call next value for hibernate_sequence
Hibernate: insert into testuser (email, username, id) values (?, ?, ?)
Hibernate: select user0_.id as id1_0_, user0_.email as email2_0_, user0_.username as username3_0_ from testuser user0_

Related

How Can we use Policy-enforcer dynamically in Java Springboot?

Saurav Chaurasia
Fri, May 7, 5:00 PM (20 hours ago)
to me
Like I am able to use the static Policy enforcer by providing the path and method and resource into the application.properties. But in realtime application we will be having N number of roles and we will be having N number of API which we will be provide in the Resource, Policies and Permissions in KeyCloak. In Future if we want some more roles to be added into the keycloak and new resources will be added with necessary permission. We wont be coming back to our springboot to change the code for resource and roles. All the permission checking should be dynamic to the spring boot how can we do that please help me out.
The static one which I am using currently is working fine for few roles. We are adding more roles into the keyCloak.
Below is the Static code.
application.properties
server.port = 8090
keycloak.realm=university
keycloak.auth-server-url=http://localhost:8080/auth
keycloak.ssl-required=external
keycloak.resource=course-management
keycloak.bearer-only=true
keycloak.credentials.secret=a5df9621-73c9-4e0e-9d7a-97e9c692a930
keycloak.securityConstraints[0].authRoles[0]=teacher
keycloak.securityConstraints[0].authRoles[1]=ta
keycloak.securityConstraints[0].authRoles[2]=student
keycloak.securityConstraints[0].authRoles[3]=parent
keycloak.securityConstraints[0].securityCollections[0].name=course managment
keycloak.securityConstraints[0].securityCollections[0].patterns[0] = /courses/get/*
#keycloak.policy-enforcer-config.lazy-load-paths=true
keycloak.policy-enforcer-config.paths[0].path=/courses/get/*
keycloak.policy-enforcer-config.paths[0].methods[0].method=GET
keycloak.policy-enforcer-config.paths[0].methods[0].scopes[0]=view
keycloak.policy-enforcer-config.paths[0].methods[1].method=DELETE
keycloak.policy-enforcer-config.paths[0].methods[1].scopes[0]=delete
Configuration.class
package com.lantana.school.course.coursemanagment.security;
`import java.util.List;
import org.keycloak.AuthorizationContext;
import org.keycloak.KeycloakSecurityContext;
import org.keycloak.representations.idm.authorization.Permission;
public class Identity {
private final KeycloakSecurityContext securityContext;
public Identity(KeycloakSecurityContext securityContext) {
this.securityContext = securityContext;
}
/**
* An example on how you can use the {#link org.keycloak.AuthorizationContext} to check for permissions granted by Keycloak for a particular user.
*
* #param name the name of the resource
* #return true if user has was granted with a permission for the given resource. Otherwise, false.
*/
public boolean hasResourcePermission(String name) {
System.out.println("Permission: "+getAuthorizationContext().hasResourcePermission(name));
return getAuthorizationContext().hasResourcePermission(name);
}
/**
* An example on how you can use {#link KeycloakSecurityContext} to obtain information about user's identity.
*
* #return the user name
*/
public String getName() {
System.out.println("UserName: "+securityContext.getIdToken().getPreferredUsername());
return securityContext.getIdToken().getPreferredUsername();
}
/**
* An example on how you can use the {#link org.keycloak.AuthorizationContext} to obtain all permissions granted for a particular user.
*
* #return
*/
public List<Permission> getPermissions() {
System.out.println("Permission 2: "+getAuthorizationContext().getPermissions());
return getAuthorizationContext().getPermissions();
}
/**
* Returns a {#link AuthorizationContext} instance holding all permissions granted for an user. The instance is build based on
* the permissions returned by Keycloak. For this particular application, we use the Entitlement API to obtain permissions for every single
* resource on the server.
*
* #return
*/
private AuthorizationContext getAuthorizationContext() {
System.out.println("getAuthorizationContext: "+ securityContext.getAuthorizationContext());
return securityContext.getAuthorizationContext();
}
}`
Controller.class
package com.lantana.school.course.coursemanagment.services;
import java.math.BigInteger;
import java.net.URI;
import java.util.ArrayList;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import org.keycloak.KeycloakSecurityContext;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.hateoas.EntityModel;
//import org.springframework.hateoas.Link;
//import org.springframework.hateoas.server.mvc.WebMvcLinkBuilder;
//import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.support.ServletUriComponentsBuilder;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.lantana.school.course.coursemanagment.security.Identity;
#RestController
public class CourseController{
#Autowired
private HttpServletRequest request;
#Autowired
private CourseService couseService;
#Autowired
private hateo hatoeslink;
List<String> rol=new ArrayList<String>();
//
// #GetMapping(value = "/courses/api")
// public String generateApi(#RequestHeader("Authorization") String token){
////// rol.clear();
////// List headers = token.;
// System.out.println("Token: "+token);
////// System.out.println("Role Controller: "+rol);
////// rol=couseService.getRole(token);
////// List<?> link=new ArrayList<>();
//////
// return new String("Role Fetched");
// }
#GetMapping(value = "/courses/get/{id}", produces = MediaType.APPLICATION_JSON_VALUE)
public EntityModel<Course> getCourse(#PathVariable("id") long id, Model model,#RequestHeader("Authorization") String token) throws JsonProcessingException {
configCommonAttributes(model);
rol=couseService.getRole(token);
System.out.println("GetRole: "+rol);
Course course = couseService.getCourse(id);
hatoeslink.hateoLink(rol, id, model, token);
return EntityModel.of(course);
}
#DeleteMapping("/courses/delete/{id}")
public EntityModel<Course> deleteStudent(#PathVariable long id) {
System.out.println("calling delete operation");
//
Course course = couseService.getCourse(id);
couseService.deleteById(id);
return EntityModel.of(course);
}
#PostMapping("/courses")
public ResponseEntity<Course> createCourse(#RequestBody Course course) {
Course savedCourse = couseService.addCourse(course);
URI location = ServletUriComponentsBuilder.fromCurrentRequest().path("/{id}")
.buildAndExpand(savedCourse.getCode()).toUri();
return ResponseEntity.created(location).build();
}
private void configCommonAttributes(Model model) {
model.addAttribute("identity", new Identity(getKeycloakSecurityContext()));
}
private KeycloakSecurityContext getKeycloakSecurityContext() {
return (KeycloakSecurityContext) request.getAttribute(KeycloakSecurityContext.class.getName());
}
}
Service.class
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
//import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import org.apache.commons.codec.binary.Base64;
import org.json.JSONArray;
import org.json.JSONObject;
import org.springframework.stereotype.Component;
#Component
public class CourseService {
public static final Map<Long, Course> courseMap = new LinkedHashMap<Long, Course>();
static {
Course cs2001 = new Course("CS2001", "Mathematical Foundations of Computing", "introduction", "term1");
Course cs2002 = new Course("CS2002", "Computer Organization and Systems", "introduction", "term1");
Course cs2003 = new Course("CS2003", "Data Management and Data Systems", "introduction", "term2");
Course cs2004 = new Course("CS2004", "Introduction to Computer Graphics and Imaging", "introduction", "term3");
Course cs2005 = new Course("CS2005", "Design and Analysis of Algorithms", "introduction", "term4");
Course cs2006 = new Course("CS2006", "Analysis of Networks", "introduction", "term4");
courseMap.put(cs2001.getId(), cs2001);
courseMap.put(cs2002.getId(), cs2002);
courseMap.put(cs2003.getId(), cs2003);
courseMap.put(cs2004.getId(), cs2004);
courseMap.put(cs2005.getId(), cs2005);
courseMap.put(cs2006.getId(), cs2006);
}
public Course getCourse(Long id) {
return courseMap.get(id);
}
public Course addCourse(Course course) {
courseMap.put(course.getId(), course);
return course;
}
public void deleteById(long id) {
courseMap.remove(id);
// return id+"Deleted Successfully";
}
public List<String> getRole(String token) {
String[] payload=token.split("\\.");
byte[] bytes = Base64.decodeBase64(payload[1]);
String decodedString = new String(bytes, StandardCharsets.UTF_8);
// System.out.println("Decoded: " + decodedString);
JSONObject jo=new JSONObject(decodedString);
JSONObject obj=jo.getJSONObject("realm_access");
JSONArray jArray=obj.getJSONArray("roles");
System.out.println(obj);
System.out.println(jArray);
List<String> role=new ArrayList();
for(int i=0;i<jArray.length();i++)
{ String ro=(String) jArray.get(i);
System.out.println(jArray.get(i));
role.add(ro);
}
// List action=new ArrayList();
// Map roles=((Map)jo.get("realm_access"));
// Iterator<Map.Entry> itr1=roles.entrySet().iterator();
// while(itr1.hasNext())
// {
// Map.Entry pair=itr1.next();
// System.out.println(pair);
// }
return role;
}
}
Model.class
package com.lantana.school.course.coursemanagment.services;
import org.springframework.hateoas.RepresentationModel;
public class Course extends RepresentationModel {
private static long nextID = 1000;
public Course(String code, String name, String modules, String enrollmentTerm) {
super();
this.id = nextID++;
this.code = code;
this.name = name;
this.modules = modules;
this.enrollmentTerm = enrollmentTerm;
}
Long id;
String code;
String name;
String modules;
String enrollmentTerm;
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getModules() {
return modules;
}
public void setModules(String modules) {
this.modules = modules;
}
public String getEnrollmentTerm() {
return enrollmentTerm;
}
public void setEnrollmentTerm(String enrollmentTerm) {
this.enrollmentTerm = enrollmentTerm;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
}
hateos Custom class
package com.lantana.school.course.coursemanagment.services;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.hateoas.EntityModel;
import org.springframework.hateoas.server.mvc.WebMvcLinkBuilder;
import org.springframework.stereotype.Component;
import org.springframework.ui.Model;
import com.fasterxml.jackson.core.JsonProcessingException;
#Component
public class hateo {
#Autowired
private CourseService couseService;
public EntityModel<Course> hateoLink(List<String> role,long id,Model model,String token)
{ Course course = couseService.getCourse(id);
course.removeLinks();
role.stream().forEach(action ->{
if(action.equalsIgnoreCase("teacher"))
{
// course.removeLinks();
course.add(WebMvcLinkBuilder.linkTo(WebMvcLinkBuilder.methodOn(CourseController.class).createCourse(course)).withRel("add"));
}
if(action.equalsIgnoreCase("student"))
{
try {
course.add(WebMvcLinkBuilder.linkTo(WebMvcLinkBuilder.methodOn(CourseController.class).getCourse(id, model,token)).withRel("view"));
} catch (JsonProcessingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if(action.equalsIgnoreCase("parent"))
{
course.add(WebMvcLinkBuilder.linkTo(WebMvcLinkBuilder.methodOn(CourseController.class).deleteStudent(id)).withRel("delete"));
}
});
return EntityModel.of(course);
}
}
If you are using Keycloak's Authorization Services, (which you are if you're using PEP), you shouldn't have to define roles in your spring boot keycloak-configuration. Notice how the roles aren't part of the policy-enforcer-config. If you just remove keycloak.securityConstraints[0].authRoles, and check for roles in your Policies over at the Keycloak server instead, you should be good.
As for the resource paths, I see that you have commented out keycloak.policy-enforcer-config.lazy-load-paths. With this together with http-method-as-scope, you shouldn't have to provide any additional configuration regarding your resources since the Keycloak-adapter will automatically grab that from annotations like #PostMapping("/courses"). (You would have to name your scopes in Keycloak after HTTP-methods for this to work). In this case, you're really using the default PEP-configuration and shouldn't have to specify anything else than enabling policy enforcing for your application.

Repository returns empty list in Spring boot

I am trying to write a simple REST to pull records from a table that was shared with me. Since the table doesn't have a default ID column, I embedded a pk column to the entity object. Please find the code below for your review.
The issue I'm facing is that the repository.findByMediaType, where mediaType is one of the entity properties, returns empty list. I made sure the query param is not null and there are records in the table for the param passed. I tried findAll as well but didn't work. I can't seem to find what's wrong with the code. I am new to spring boot and would like to know the different ways I can debug this.
Service implementation class
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.hyb.enterprisedashboard.api.entity.Tenders;
import com.hyb.enterprisedashboard.api.repository.DashboardRepository;
#Service
public class DashboardServiceImpl implements DashboardService{
Logger logger = LoggerFactory.getLogger(DashboardServiceImpl.class);
#Autowired
DashboardRepository dashboardRepository;
#Override
public List<Tenders> getTenderByMediaType(String mediaType) {
List<Tenders> tenderList = dashboardRepository.findAll();
//findByMediaType(mediaType);
tenderList.stream().forEach(tender -> {
logger.info("Order {} paid via {}",tender.getId().getOrderNumber(), tender.getMediaType());
});
return tenderList;
}
}
Entity class
import java.math.BigDecimal;
import java.util.Date;
import javax.persistence.Column;
import javax.persistence.EmbeddedId;
import javax.persistence.Entity;
import javax.persistence.Table;
#Entity
#Table(name = "TENDERS")
public class Tenders {
/** The id. */
#EmbeddedId
private TendersPK id;
/** The dateTime. */
#Column(name="DATE_TIME")
private Date dateTime;
/** The tenderMedia. */
#Column(name="TENDERED_MEDIA")
private String tenderMedia;
/** The mediaType. */
#Column(name="MEDIA_TYPE")
private String mediaType;
/** The tenderAmount. */
#Column(name="TENDERED_AMOUNT")
private BigDecimal tenderAmount;
/**
* #return the id
*/
public TendersPK getId() {
return id;
}
/**
* #param id the id to set
*/
public void setId(TendersPK id) {
this.id = id;
}
/**
* #return the dateTime
*/
public Date getDateTime() {
return dateTime;
}
/**
* #param dateTime the dateTime to set
*/
public void setDateTime(Date dateTime) {
this.dateTime = dateTime;
}
/**
* #return the tenderMedia
*/
public String getTenderMedia() {
return tenderMedia;
}
/**
* #param tenderMedia the tenderMedia to set
*/
public void setTenderMedia(String tenderMedia) {
this.tenderMedia = tenderMedia;
}
/**
* #return the mediaType
*/
public String getMediaType() {
return mediaType;
}
/**
* #param mediaType the mediaType to set
*/
public void setMediaType(String mediaType) {
this.mediaType = mediaType;
}
/**
* #return the tenderAmount
*/
public BigDecimal getTenderAmount() {
return tenderAmount;
}
/**
* #param tenderAmount the tenderAmount to set
*/
public void setTenderAmount(BigDecimal tenderAmount) {
this.tenderAmount = tenderAmount;
}
#Override
public String toString() {
return "Tenders [id=" + id + ", dateTime=" + dateTime + ", tenderMedia=" + tenderMedia + ", mediaType="
+ mediaType + ", tenderAmount=" + tenderAmount + "]";
}
}
PK Embedded class
import java.io.Serializable;
import javax.persistence.Column;
import javax.persistence.Embeddable;
#Embeddable
public class TendersPK implements Serializable{
/** The Constant serialVersionUID.*/
private static final long serialVersionUID = 1L;
/**
*
*/
public TendersPK() {
}
/**
* #param storeNumber
* #param orderNumber
*/
public TendersPK(long storeNumber, long orderNumber) {
super();
this.storeNumber = storeNumber;
this.orderNumber = orderNumber;
}
#Column(name = "STORE_NUMBER")
private long storeNumber;
#Column(name = "ORDER_NUMBER")
private long orderNumber;
/**
* #return the storeNumber
*/
public long getStoreNumber() {
return storeNumber;
}
/**
* #param storeNumber the storeNumber to set
*/
public void setStoreNumber(long storeNumber) {
this.storeNumber = storeNumber;
}
/**
* #return the orderNumber
*/
public long getOrderNumber() {
return orderNumber;
}
/**
* #param orderNumber the orderNumber to set
*/
public void setOrderNumber(long orderNumber) {
this.orderNumber = orderNumber;
}
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + (int) (orderNumber ^ (orderNumber >>> 32));
result = prime * result + (int) (storeNumber ^ (storeNumber >>> 32));
return result;
}
#Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (!(obj instanceof TendersPK))
return false;
TendersPK other = (TendersPK) obj;
if (orderNumber != other.orderNumber)
return false;
if (storeNumber != other.storeNumber)
return false;
return true;
}
#Override
public String toString() {
return "TendersPK [storeNumber=" + storeNumber + ", orderNumber=" + orderNumber + "]";
}
}
Repository class
import java.util.List;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.stereotype.Repository;
import com.hyb.enterprisedashboard.api.entity.Tenders;
import com.hyb.enterprisedashboard.api.entity.TendersPK;
#Repository
public interface DashboardRepository extends JpaRepository<Tenders, TendersPK>{
#Query("select t from Tenders t where t.mediaType = ?1")
List<Tenders> findByMediaType(String mediaType);
}
And I see the below query passed in the console
Hibernate: select tenders0_.order_number as order_number1_0_, tenders0_.store_number as store_number2_0_, tenders0_.date_time as date_time3_0_, tenders0_.media_type as media_type4_0_, tenders0_.tendered_amount as tendered_amount5_0_, tenders0_.tendered_media as tendered_media6_0_ from tenders tenders0_
Could anyone please help to find the cause?
This was happening to me and it turns out my spring.datasource.* properties were not being set. I had them in the wrong file and the were not being read.
I would think that my repository query would error out if I had not provided datasource url, username, and password - instead I would simply just get an empty list returned.
I ended up figuring out that I was not pulling my datasource credentials by adding this in my RestController:
#Value("${spring.datasource.username}")
String username;
Then I just printed username to the system.out.println. When starting the application I would get an error that spring.datasource.username was undefined. Hence I knew I was not loading datasource information that I thought I was.

Is there any way that we can use two custom error messages using spring-boot custom validation?

I'm using the below custom validation code to validate personName and it seems to be working fine, but the problem is when am passing an EMPTY string, it is giving same error message instead of the empty error message. Can someone please help me with this?
#Documented
#Constraint(validatedBy = {PersonNameValidator.class})
#Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE})
#Retention(RUNTIME)
#ReportAsSingleViolation
public #interface PersonName {
/**
* Default error message defined for the validator.
*
* #return message
*/
String message() default "invalid person name";
/**
* Method to define groups parameters for validation.
*
* #return groups
*/
Class[] groups() default {};
/**
* Method to load payload.
*
* #return payload
*/
Class<? extends Payload>[] payload() default {};
}
public class PersonNameValidator implements ConstraintValidator<PersonName, String> {
#Override
public boolean isValid(String name, ConstraintValidatorContext context) {
if (name.length() == 0) {
throw new IllegalArgumentException("must not be Empty");
} else if (!name.matches("(?=^(?!\\s*$).+)(^[^±!#£$%^&*_+§€#¢§¶•«\\\\/<>?:;|=]{1,256}$)")) {
throw new IllegalArgumentException("name should start with uppercase.");
}
return true;
}
}
#Data
public class NameDto {
#NotNull
#PersonName
private String family1Name;
#PersonName
private String family2Name;
#NotNull
#PersonName
private String givenName;
#PersonName
private String middleName;
}
Getting NullPointerException
#Name
#NotEmpty(message = "name cannot be empty")
String name;
should work
but if you want to join constraint you should use a custom ConstraintValidator add provide this validator via #Constraint(validatedBy = {YourCustomValidator.class}
see example below
full example
used dependency spring-boot-starter-validation (not needed if you use spring-boot-starter-web)
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
application.properties
upper.name=dirk
application
package stackoverflow.demo;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import javax.validation.Constraint;
import javax.validation.Payload;
import javax.validation.ReportAsSingleViolation;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.Pattern;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;
import org.springframework.validation.annotation.Validated;
#SpringBootApplication
public class SoCustomValidationApplication {
public static void main(String[] args) {
SpringApplication.run(SoCustomValidationApplication.class, args);
}
}
#Component
class ConfigurationLoader{
final MyCustomValidatedProperties config;
ConfigurationLoader(MyCustomValidatedProperties config){
this.config = config;
}
#EventListener()
void showName() {
System.err.println("name is: " + config.getName());
}
}
#org.springframework.context.annotation.Configuration
#Validated
#ConfigurationProperties(prefix = "upper")
class MyCustomValidatedProperties {
#Uppercase
#NotEmpty(message = "name cannot be empty")
String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
#Constraint(validatedBy = {ValidNameValidator.class})
#Target({ElementType.FIELD})
#Retention(RetentionPolicy.RUNTIME)
#ReportAsSingleViolation
#interface Uppercase {
String message() default "name should start with uppercase";
Class[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
class ValidNameValidator implements ConstraintValidator<Uppercase, String> {
#Override
public boolean isValid(String name, ConstraintValidatorContext context) {
if (null == name || 0 == name.length() ) {
throw new IllegalArgumentException("name cannot be empty.");
} else if(!name.matches("^([A-Z][a-z]+)")) {
throw new IllegalArgumentException("name should start with uppercase.");
}
return true;
}
}
APPLICATION FAILED TO START
Description:
Binding to target org.springframework.boot.context.properties.bind.BindException: Failed to bind properties under 'upper' to stackoverflow.demo.MyCustomValidatedProperties$$EnhancerBySpringCGLIB$$d0094cdb failed:
Property: upper.name
Value: dirk
Origin: class path resource [application.properties]:1:12
Reason: name should start with uppercase
and if you leave upper.name empty
upper.name=
APPLICATION FAILED TO START
Description:
Binding to target org.springframework.boot.context.properties.bind.BindException: Failed to bind properties under 'upper' to stackoverflow.demo.MyCustomValidatedProperties$$EnhancerBySpringCGLIB$$29925f50 failed:
Property: upper.name
Value:
Origin: class path resource [application.properties]:1:12
Reason: name cannot be empty
Property: upper.name
Value:
Origin: class path resource [application.properties]:1:12
Reason: name should start with uppercase

methods in an ejb session bean for an entity dont work in a spring MVC project

I created a maven entreprise application project, in the ejb module i put my entities in a package and i generated my session beans in an other package and i deployed my ejb module in glassfish,in the web module i added dependencies of spring and I created a controller that search the session bean and call the methode find all() but it doesnt get my objects stored in database, what should i do?
Category entity
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package com.mycompany.entities;
import java.io.Serializable;
import java.util.List;
import javax.persistence.Basic;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Lob;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import javax.validation.constraints.Size;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlTransient;
/**
*
* #author DELL
*/
#Entity
#Table(name = "categorie")
#XmlRootElement
#NamedQueries({
#NamedQuery(name = "Categorie.findAll", query = "SELECT c FROM Categorie c"),
#NamedQuery(name = "Categorie.findByIdCategorie", query = "SELECT c FROM Categorie c WHERE c.idCategorie = :idCategorie"),
#NamedQuery(name = "Categorie.findByDescription", query = "SELECT c FROM Categorie c WHERE c.description = :description"),
#NamedQuery(name = "Categorie.findByName", query = "SELECT c FROM Categorie c WHERE c.name = :name"),
#NamedQuery(name = "Categorie.findByNomPhoto", query = "SELECT c FROM Categorie c WHERE c.nomPhoto = :nomPhoto")})
public class Categorie implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Basic(optional = false)
#Column(name = "idCategorie")
private Integer idCategorie;
#Size(max = 255)
#Column(name = "description")
private String description;
#Size(max = 20)
#Column(name = "name")
private String name;
#Size(max = 255)
#Column(name = "nomPhoto")
private String nomPhoto;
#Lob
#Column(name = "photo")
private byte[] photo;
#OneToMany(mappedBy = "idCategorie")
private List<Produit> produitList;
public Categorie() {
}
public Categorie(Integer idCategorie) {
this.idCategorie = idCategorie;
}
public Integer getIdCategorie() {
return idCategorie;
}
public void setIdCategorie(Integer idCategorie) {
this.idCategorie = idCategorie;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getNomPhoto() {
return nomPhoto;
}
public void setNomPhoto(String nomPhoto) {
this.nomPhoto = nomPhoto;
}
public byte[] getPhoto() {
return photo;
}
public void setPhoto(byte[] photo) {
this.photo = photo;
}
#XmlTransient
public List<Produit> getProduitList() {
return produitList;
}
public void setProduitList(List<Produit> produitList) {
this.produitList = produitList;
}
#Override
public int hashCode() {
int hash = 0;
hash += (idCategorie != null ? idCategorie.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 Categorie)) {
return false;
}
Categorie other = (Categorie) object;
if ((this.idCategorie == null && other.idCategorie != null) || (this.idCategorie != null && !this.idCategorie.equals(other.idCategorie))) {
return false;
}
return true;
}
#Override
public String toString() {
return "com.mycompany.entities.Categorie[ idCategorie=" + idCategorie + " ]";
}
}
AbstractFacade
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package com.mycompany.dao;
import java.util.List;
import javax.persistence.EntityManager;
/**
*
* #author DELL
*/
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();
}
}
CategorieFacade
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package com.mycompany.dao;
import com.mycompany.entities.Categorie;
import java.util.List;
import javax.ejb.Stateless;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
/**
*
* #author DELL
*/
#Stateless
public class CategorieFacade extends AbstractFacade<Categorie> implements CategorieFacadeLocal {
#PersistenceContext(unitName = "com.mycompany_ProjetCommerce-ejb_ejb_1.0-SNAPSHOTPU")
private EntityManager em;
#Override
protected EntityManager getEntityManager() {
return em;
}
public CategorieFacade() {
super(Categorie.class);
}
}
CategorieFacadeLocal
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package com.mycompany.dao;
import com.mycompany.entities.Categorie;
import java.util.List;
import javax.ejb.Local;
/**
*
* #author DELL
*/
#Local
public interface CategorieFacadeLocal {
void create(Categorie categorie);
void edit(Categorie categorie);
void remove(Categorie categorie);
Categorie find(Object id);
List<Categorie> findAll();
List<Categorie> findRange(int[] range);
int count();
}
CategorieController
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package com.mycompany.controlleur;
import com.mycompany.dao.CategorieFacadeLocal;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import org.springframework.stereotype.*;
import org.springframework.ui.*;
import org.springframework.web.bind.annotation.*;
#Controller
#RequestMapping("categorie")
public class CategorieController {
CategorieFacadeLocal categorieFacade = lookupCategorieFacadeLocal();
#RequestMapping(method = RequestMethod.GET)
public String index(ModelMap modelmap){
modelmap.put("listeCategorie", categorieFacade.findAll());
return "index";
}
private CategorieFacadeLocal lookupCategorieFacadeLocal() {
try {
Context c = new InitialContext();
return (CategorieFacadeLocal) c.lookup("java:global/com.mycompany_ProjetCommerce-ear_ear_1.0-SNAPSHOT/com.mycompany_ProjetCommerce-ejb_ejb_1.0-SNAPSHOT/CategorieFacade!com.mycompany.dao.CategorieFacadeLocal");
} catch (NamingException ne) {
Logger.getLogger(getClass().getName()).log(Level.SEVERE, "exception caught", ne);
throw new RuntimeException(ne);
}
}
}

Spring Data REST #Idclass not recognized

I have an entity named EmployeeDepartment as below
#IdClass(EmployeeDepartmentPK.class) //EmployeeDepartmentPK is a serializeable object
#Entity
EmployeeDepartment{
#Id
private String employeeID;
#Id
private String departmentCode;
---- Getters, Setters and other props/columns
}
and I have a Spring Data Repository defined as as below
#RepositoryRestResource(....)
public interface IEmployeeDepartmentRepository extends PagingAndSortingRepository<EmployeeDepartment, EmployeeDepartmentPK> {
}
Further, I have a converter registered to convert from String to EmployeeDepartmentPK.
Now, for an entity, qualified by ID employeeID="abc123" and departmentCode="JBG", I expect the ID to use when SDR interface is called is abc123_JBG.
For example http://localhost/EmployeeDepartment/abc123_JBG should fetch me the result and indeed it does.
But, when I try to save an entity using PUT, the ID property available in BasicPersistentEntity class of Spring Data Commons is having a value of
abc123_JBG for departmentCode. This is wrong. I'm not sure if this is an expected behaviour.
Please help.
Thanks!
Currently Spring Data REST only supports compound keys that are represented as by a single field. That effectively means only #EmbeddedId is supported. I've filed DATAJPA-770 to fix that.
If you can switch to #EmbeddedId you still need to teach Spring Data REST the way you'd like to represent your complex identifier in the URI and how to transform the path segment back into an instance of your id type. To achieve that, implement a BackendIdConverter and register it as Spring bean.
#Component
class CustomBackendIdConverter implements BackendIdConverter {
#Override
public Serializable fromRequestId(String id, Class<?> entityType) {
// Make sure you validate the input
String[] parts = id.split("_");
return new YourEmbeddedIdType(parts[0], parts[1]);
}
#Override
public String toRequestId(Serializable source, Class<?> entityType) {
YourIdType id = (YourIdType) source;
return String.format("%s_%s", …);
}
#Override
public boolean supports(Class<?> type) {
return YourDomainType.class.equals(type);
}
}
If you can't use #EmbeddedId, you can still use #IdClass. For that, you need the BackendIdConverter as Oliver Gierke answered, but you also need to add a Lookup for your domain type:
#Configuration
public class IdClassAllowingConfig extends RepositoryRestConfigurerAdapter {
#Override
public void configureRepositoryRestConfiguration(RepositoryRestConfiguration config) {
config.withEntityLookup().forRepository(EmployeeDepartmentRepository.class, (EmployeeDepartment ed) -> {
EmployeeDepartmentPK pk = new EmployeeDepartmentPK();
pk.setDepartmentId(ed.getDepartmentId());
pk.setEmployeeId(ed.getEmployeeId());
return pk;
}, EmployeeDepartmentRepository::findOne);
}
}
Use #BasePathAwareController to customize Spring data rest controller.
#BasePathAwareController
public class CustInfoCustAcctController {
#Autowired
CustInfoCustAcctRepository cicaRepo;
#RequestMapping(value = "/custInfoCustAccts/{id}", method = RequestMethod.GET)
public #ResponseBody custInfoCustAccts getOne(#PathVariable("id") String id) {
String[] parts = id.split("_");
CustInfoCustAcctKey key = new CustInfoCustAcctKey(parts[0],parts[1]);
return cicaRepo.getOne(key);
}
}
It's work fine for me with sample uri /api/custInfoCustAccts/89232_70
A more generic approach would be following -
package com.pratham.persistence.config;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.sun.istack.NotNull;
import lombok.RequiredArgsConstructor;
import org.springframework.data.rest.webmvc.spi.BackendIdConverter;
import org.springframework.lang.NonNull;
import org.springframework.stereotype.Component;
import javax.persistence.EmbeddedId;
import java.io.Serializable;
import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.Base64;
import java.util.Optional;
import static java.nio.charset.StandardCharsets.UTF_8;
/**
* Customization of how composite ids are exposed in URIs.
* The implementation will convert the Ids marked with {#link EmbeddedId} to base64 encoded json
* in order to expose them properly within URI.
*
* #author im-pratham
*/
#Component
#RequiredArgsConstructor
public class EmbeddedBackendIdConverter implements BackendIdConverter {
private final ObjectMapper objectMapper;
#Override
public Serializable fromRequestId(String id, Class<?> entityType) {
return getFieldWithEmbeddedAnnotation(entityType)
.map(Field::getType)
.map(ret -> {
try {
String decodedId = new String(Base64.getUrlDecoder().decode(id));
return (Serializable) objectMapper.readValue(decodedId, (Class) ret);
} catch (JsonProcessingException ignored) {
return null;
}
})
.orElse(id);
}
#Override
public String toRequestId(Serializable id, Class<?> entityType) {
try {
String json = objectMapper.writeValueAsString(id);
return Base64.getUrlEncoder().encodeToString(json.getBytes(UTF_8));
} catch (JsonProcessingException ignored) {
return id.toString();
}
}
#Override
public boolean supports(#NonNull Class<?> entity) {
return isEmbeddedIdAnnotationPresent(entity);
}
private boolean isEmbeddedIdAnnotationPresent(Class<?> entity) {
return getFieldWithEmbeddedAnnotation(entity)
.isPresent();
}
#NotNull
private static Optional<Field> getFieldWithEmbeddedAnnotation(Class<?> entity) {
return Arrays.stream(entity.getDeclaredFields())
.filter(method -> method.isAnnotationPresent(EmbeddedId.class))
.findFirst();
}
}

Resources