I am using spring mvc and I have a service class called UserManager. The class is used to manage a collection of users like add user and delete user from colection. Basicaaly it provides all the information about a collection of users.
This class is used by controllers to access user collection information.
Now the problem is I have to use it as a bean for spring injection. But a bean should have getters and setters only.
So i am confused as to how i implement this class.
here is the code for UserManager
import com.bo.user.UserBO;
/*
* UserManager class is a service class which provides service to Controller for managing the users in the system.
* It has a collection _allUserMap which maintains the users inside the system all through the life of system.
* It manages the addition, deletion and updation of users.
* UserBO is the service which helps UserManager access the users, individually, from Database
*/
#Service
public class UserManager{
#Autowired
private UserBO userBo;
private static Map<Integer,User> _allUserMap = new HashMap<Integer, User>();
/*
* Method populates the _allUserMap
* using userBo
*/
#PostConstruct
public void loadAllUsers(){
Integer id = null;
List<User> _allUserList = userBo.listAllUser();
System.out.println("<--------Initializing all user map--------->");
for(User user : _allUserList){
id = user.getId();
_allUserMap.put(id, user);
}
}
/*
* Adds the user after checking if the user exists
* #param User:Takes the User to add from the Controller
* #Return boolean User added or not
* Beta 1.1 validation for correct user addition form input
*/
public boolean addUser(User user){
boolean userAdded = false;
if (hasUser(user)){
userAdded = false;
}else{
userBo.save(user);
userAdded = true;
}
return userAdded;
}
/*
* Checks if the user is already present
* #Param User
* #Return is user present
*/
private boolean hasUser(User formUser){
boolean isUser = false;
User user = null;
for(Entry<Integer, User> entry: _allUserMap.entrySet()){
user = entry.getValue();
if(user.equals(formUser)){
isUser = true;
}
return isUser;
}
return isUser;
}
/*
* #Param User
* #Return String : message gives what feild is alreay in database
*/
public String matchCredentails(User formUser){
String message = "";
User user = null;
for(Entry<Integer, User> entry: _allUserMap.entrySet()){
user = entry.getValue();
if(user.getEmail().equals(formUser.getEmail())){
message = "Email alreay exists+";
}
if(user.getMobileNumber()== formUser.getMobileNumber()){
message = message + "Mobile number alreay exists+";
}
if(user.getUserName().equals(formUser.getUserName())){
message = message + "UserName alreay exists+";
}
}
return message;
}
}
here is how i am accessing it in controller
#Controller
public class UserController {
//These are the instances of the service providing bean and not the state of the spring controller
#Autowired
private UserManager userManager;
My question is simple... should i make this class a bean. because this class is not a simple pojo by definition.
No, there is absolutely no need for getters and setters here since you are injecting the UserBO field through #Autowired.
The Spring documentation says
A bean is simply an object that is instantiated, assembled and
otherwise managed by a Spring IoC container; other than that, there is
nothing special about a bean [...].
There is no mention of getters/setters and therefore you shouldn't think of them as necessary for beans. Use them when appropriate.
Related
I am setting up a Java Spring project with multiple StoredProcedures to two completely different Oracle databases.
It is not allowed to use any auto-generated SQL.
I didn't find anywhere a complete solution or implementation example so I will try to sum up the question and clean solution here.
I sincerely hope this will help someone someday.
You will need a working Spring Boot project.
Please let me know if there is anything confusing and I should explain it better.
Database connection settings
The Database connection Properties (db.properties)
Please place this file in resources folder.
db1.datasource.url=jdbc:oracle:thin:#url:sid
db1.datasource.username=username
db1.datasource.password=password
db1.datasource.driver-class-name=oracle.jdbc.OracleDriver
db2.datasource.url=jdbc:oracle:thin:#url:sid
db2.datasource.username=username
db2.datasource.password=password
db2.datasource.driver-class-name=oracle.jdbc.OracleDriver
Database configuration class (DbConfiguration.java)
#Configuration
#Order(1)
#PropertySource("classpath:/db.properties")
public class DbConfiguration {
/**
* Configuration beans for establishing a connection to db1 database.
* The primary database dataSource is automatically populated to the constructor in StoredProcedure extended class.
*/
#Bean
#Primary
#ConfigurationProperties("db1.datasource")
public DataSourceProperties db1DataSourceProperties() {
return new DataSourceProperties();
}
#Bean(name = "db1")
#Primary
public DataSource db1DataSource() {
return db1DataSourceProperties().initializeDataSourceBuilder().build();
}
/**
* Configuration beans for establishing a connection to db2 database.
*/
#Bean
#ConfigurationProperties("db2.datasource")
public DataSourceProperties db2DataSourceProperties() {
return new DataSourceProperties();
}
#Bean(name = "db2")
public DataSource db2DataSource() {
return db2DataSourceProperties().initializeDataSourceBuilder().build();
}
Db1 stored procedure classes
StoredProcedure for retrieving a single payment (SPGetSinglePayment.java)
/**
* The type Sp get payment.
* A StoredProcedure class where we define IN and OUT parameters.
*/
#Component
public class SPGetSinglePayment extends StoredProcedure {
public static final String PROCEDURE_GET_PAYMENT = "GET_PAYMENT";
public static final String PROCEDURE_GET_PAYMENT_PARAM_IN_ID = "P_PAYMENT_ID";
public static final String PROCEDURE_GET_PAYMENT_PARAM_OUT_RESULT = "PAYMENT_RESULT";
public SPGetSinglePayment(final DataSource dataSource) {
super(dataSource, PACKAGE_NAME + PROCEDURE_GET_PAYMENT);
declareParameter(new SqlParameter(PROCEDURE_GET_PAYMENT_PARAM_IN_ID, OracleTypes.VARCHAR));
declareParameter(new SqlOutParameter(PROCEDURE_GET_PAYMENT_PARAM_OUT_RESULT, OracleTypes.CURSOR));
compile();
}
}
StoredProcedure Response builder class (SinglePaymentResponseBuilder.java)
/**
* The type Payment response builder. Gets an object from Oracle DB and populates POJOs.
*/
#Component
public class SinglePaymentResponseBuilder {
/**
* Builds list of payment transaction details from stored procedure result set.
*
* #param getPaymentObject the object containing payment details result set
* #param getItineraryDataObject the object containing itinerary data result set
* #return list of payment details for payment
*/
public List<SinglePaymentDto> build(final Object getPaymentObject, final Object getItineraryDataObject) {
final List<Map<String, Object>> spMap = getListOfObjectMaps(getPaymentObject);
final List<SinglePaymentDto> response = new ArrayList<>();
for (Map<String, Object> dtos : spMap) {
SinglePaymentDto payment = new SinglePaymentDto(
new PaymentInfo(getStringValue(dtos.get(PaymentInfo.PAYMENT_ID)),
... build and return response
StoredProcedure Helper class (StoredProcedureHelper.java)
Here we actually execute two stored procedures to a single database.
/**
* Contains methods to call Oracle prepared statements. Responsible for handling procedure specific input and output parameters.
*/
#Component
public class StoredProcedureHelper {
public static final String PACKAGE_NAME = "A_PACKAGE_NAME.";
private final SPGetSinglePayment getSinglePayment;
private final SinglePaymentResponseBuilder singlePaymentResponseBuilder;
#Autowired
public StoredProcedureHelper(
final SPGetSinglePayment getSinglePayment,
final SinglePaymentResponseBuilder singlePaymentResponseBuilder,
...){
this.getSinglePayment = getSinglePayment;
this.singlePaymentResponseBuilder = singlePaymentResponseBuilder;
...
}
/**
* Calls stored procedure to get all payment details for given payment.
*
* #param id the payment id
* #return payment details
*/
public List<SinglePaymentDto> findSinglePayment(final String id) {
LOG.info(LOG_PATTERN, SPGetSinglePayment.class.getSimpleName(),
PACKAGE_NAME, PROCEDURE_GET_PAYMENT);
Object spGetPaymentResult = getSinglePayment.execute(id).get(PROCEDURE_GET_PAYMENT_PARAM_OUT_RESULT);
Object spGetItineraryDataResult = getItineraryData.execute(id).get(PROCEDURE_GET_ITINERARY_DATA_PARAM_OUT_RESULT);
return singlePaymentResponseBuilder.build(spGetPaymentResult, spGetItineraryDataResult);
}
Db2 stored procedure classes
StoredProcedure for retrieving a decrypted toothbrush from its identifier token (SPGetToothbrush.java)
I'd like to expose just below class here. Please note that if you'd like to use a db2 you will have to define it by #Qualifier annotation.
Other classes will follow the above pattern for each stored procedure.
On request I can also provide unit test examples.
/**
* The type Sp get toothbrush.
* A StoredProcedure class where we define IN and OUT parameters.
*/
#Component
public class SPGetToothbrush extends StoredProcedure {
public static final String PROCEDURE_GET_TOOTHBRUSH = "GET_IDENTIFIER";
public static final String PROCEDURE_GET_TOOTHBRUSH_PARAM_IN_INSTRUMENT_ID = "P_TOKEN";
public static final String PROCEDURE_GET_TOOTHBRUSH_PARAM_OUT_RESULT = "OUT_IDENTIFIER";
/**
* Instantiates a new Sp get toothbrush.
*
* #param dataSource is populated by db2 properties by use of #Qualifier.
*/
public SPGetToothbrush(#Qualifier("db2") final DataSource dataSource) {
super(dataSource, StoredProcedureToothbrushHelper.PACKAGE_NAME + PROCEDURE_GET_TOOTHBRUSH);
declareParameter(new SqlParameter(PROCEDURE_GET_TOOTHBRUSH_PARAM_IN_INSTRUMENT_ID, OracleTypes.VARCHAR));
declareParameter(new SqlOutParameter(PROCEDURE_GET_TOOTHBRUSH_PARAM_OUT_RESULT, OracleTypes.VARCHAR));
compile();
}
}
I am tring to implement logging of users in Spring Security on the basis of a popular Tutorial and have a doubt about how spring beans are wired.
The below class is defined as a standard bean in the Spring Context
public class ActiveUserStore {
public List<String> users;
public ActiveUserStore() {
users = new ArrayList<String>();
}
public List<String> getUsers() {
return users;
}
public void setUsers(List<String> users) {
this.users = users;
}
}
The above is defined as a Bean through
#Bean
public ActiveUserStore activeUserStore(){
return new ActiveUserStore();
}
And the Bean is being used in the below class, please note the users.add(user.getUsername());
#Component
public class LoggedUser implements HttpSessionBindingListener {
private String username;
private ActiveUserStore activeUserStore;
public LoggedUser(String username, ActiveUserStore activeUserStore) {
this.username = username;
this.activeUserStore = activeUserStore;
}
public LoggedUser() {}
#Override
public void valueBound(HttpSessionBindingEvent event) {
List<String> users = activeUserStore.getUsers();
LoggedUser user = (LoggedUser) event.getValue();
if (!users.contains(user.getUsername())) {
users.add(user.getUsername());//HOW IS THIS SAVED TO THE ACTIVEUSERSTORE BEAN
}
}
#Override
public void valueUnbound(HttpSessionBindingEvent event) {
List<String> users = activeUserStore.getUsers();
LoggedUser user = (LoggedUser) event.getValue();
if (users.contains(user.getUsername())) {
users.remove(user.getUsername());//HOW IS THIS SAVED TO THE ACTIVEUSERSTORE BEAN
}
}
My Question: Since users variable belongs to the ActiveUserStore Bean, how does the following line of code inside the valueBound and valueUnbound methods of Logged User Class, automatically save the ussers data inside the ActiveUserStore Bean ?
users.add(user.getUsername());
I have also marked this line in the code snipped above for clarity.
Any help is appreciated, since I think my understanding of how Beans are wired and used is probably not clear since I am not able to understand the above working. Thanks.
Think about Spring Bean as usual Java class. This class instantiated by Spring at application start just once (actually this is not always true, but by default it is)
Now you inside of your bean, you have reference to List variable and get it with:
List<String> users = activeUserStore.getUsers();
Now users variable contains reference to the List stored in bean. This is because in Java objects are passed by reference.
LoggedUser user = (LoggedUser) event.getValue();
if (!users.contains(user.getUsername())) {
and here users variable contains List class reference. List class have method add, and LoggedUser have getUsername method. As users variable refers to same List object as your bean property contain, this adds username to your bean:
users.add(user.getUsername());
}
I am using Dropwizard with JDBI. I have a typical dao for user data:
public interface UserDao
{
#SqlQuery("select * from users where role = :id")
#Mapper(UserMapper.class)
String findNameById(#BindBean Role role);
}
The user itself has an attribute with a Role type:
class User
{
private Role role;
/* the rest: other attributes, getters, setters, etc. */
}
Role is contained in another table called roles. Now, I need to map Role in the mapper, but I do not want to change the SELECT ... statement to add the JOIN roles ... part. We all know how joins affect queries and in the long run I'd like to avoid any joins if possible.
I know, that ResultSetMapper interface has a map() method, which gets a StatementContext passed to it. That context has a getBinding() method, which returns a Binding class with all the data I need:
named = {HashMap$Node#1230} size = 3
0 = {HashMap$Node#1234} "id" -> "1"
1 = {HashMap$Node#1235} "name" -> "TestRole"
2 = {HashMap$Node#1236} "class" -> "class com.example.Role"
But that class com.example.Role is not an instance of Role, it's an instance of Argument and I can't work with it.
So, is there a way to get that Role argument and I just don't see it or do I have to instantiate it (again...) from the binding arguments (obviously they are there as debugger shows)?
I finally solved it by using a custom binder. First, I modified UserDao to use #BindRole instead of #BindBean.
Next, I had to create the binder for the role. Here the role is bound manually on separate values:
#BindingAnnotation(BindRole.RoleBinderFactory.class)
#Retention(RetentionPolicy.RUNTIME)
#Target({ElementType.PARAMETER})
public #interface BindRole
{
public static class RoleBinderFactory implements BinderFactory
{
public Binder build(Annotation annotation)
{
return new Binder<BindRole, User>()
{
public void bind(SQLStatement q, BindRole bind, Role role)
{
q.bind("id", role.getId());
q.bind("name", role.getName());
q.define("role", role);
}
};
}
}
}
Notice the define() method, it is responsible for setting attributes in StatementContext, so don't overlook it.
Next, in the mapper I just have to get the Role with getArgument():
Role role = new Role();
role.setId(1);
role.setName("TestRole");
Role r = (Role) statementContext.getAttribute("role");
boolean equals = e.equals(role);
In the debugger equals is shown as true, so problem solved. Woohoo.
Let's say I have a User class (with properties like name, last name, etc.) and a UserService class that is used to query the database and get User data. Let's say I have the following method in my UserService class:
public User getUser(int userId)
{
User user = new User();
/* query database and returns user data. Then I use setters to set User data
...........
*/
return user;
}
I have the following controller class:
public class DIGRCController {
private User user;
private UserService service;
//getters and setters
#PostConstruct
public void init() {
service = new UserService();
//imagine userId is coming from somewhere (not relevant to this question)
user = service.getUser(userId);
}
}
My question is:
Is user = service.getUser(userId) good practice?
user has one object reference and then I am pointing it to another reference. Is this ok in programming? If not, what should I do?
I checked a few questions in SO but couldn't find one with what I am looking for. If you have a SO question I could look at, please show me.
Is it possible to specify projection when calling data repository method directly? Here's repository code - note I would not like to expose it via REST, instead I would like to be able to call it from a service or controller:
#RepositoryRestResource(exported = false)
public interface UsersRepository extends PagingAndSortingRepository<User, Long> {
#Query(value = "SELECT u FROM User u WHERE ....")
public Page<User> findEmployeeUsers(Pageable p);
}
Then in a controller I do this:
#PreAuthorize(value = "hasRole('ROLE_ADMIN')")
#RequestMapping(value = "/users/employee")
public Page<User> listEmployees(Pageable pageable) {
return usersRepository.findEmployeeUsers(pageable);
}
Is there any way to specify projection for findEmployeeUsers method when it is called directly like above?
I realise that the code above might look odd for someone... it would be possible to expose the repository via REST and put the #PreAuthorize thing in the repository. Thought controller is the more right place to do security checks - it is more natural as well as simpler to test.
So, can projection thing be somehow passed into a repository method called directly?
No it's not, especially as projections are usually applied to the result of a query execution on a case by case basis. Thus they're currently designed to be selectively applied to domain types.
As of the latest Spring Data Fowler release train GA release the projection infrastructure can be used programmatically in Spring MVC controllers. Simply declare a Spring bean for SpelAwareProxyProjectionFactory:
#Configuration
class SomeConfig {
#Bean
public SpelAwareProxyProjectionFactory projectionFactory() {
return new SpelAwareProxyProjectionFactory();
}
}
Then inject it into your controller and use it:
#Controller
class SampleController {
private final ProjectionFactory projectionFactory;
#Autowired
public SampleController(ProjectionFactory projectionFactory) {
this.projectionFactory = projectionFactory;
}
#PreAuthorize(value = "hasRole('ROLE_ADMIN')")
#RequestMapping(value = "/users/employee")
public Page<?> listEmployees(Pageable pageable) {
return usersRepository.findEmployeeUsers(pageable).//
map(user -> projectionFactory.createProjection(Projection.class, user);
}
}
See how as of the latest release Page has a map(…) method that can be used to transform the page content on the fly. We use a JDK 8 lambda to provide a conversion step using the ProjectionFactory.
Additionally to the #Oliver's answer, if you want to lookup the Projections by name as SpringDataRest does (instead of hardwired them in your controller), this is what you have to do:
Inject RepositoryRestConfiguration into your controller. This bean gives you access to a class called ProjectionDefinitions (see, getProjectionConfiguration()) which acts a projection metadata directory.
Using ProjectionDefinitions you can retrieve Projection Classes given their names and their associated bound classes.
Later, you can use the method detailed by #Oliver to create the projections instances ...
This is a small Controller that implements what I describe:
#RestController
#RequestMapping("students")
public class StudentController {
/**
* {#link StudentController} logger.
*/
private static final Logger logger =
LoggerFactory.getLogger(StudentController.class);
/**
* Projections Factory.
*/
private ProjectionFactory p8nFactory;
/**
* Projections Directory.
*/
private ProjectionDefinitions p8nDefs;
/**
* {#link Student} repository.
*/
private StudentRepository repo;
/**
* Class Constructor.
*
* #param repoConfig
* {#code RepositoryRestConfiguration} bean
* #param p8nFactory
* Factory used to create projections
* #param repo
* {#link StudentRepository} instance
*/
#Autowired
public StudentController(
RepositoryRestConfiguration repoConfig,
ProjectionFactory p8nFactory,
StudentRepository repo
) {
super();
this.p8nFactory = p8nFactory;
this.p8nDefs = repoConfig.getProjectionConfiguration();
this.repo = repo;
}
...
/**
* Retrieves all persisted students.
*
* #param projection
* (Optional) Name of the projection to be applied to
* students retrieved from the persistence layer
* #return
* {#code ResponseEntity} whose content can be a list of Students
* or a projected view of them
*/
#GetMapping(path = "", produces = APPLICATION_JSON_VALUE)
public ResponseEntity<Object> retrieveAll(
#RequestParam(required = false) String projection
) {
Class<?> type; // Kind of Projection to be applied
List<?> rawData; // Raw Entity Students
List<?> pjData; // Projected students (if applies)
rawData = this.repo.findAll();
pjData = rawData;
if (projection != null) {
type = this.p8nDefs.getProjectionType(Student.class, projection);
pjData = rawData
.stream()
.map(s -> this.p8nFactory.createProjection(type, s))
.collect(Collectors.toList());
}
return new ResponseEntity<>(pjData, HttpStatus.OK);
}
}
It can be easily done in the lates Spring Data Rest releases!
All you need to do is to:
pass projection name as request param
`/api/users/search/findEmployeeUsers?projection=userView`
return PagedModel<PersistentEntityResource> instead of Page<User> from your service method;
Done!
and I assume you want to call this service method from your custom controller, in this case you need to return ResponseEntity<PagedModel<PersistentEntityResource>> from your controller method.
Don't want it pageable? Simply return ResponseEntity<CollectionModel<PersistentEntityResource>> instead.
Also check out example for single resoure projection.
Spring Data Rest takes care of applying #Projections to PersistentEntityResources on api requests, it's just like you keep exposing your #RestResource from #RepositoryRestResource; same behaviour for projections, keeping same naming convention, basically same URI (for current example).
Your service method with a bit of bussiness logic might look like:
#Override
#Transactional(readOnly = true)
public PagedModel<PersistentEntityResource> listEmployees(Pageable pageable, PersistentEntityResourceAssembler resourceAssembler) {
Page<User> users = userRepository.findEmployeeUsers(pageable);
List<User> entities = users.getContent();
entities.forEach(user -> user.setOnVacation(isUserOnVacationNow(user)));
CollectionModel<PersistentEntityResource> collectionModel = resourceAssembler.toCollectionModel(entities);
return PagedModel.of(collectionModel.getContent(), new PagedModel.PageMetadata(
users.getSize(),
users.getNumber(),
users.getTotalElements(),
users.getTotalPages()));
}
and your controller method might look like this:
#BasePathAwareController
public class UsersController {
#GetMapping(value = "/users/search/findEmployeeUsers")
ResponseEntity<PagedModel<PersistentEntityResource>> findEmployeeUsers(Pageable pageable,
PersistentEntityResourceAssembler resourceAssembler) {
return ResponseEntity.status(HttpStatus.OK)
.body(userService.listEmployees(pageable, resourceAssembler));
}
}
I'm using spring-boot-starter-data-rest:2.3.4.RELEASE with spring-data-rest-webmvc:3.3.4.RELEASE and spring-data-rest-webmvc:3.3.4.RELEASE as dependencies, configuring it as parent of my pom.xml
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.4.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>