Hibernate Model: set default #ManyToOne value of a Child Class - java

I have class User which has #ManyToOne field Role.
#Entity
#Table(name="USERS")
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
#DiscriminatorValue("ROLE_ADMIN")
#DiscriminatorColumn (name="ROLENAME", discriminatorType= DiscriminatorType.STRING, length=20)
public class User extends BaseEntity implements UserDetails {
// all others fields like username, password,...
#ManyToOne
#JoinColumn(name = "role_id", referencedColumnName="id")
#NotNull(message = "ROLE field is mandatory")
private Role role;
//getter and setter
}
I have many classes that extends User: UserSeller, UserClient, UserOffice....
#Entity
#DiscriminatorValue("ROLE_SELLER")
#AttributeOverride(name = "role", column = #Column(name = "role_id"))
public class UserSeller extends User {
//additional fields like CompanyId,...
//getter & setter
}
I have one panel where i can insert / edit / delete all kind of users,
but i must have also "n" panel: one for each kind of user.
When i'm in these panel, i would like to be able to insert these UserSeller without having
to put a where to choose role, but i want set this role as default.
• I tried to put a costructor in the UserSeller
#Entity
#DiscriminatorValue("ROLE_SELLER")
#AttributeOverride(name = "role", column = #Column(name = "role_id"))
public class UserSeller extends User {
#Transient
RoleService roleService;
#Transient
User user = new User();
public UserSeller()
{
super();
try
{
this.setRole(roleService.getRole("ROLE_SELLER"));
}
catch (RecordNotFoundException e)
{
}
}
but i get this error:
Could not get constructor for org.hibernate.persister.entity.SingleTableEntityPersister
• i tried to pass a User object to constructor:
public UserSeller(User user)
and in controller i do this:
User user = new User();
UserSeller seller = new UserSeller(user);
model.addAttribute("seller", seller);
but i get this error:
No default constructor for entity: com.machinet.model.UserVenditore
If, in UserSeller i declare again the Role field i get error about
"Redefined Column"....
Finally i found this that i though could be my solution (in
UserSeller class):
#PrePersist
public void prePersist() {
try
{
this.setRole(roleService.getRole("ROLE_SELLER"));
}
catch (RecordNotFoundException e)
{
}
}
but when i'm in the UserSeller panel and i try to add a new Seller, it doesnt' take the default role and the validation fails.
I would like to know how can i do the trick: i want that my UserSeller, UserClient,... have a default value when i insert a new record.
Do i really need to do this in the controller? is this the only way? because for a beginner like me, it doesn't look so so elegant solution:
UserVenditore venditore = new UserVenditore();
try
{
venditore.setRole(roleService.getRole("ROLE_VENDITORE"));
}
catch (RecordNotFoundException ex)
{
}
model.addAttribute("venditore", venditore);
edit: also this last solution doesn't work: it fails the validation!
Thank you for any suggestion!

for now, i found only this solution (in the ControllerClass):
in removed #Valid before #ModelAttribute
i manually set the role
i manually validate the object
#RequestMapping(value = "/add", method = RequestMethod.POST)
public String addingSeller(#ModelAttribute UserSeller seller,
BindingResult result, RedirectAttributes redirectAttrs) {
logger.info("IN: Seller/add-POST");
//set role
try
{
seller.setRole(roleService.getRole("ROLE_SELLER"));
}
catch (RecordNotFoundException ex)
{
}
//validation
validator.validate(seller, result);
if (result.hasErrors()) {
logger.info("Seller-add error: " + result.toString());
redirectAttrs.addFlashAttribute("org.springframework.validation.BindingResult.seller", result);
redirectAttrs.addFlashAttribute("seller", seller);
} else {
try
{
SellerService.addSeller(seller);
//message to the user
String message = "Seller added!";
redirectAttrs.addFlashAttribute("message", message);
redirectAttrs.addFlashAttribute("message_class", "alert-success");
}
catch (DuplicateRecordException e)
{
//the username already exists
//message to the user
String message = "Already exists an user with this USERNAME";
redirectAttrs.addFlashAttribute("message", message);
redirectAttrs.addFlashAttribute("message_class", "alert-danger");
redirectAttrs.addFlashAttribute("seller", seller);
}
}
return "redirect:/sellers/list";
}

Related

SpringBoot: Persist nested JSON [using spring-boot-starter-data-rest + sql]

I have the following POST request:
{
"name": "Peter",
"lastName": "Smith",
"contact": {
"phone":"12345679",
"email": "peter#smith.com"
}
}
And I would like to store that in a SQL DB as follow:
| id (int) | name (varchar) | lastName (varchar) | contact (JSON) |
I'm using spring-boot-starter-data-rest so I only have the UserRepository and User Entity, which has an Embedded property contact
User.java
#Entity
#Table(name="user")
public class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(name = "name")
private String title;
#Column(name = "lastName")
private String lastName;
#Embedded
#Column(name = "contact")
private Contact contact;
}
Contact.java
#Embeddable
public class Contact {
private String phone;
private String email;
}
UserRepository.java
public interface UserRepository extends JpaRepository<User, Integer> {
//
}
If I make a POST request I get an error, because (guess) I'm not converting Contact to JSON.
I've already tried adding a #Convert(converter = HashMapConverter.class) but I get an error.
HashMapConverter
public class HashMapConverter implements AttributeConverter<Object, String> {
private static final ObjectMapper om = new ObjectMapper();
#Override
public String convertToDatabaseColumn(Object attribute) {
try {
return om.writeValueAsString(attribute);
} catch (JsonProcessingException ex) {
//log.error("Error while transforming Object to a text datatable column as json string", ex);
return null;
}
}
#Override
public Object convertToEntityAttribute(String dbData) {
try {
return om.readValue(dbData, Object.class);
} catch (IOException ex) {
//log.error("IO exception while transforming json text column in Object property", ex);
return null;
}
}
}
i've got the same case for storing json field which is working perfectly. Please try :
Add dependency to pom.xml :
<dependency>
<groupId>com.vladmihalcea</groupId>
<artifactId>hibernate-types-52</artifactId>
<version>2.9.7</version>
</dependency>
Edit User class within :
#Entity
#TypeDef(name = "json", typeClass = JsonStringType.class)
public class User {
// other field here
#Type(type = "json")
#Column(columnDefinition = "json")
private Contact contact;
// getters, setters
}
Of course your database should support json type. For MariaDB for example you can refer to https://mariadb.com/kb/en/json-data-type/
You need to create an entity for Contact and then create a one to one relationship between the two. Check out this example.

how to receive a json object with #RequestBody or #RequestParam

First of all I apologize, I am new using this technology and I really have many doubts.
I am trying to send a json object to my controller class, the problem is that with #RequestBody all the data arrives but the foreign keys arrive null. Example enter a new user with the id of a role that already exists in the BD, the user data arrives complete but the role ID arrives null
First I register the data of the role via POST and everything works perfectly with #RequestBody, but when I try to register a user with the role_id that is already saved in the database also using #RequestBody it is saved with the id_rol null
My user entity:
#Entity
public class Usuario implements Serializable{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
private String correo;
private String pass;
private boolean activo;
#OneToOne(optional = false, cascade = CascadeType.ALL, fetch = FetchType.LAZY)
#JoinColumn(name="rol_id", nullable = false)
private Rol rol;
public Usuario(){}
public Usuario(String correo, String pass, boolean activo, Rol rol) {
this.correo = correo;
this.pass = pass;
this.activo = activo;
this.rol = rol;
}
/*getter y setter*/
My role entity:
#Entity
//#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
public class Rol implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
private String nombre;
private boolean activo;
#OneToMany(mappedBy = "rol")
private List<Usuario> usuarios;
public Rol(){}
public Rol(String nombre, boolean activo, List<Usuario> usuarios) {
this.nombre = nombre;
this.activo = activo;
this.usuarios = usuarios;
}
/*getter y setter*/
User Repository:
#Repository
public interface UsuarioRepository extends JpaRepository<Usuario, Serializable> {
public abstract Usuario findByCorreo(String correo);
}
Role Repository:
public interface RolRepository extends JpaRepository<Rol, Serializable> {
}
User controller class:
#RestController
#RequestMapping("/v1")
public class UsuarioController {
#Autowired
UsuarioRepository usuarioRepository;
#PostMapping("/usuario/add")
public #ResponseBody ResponseEntity<String> crear(#Valid #RequestBody Usuario usuario) {
try{
usuarioRepository.save(usuario);
return new ResponseEntity<String>("Registro Exitoso..", HttpStatus.OK);
}catch (Exception e){
return new ResponseEntity<String>("Ha ocurrido un Error..", HttpStatus.BAD_REQUEST);
}
}
}
role controller class:
#RestController
#RequestMapping("/v1")
public class RolController {
#Autowired
RolRepository rolRepository;
#PostMapping("/rol/add")
public #ResponseBody ResponseEntity<String> crear(#Valid #RequestBody Rol rol) {
try{
rolRepository.save(rol);
return new ResponseEntity<String>("Registro Exitoso..", HttpStatus.OK);
}catch (Exception e){
return new ResponseEntity<String>("Ha ocurrido un Error..", HttpStatus.BAD_REQUEST);
}
}
}
This is what I send to my controller:
{
"correo": "pepito#gmail.com",
"pass": "1234",
"activo": "true",
"rol_id": "5"
}
And this is what I receive:
{
"correo": "pepito#gmail.com",
"pass": "1234",
"activo": true,
"rol_id": null
}
How should I receive this body with id_rol = 5?
I know that I am doing something wrong in #RequestBody, I appreciate any example you can provide as I have searched and I have not found that. Thank you..!
This is because you are sending a wrong JSON structure to your REST Controller.
In your Usuario class, the role_id is actually an object field with a name rol which represents Rol class.
So you need to pass Rol with id as an JSON object in your request:
{
"correo": "pepito#gmail.com",
"pass": "1234",
"activo": "true",
"rol": {
"id":"5"
}
}

How to pass an object to a ModelAttrbiute in MockMVC post?

User model:
#Entity
#Table(name="user")
public class User {
#Id #GeneratedValue(strategy=GenerationType.IDENTITY)
private Integer id;
#NotBlank
#Column(name="username")
private String username;
#NotEmpty
#ManyToMany(fetch = FetchType.EAGER)
#JoinTable(name="user_role", joinColumns = {#JoinColumn(name="user_id")},
inverseJoinColumns = {#JoinColumn(name="role_id")})
private Set<Role> roles;
}
Controller:
#RequestMapping(value = {"/users/edit/{id}"}, method = RequestMethod.POST)
public String editUser(ModelMap model, #Valid #ModelAttribute("user") User user, BindingResult result) {
if(result.hasErrors()) {
return "AddUserView";
}
return "redirect:/users";
}
Test with MockMVC:
#Test
public void performUpdateUserTest() throws Throwable {
mockMvc.perform(post("/users/edit/{id}", user.getId())
.param("username", "User"));
}
Well, fine, I can pass a param username as always using param(). But what should I do with ROLES? This field is a separate object. I can't pass it using param(). Then how is it possible to pass it in the test?
The only way out I found is to create an entity and pass it using .flashAttr():
#Test
public void performUpdateUserTest() throws Throwable {
User user = new User("User", new HashSet<Role>(Arrays.asList(new Role("USER"))));
mockMvc.perform(post("/users/edit/{id}", user.getId())
.flashAttr("user", user));
}
But then, what if I need to test that user can't be updated because of binding error in the ROLES field(ROLES can't be null, and suppose, it was set as null)? Thus, I'm not able to create user(and use it with .flashAttr) already with a binding error as the exception will be thrown. And I still have to pass it separately.
Well, after a long time of searching, I found out that I should add a converter to the MockMVC. What converter is you can read HERE, for instance.
I had it already in my project but didn't realize that it didn't work with MockMVC.
So, you can add the converter to MockMVC like that:
#Autowired
private StringToRoleConverter stringToRoleConverter;
#Before
public void init() {
FormattingConversionService cs = new FormattingConversionService();
cs.addConverter(stringToRoleConverter);
mockMvc = MockMvcBuilders.standaloneSetup(userController)
.setConversionService(cs)
.build();
}
Converter itself:
#Component
public class StringToRoleConverter implements Converter<String, Role> {
#Autowired
private RoleService roleService;
#Override
public Role convert(String id) {
Role role = roleService.findById(Integer.valueOf(id));
return role;
}
}
And then I can add param like that:
mockMvc.perform(post("/users/edit/{id}", user.getId())
.param("roles", "2"))
though I'm passing a string there, it will be converter to Role with the help of Spring converter.

How to make this messaging system work as desired

My requirement is quite straightforward, I've admin panel through that admin sends messages to all registered users of the System, User and Message Entity relationship is:
#Entity
public class Message implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
private String message;
#Temporal(TemporalType.TIMESTAMP)
private Date sentDate;
#ManyToMany(mappedBy = "messages", cascade = {CascadeType.MERGE})
private List<User> users;
// setter - getters methods are omitted
}
And:
#Entity
public class User implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long userId;
private String userName;
#Temporal(TemporalType.TIMESTAMP)
private Date userJoinedAt;
#ManyToMany(cascade = { CascadeType.PERSIST })
#JoinTable
private List<Message> messages;
// setter - getters methods are omitted
}
At this point I want that when a new user signs-up in the System, he should only see the admin sent messages that are sent after his sign-up time. I've tried:
public void addMessage(Message message) throws Exception {
message.setMessageSentDate(new Date());
messageService.save(message);
List<Message> messages = messageService.getAll();
List<User> registeredUsers = userService.getAll();
for (User user : registeredUsers) {
for (Message savedMessage : messages) {
if(user.getUserJoinedAt().before(savedMessage.getMessageSentDate())){
user.setMessages(messages);
userService.save(user);
}
}
}
}
But this method is not working as desired, means it adds all the messages present in Messages Entity, even if those are sent before User Joined Date.
How can I make that possible to get desired job don?
UPDATE
public void addMessage(Message message) throws Exception {
message.setMessageSentDate(new Date());
messageService.save(message);
List<Message> messages = messageService.getAll();
List<User> registeredUsers = userService.getAll();
for (User user : registeredUsers) {
// Initializing new variable to store afterJoinedMessages
List<Message> afterJoinedMessages = new CopyOnWriteArrayList<Message>();
for (Message savedMessage : messages) {
if(user.getUserJoinedAt().before(savedMessage.getMessageSentDate())){
// Here adding those messages to afterJoinedMessages List
afterJoinedMessags.add(savedMessage);
// and saving those to user
user.setMessages(afterJoinedMessags);
userService.save(user);
}
}
}
}
Would this be a better solution or the one which is given in answer?
You are adding the whole messages list whenever any of the messages matches the date condition.
Change the call user.setMessages(messages); to a new one you need to create in the User class (addMessage) which simply adds the message to the user messages list. (You can also save the user only once at the end of the loop, not in every iteration).
for (Message savedMessage : messages) {
if(user.getUserJoinedAt().before(savedMessage.getMessageSentDate())){
user.addMessage(savedMessage);
}
}
userService.save(user);
In your User class add something like :
public void addMesage(Message msg) {
messages.add(msg);
}
UPDATE
I am not aware of all the details, but as the message is being created with the current date, I'd expect it will need to be added to ALL existing users (which should have been registered before now).
So .... I'd leave the method like this : (just save the message and add it to all the users).
public void addMessage(Message message) throws Exception {
message.setMessageSentDate(new Date());
messageService.save(message);
List<User> registeredUsers = userService.getAll();
for (User user : registeredUsers) {
user.addMessage(message);
userService.save(user);
}
}

after setting attribute for HttpSession it becomes Null

I'm developing a Sign up operation, where the user enter his email and password press the submit(createUser method) button, User entity get persisted AND set the id of User in HttpSession, next he moved to next jsf where enter information for School Object (Entity). My problem is that the User get persisted BUT the School is not. here is my code:
public CreateBn() {
user = new User();
school = new School();
adress = new Adresse();
school.setAdresse(adress);
facesContext = FacesContext.getCurrentInstance();
session = (HttpSession) facesContext.getExternalContext().getSession(false);
}
public String createUser() {
initialiserDateInscription();
session.setAttribute("UserId", user.getId());
//System.out.println((BigInteger) session.getAttribute("UserId"));
userPr.createUser(user);
return SHCOOL_INSCRIPTION;
}
public String createSchool() {
BigInteger userId = (BigInteger) session.getAttribute("UserId");
System.out.println("MEHDI : " + userId);
try {
User userTemp = userPr.getUserById(userId);// Here is the problem
school.setUser(userTemp);
} catch (Exception e) {
e.printStackTrace();
}
session.setAttribute("SchoolId", school.getId());
school.setAdresse(adress);
schoolPr.createSchool(school);
return INSCRIPTION_RETURN;
}
as you can see I get the User entity based on the UserId saved in session, but I get nothing, it says:
javax.persistence.NoResultException: getSingleResult() did not retrieve any entities.
any more info, I'm here. So how can I solve this??
#Entity
#Table(schema = "school", name = "school")
public class School implements Serializable {
private static final long serialVersionUID = 1L;
#Id
private BigInteger id;
private String name;
#OneToOne(fetch=FetchType.EAGER)
#JoinColumn(name = "userId")
private User user;
#OneToOne(fetch=FetchType.LAZY, cascade=CascadeType.PERSIST)
#JoinColumn(name = "adressId")
private Adresse adresse;
EJB
#Stateless
public class UserPr {
#PersistenceContext(unitName = "proj")
private EntityManager em;
public void createUser(User user) throws RuntimeException{
try {
em.persist(user);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public User getUserById(BigInteger UserId) throws Exception{
try{
return em.createNamedQuery("User.findById", User.class).setParameter("id", UserId).getSingleResult();
}catch(Exception e){
e.printStackTrace();
}
return null;
}
}
#NamedQueries({
#NamedQuery(name="User.findById", query="SELECT u FROM User u WHERE u.id = :id")
})
public class User implements Serializable {
Try changing the way you get and set the userId.
instead of this line
session.setAttribute("UserId", user.getId());
try this
ExternalContext externalContext = FacesContext.getCurrentInstance().getExternalContext();
Map<String, Object> sessionMap = externalContext.getSessionMap();
sessionMap.put("UserId", user.getId());
and instead of this
BigInteger userId = (BigInteger) session.getAttribute("UserId");
try this
BigInteger userId = (BigInteger) sessionMap.get("UserId");
First some remarks about the mode: I don't think School should have a OneToOne relationship with User. More likely it is a OneToMany relationship. And maybe you don't want to have a separate Entity for Adresse, it could probably be an Embeddable instead.
How is your persistence set up? How are you managing transactions? The problem you see might be caused by transactional boundaries. How are the createUser and createSchool methods called exactly?

Categories

Resources