I am new to spring and struts2. I found there were two instances of my action, one with the spring injection and the other not.
Here are my simple WelcomeUserAction.java
public class WelcomeUserAction extends ActionSupport {
private String username;
private String password;
private String name;
private UserDaoImpl userDao;
#Action(value="Welcome", results = {
#Result(name ="success",location="pages/welcome_user.jsp"),
#Result(name ="input", location="pages/signup.jsp")
})
public String execute(){
User user = new User();
user.setPassword(password);
user.setUsername(username);
user.setName(name);
if (getUserDao() == null){
System.out.println("getUserDao is null");
}
getUserDao().saveUser(user);
return SUCCESS;
}
/* getter and setters... */
}
When I debug this application, I found even before the index page loaded, the spring was doing the injection. As the pic shows
And when the WelcomeUserAction starts working, there is another WelcomeUserAction instance, and the spring managing part,that is userDao became null.
I don't know why this happens. Should I provide more information such as applicationContext.xml and web.xml ?
I found what I am missing. I forgot to add struts2-spring-plugin.jar to my buildpath.
Related
I have a form in which :
firstname and lastname are mandatory fields for registered user.
ssn for new user.
contract number for owner.
So, on clicking the submit button, REST API (connect API) is called with values
from either of the above groups.
My bean class has members :
FN
LN
SSN
contractNum
How do I validate using bean/hibernate validator and identify which group has been passed ?
From the Hibernate Documentation, you can read for detail
https://hibernate.org/validator/
Hibernate Validator allows to express and validate application
constraints. The default metadata source are annotations, with the
ability to override and extend through the use of XML. It is not tied
to a specific application tier or programming model and is available
for both server and client application programming. But a simple
example says more than 1000 words:
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
public class User {
#NotNull
private String firstName;
#NotNull
private String lastName;
#NotNull
private String ssn;
}
Bean Validation is best used for simple validation logic. If your validation requires more complexity, use Spring's Validator interface instead.
I don't know the context domain, so I'll just call your bean "Form" with all String fields for the example:
public class Form {
private String firstName;
private String lastName;
private String ssn;
private String contractNumber;
// getters and setters
}
Then create a validator for this class:
public class FormValidator implements Validator {
public boolean supports(Class clazz) {
return Form.class.isAssignableFrom(clazz);
}
public void validate(Object target, Errors errors) {
Form form = (Form) target;
// validation logic
}
}
Then you can simply use it like this:
Form form = ...;
Validator validator = new FormValidator();
Errors errors = new Errors();
validator.validate(form, errors);
if (errors.hasErrors() {
// not valid
} else {
// is valid
}
I want to implement a PATCH-Requese on an entity 'User' for changing the password with an additional transient property 'oldpassword' to compare it in the EventHandler.
The POST- and the PUT-request fill the property.
The PATCH-request doesn't: 'oldpassword' is null.
I'm using
spring-boot-starter-parent
spring-boot-starter-data-rest (2.1.6)
spring-boot-starter-web (2.1.6)
spring-boot-starter-data-jpa (2.1.6)
spring-data-jpa 2.1.9
spring-data-rest 3.1.9
spring-security 5.1.5 (presumably irrelevant)
I tried
the annotation #JsonProperty("oldpassword") (even though POST and PUT work).
the annotation #JsonDeserialize (JSON: #Transient field not seralizing)
to configure Jackson to disable the check for #Transient annotations (JPA Transient Annotation and JSON)
#JsonAutoDetect(fieldVisibility = Visibility.ANY) as class decorator
The simplified code is:
The entity 'User'
#Entity
public class User implements UserDetails, Serializable {
[...]
#NotNull
String password;
#Transient
String newpassword;
#Transient
String oldpassword;
public void setPassword(String password) {
this.newpassword = password;
}
public void setOldpassword(String oldpassword) {
this.oldpassword = oldpassword;
}
[...]
}
The Repository
#RepositoryRestResource(exported = true)
public interface UserRepository extends JpaRepository<User, Long> {
}
PATCH-Request (
HTTP Method = PATCH
Request URI = /api/users/2
Parameters = {}
Headers = [Content-Type:"application/json;charset=UTF-8", Authorization:"Basic aXJ0Z2VuZGFhczpFaW4gcGFzc3dvcmQ="]
Body = {
"username": "myusername",
"password": "mynewpassword",
"oldpassword": "theoldone"
}
The EventHandler
#Component
#RepositoryEventHandler(User.class)
public class UserEventHandler {
#HandleBeforeSave
public void printdata(User p) {
/* returns the new password*/
System.out.println("newpassword" + p.newpassword);
/* returns null (if it's a PATCH-request) */
System.out.println("oldpassword" + p.oldpassword);
/* returns the old persisted password */
System.out.println("password" + p.password);
}
}
The transient property 'newpassword' works, since I use the setter of the persisted property 'password'.
It seems you want to create a change-password feature. It won't work this way. Create a unique controller method for it.
It's not a standard REST request anyway.
I'm not new to java (i did some projects with javafx and jee ) but I'm new to the whole spring thing
Here is what my scenario is
All users have same level of access but some have more accee defiened by their role in the database ( there can be upto 25!(yes that is factorial) roles all created dynamically by the main admin) .
My template is made of 3 fragments (header with admin links that are available to users associated to their roles , side bar available to everyone and footer)
So I'm stuck in how to make a global variable for the header part to check the logged in user on each page load and choose the correct links
I know how to do this with jee and jsp but not with spring
Thanks in advance.
Use a repository and hold your session data after login like below
#Repository
#Scope(value = "session", proxyMode = ScopedProxyMode.TARGET_CLASS)
public class LoginRepository {
private String accessToken;
private String name;
private String password;
private String userId;
private String rolename;
private String department;
}
then use #ControllerAdvice to pass this in every Model
#Autowired
LoginRepository settings;
#ModelAttribute
public void addAttributes(Model model) {
model.addAttribute("rolenamecode", settings.getRolename());
.......
}
Then based on your role or other parameters (th:if) you can load your links in you page
I have just started using spring data MongoDb with Spring-Boot.
I have some mongo based json queries added in the interface using #query annotation when using spring data repository.
I want to know if it is possible to externalize or separate out the JSON query outside the codebase so that it can be optimized separately and
also not having it mixed with code.
Thanks for your suggestions.
This is the code which i have added in my interface and annotated with #query annotation.
#Query("{ 'firstname' : ?0 ,'lastname': ?1}")
List findByCriteria(String firstname,String lastname);
The above is a simple example. I have complex conditions involving $and and $or operators too .
What i basically want to achieve is externalize the above native mongo json query to a config file and refer that in the above annotation.
Spring data supports something similar when using jpa with hibernate. But not sure if we can do the same using spring data mongodb with spring boot.
Do like this (I am explaining only for the API)
Suppose you have an Entity user
At the Top there will be User domain
public class User extends CoreDomain {
private static final long serialVersionUID = -4292195532570879677L;
#Length(min = 2)
private String name;
#Length(min = 2)
#UniqueUserName(message = "User name already registered,Please choose something Different")
private String userName;
#Length(min = 6)
private String password;
}
User Controller
User Service (Interface)
User ServiceImpl(Service Implementation)
Mongo Repository(Since, I have MongoDb)
Now in userController you will take all the queries , Param(Parameters) , Pagerequest like this
public class UserController extends CoreController {
#Autowired
private UserService userService;
/*
* This controller is for getting the UserDetails on passing the UserId in
* the #param Annotation
*/
#GET
#Path("{id}")
public User getUser(#PathParam("id") String UserId) {
User user = new User();
user = userService.findUserId(UserId);
if (user == null)
throw new NotFoundException();
log.info("The userId you searched is having the details as :" + user);
return user;
}}
For serviceInterface you will have :
public interface UserService {
// Boolean authenticateUser(User user);
User findUserId(String UserId);
}
For serviceImpl :
public class UserServiceImpl implements UserService {
#Setter
#Autowired
private UserRepository userRepository;
/*
* This method will find user on the basis of their userIds passed in the
* parameter.
*/
#Override
public User findUserId(String UserId) {
User userIdResult = userRepository.findOne(UserId);
log.info("The userDetail is" + userIdResult);
return userIdResult;
}
In mongoRepository for user we will have:
A default query findById(String userId);
Hopefully this will help you.
I seem to be baffled on how JPA Repositories are suppose to work.
In a nut-shell
#Entity
public class User extends AbstractEntity {
protected final static String FK_NAME = "USER_ID";
#Column(nullable = false)
private String firstName;
#OneToMany(cascade = ALL, fetch = FetchType.LAZY, orphanRemoval = true)
#JoinColumn(name = "userId")
private List<Detail> details = new ArrayList<Detail>();
}
#Entity
public class Detail extends AbstractEntity {
Long userId;
String hello;
}
#Repository
public interface UserRepository extends CrudRepository<User, Long> {
User findByFirstName(#Param("firstName") String firstName);
}
And here is the only controller in the app:
#RestController
public class Home {
#Autowired
UserRepository userRepository;
#Autowired
DetailsRepository loanRepository;
#RequestMapping(value = "")
public HttpEntity home() {
User user = userRepository.findByFirstName("John");
if (user == null) {
user = new User();
user.setFirstName("John");
}
Detail detail = new Detail();
detail.setHello("Hello Msh");
user.getDetails().add(detail);
userRepository.save(user);
return new ResponseEntity("hi", HttpStatus.OK);
}
}
Below a screenshot from debugging session where the app just started and the get request to home() method creates new user, new detail, adds detail to user.
Below example - when the user is saved, the detail entity gets updated
Now on the next request, the old user John is found and has been added a new instance of detail.
The old user has been saved but now the newly created detail does not get updated outside.
How come this only works first time ?
Basically theres so much fail going on so that I would advise you to go a step backwards. If youre wana go the short path of getting a solution for exactly this problem continue reading ;)
First part related to the answer of Jaiwo99:
As I can see in the gradle view of intellij, your using Spring Boot. So it is necessary to place #EnableTransactionManagement on top of your configuration class. Otherwise the #Transacion annotation does not have any effect.
Second part your JPA/Hibernate model mapping. Theres so much bad practise on the net that it is no wonder that most beginners have troubles starting with it.
A correct version could look like (not tested)
#Entity
public class User extends AbstractEntity {
#Column(nullable = false)
private String firstName;
#OneToMany(cascade = ALL, fetch = FetchType.LAZY, orphanRemoval = true, mappedBy="user")
private List<Detail> details = new ArrayList<Detail>();
public void addDetail(Detail detail) {
details.add(detail);
detail.setUser(user);
}
}
#Entity
public class Detail extends AbstractEntity {
#ManyToOne
private User user;
private String hello;
public void setUser(User user){
this.user = user;
}
}
Some general advice related to creating a model mapping:
avoid bi-directional mappings whenever possible
cascade is a decision made on the service level and not at the model level and can have huge drawbacks. So for beginners avoid it.
I have no idea why people like to put JoinColumn, JoinTable and whatever join annotation on top of fields. The only reason to do this is when you have a legacy db (my opinion). When you do not like the names created by your jpa provider, provide a different naming strategy.
I would provide a custom name for the user class, because this is in some databases a reserved word.
Very simple, the first time you saved a new entity outside of hibernate session, the second time, the user object you got is a detached object, by default hibernate will not consider it is changed in this case.
*solution *
Move this logic to another service class, which annotated with #transactional
Or
Annotate your controller with transactional
Or
Override equals and hashCode method on user class may also help