I have an existing database modeled the following way:
users - Id(PK), login, password
locales - Id(PK), descripcion, direccion, etc...
users_locales - user_id(K), local_id(K)
and entity classes as follow
User
#Entity
#Table(name="users")
public class User {
#Id
#GeneratedValue
private Integer id;
private String login;
private String password;
#ManyToMany(cascade = CascadeType.ALL)
#JoinTable(name = "user_locales", joinColumns = {#JoinColumn(name = "user_id") }, inverseJoinColumns = { #JoinColumn(name = "local_id") })
private List<Local> userLocales = new ArrayList<Local>();
Local
#Entity
#Table(name="locales")
public class Local {
#Id
#Column(name="id")
#GeneratedValue
private Integer id;
#Column(name="descripcion")
private String descripcion;
#Column(name="direccion")
private String direccion;
#ManyToMany(cascade = CascadeType.ALL)
#JoinTable(name = "user_locales", joinColumns = {#JoinColumn(name = "local_id") }, inverseJoinColumns = { #JoinColumn(name = "user_id") })
private List<User> localesUser = new ArrayList<User>();
I need to use a option select in a jsp page that displays a list of "locals" partners the user logged in, but i cant deploy it correctly.
This is not fully developed and I'm doing one or more things wrong, because i can't fully understand how hibernate and Spring security works.
Would need to create a service in "UserService" that generated the list and this is what I need help.
I add some code snippets that I think could be needed for this task.
userService
public interface UserService {
public User getUser(String login);
}
localServiceImpl
#Transactional
public List<Local> listLocal() {
return localDAO.listLocal();
}
//This just show a list of all Local objects in database
localDAOImpl
#Repository
public class LocalDAOImpl implements LocalDAO {
#Autowired
private SessionFactory sessionFactory;
public List<Local> listLocal() {
return sessionFactory.getCurrentSession().createQuery("from Local")
.list();
}
}
controller
#RequestMapping(value="/index", method=RequestMethod.GET)
public ModelAndView indexPage( Map<String, Object> map ) {
map.put("localList", localService.listLocal());
return new ModelAndView("home");
}
//Now its pointing to localService and this list all objets in database
The home.jsp file
<select label="Locales" array="Locales" name="Locales">
<c:forEach items="${localList}" var="local">
<option value="${local.localidad}">
<c:out value="${local.descripcion}"/>
</option>
</c:forEach>
</select>
How could i do a list of "locales" related to the current user session? sorry for possible spelling or grammatical mistakes.
At the moment you are selecting the whole table. You need to modify localDAO.listLocal() and pass the login as parameter then modify your query and use that parameter to limit the output records:
public List<Local> listLocal(String userLogin) {
Query query = sessionFactory.getCurrentSession()
.createQuery("select u.userLocales from User u where u.login = ?");
query.setParameter(0, userLogin);
return query.list();
}
Hibernate, Spring or whatever framework you use, don't limit data automatically. You need to take care of limiting retrieved data for current user yourself.
Related
I am trying to update the parent entity and child entity (comes with parent) using JPA. I tried many ways but I couldn't do it. It gives me the following error.
javax.persistence.EntityExistsException: A different object with the
same identifier value was already associated with the session Contact(6)
Following is the entity,
Parent -> User.java
#Entity
public class User extends Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "seq_generator")
#SequenceGenerator(name = "seq_generator", sequenceName = "seq_users", initialValue = 1, allocationSize = 1)
private Long id;
private String firstName;
private String lastName;
private String email;
private String password;
private String street;
private String city;
#OneToOne(cascade = CascadeType.ALL, mappedBy = "userContact")
private Contact contact;
}
Child -> Contact.java
#Entity
public class Contact extends Serializable {
private static final long serialVersionUID = 1L;
#Getter(value = AccessLevel.NONE)
#Setter(value = AccessLevel.NONE)
#Id
#Column(insertable = false, updatable = false)
private Long id;
#MapsId
#OneToOne
#JoinColumn(name = "id", nullable = false)
private User userContact;
private String phone;
}
In my Service.java I am updating like this,
#Override
#Transactional
public User update(Long id, User user) {
Optional<User> curUser = userRepo.findById(id);
user.setId(curUser.get().getId());
user.setStreet(curUser.get().getStreet());
user.setCity(curUser.get().getCity());
Contact contact = user.getContact();
contact.setUserContact(user);
user.setContact(contact);
return userRepo.save(user);
}
And the update JSON payload is like this,
{
"firstName": "FirstName",
"lastName": "LastName",
"email": "Email",
"password": "Password",
"contact": {
"phone": "0123456789"
}
}
So why I can't update this at once? The insert is working fine but this update is not working. What I am doing wrong here? I really appreciate it if anybody can help me. Thanks in advance.
Here is the new way I tried,
#Override
#Transactional
public User update(Long id, User user) {
Optional<User> curUser = userRepo.findById(id);
curUser.setFirstName(user.getFirstName());
curUser.setlastName(user.getLastName());
curUser.setEmail(user.getEmail());
curUser.setPassword(user.getPassword());
Contact contact = user.getContact();
contact.setUserContact(curUser);
curUser.setContact(contact);
return userRepo.save(curUser);
}
Because you have the same user (i.e. same id) represented twice in the persistence context.
This line loads the user curUser in the persistence context.
Optional<User> curUser = userRepo.findById(id);
This line makes user the same user by id, but it is a different instant.
user.setId(curUser.get().getId());
And finally this line tries to add user to the persistence context.
userRepo.save(user);
This would result with the two instances being attached to the persistence context while representing the same user with the same id which would result in an inconsistent state since JPA wouldn't know which version to persist. Therefore this is forbidden and throws the exception you are seeing.
In your case changing
Optional<User> curUser = userRepo.findById(id);
user.setId(curUser.get().getId());
to
user.setId(id);
should do the trick.
Regarding your comments:
The same problem exists for referenced objects. You'll have to make sure that you: either load an entity and change it's attribute, or do NOT load the entity, not directly nor indirectly via a reference and save a detached entity, constructed from your request.
If you load an entity and then try to save a detached instance of it, you will get this kind of error.
i'm have two model class.
one of them is user and other is degree.
each user can get more than one degree.
I want to send list of degrees along with Json when I create a new user in postbody.
my user class like this:
#Entity
#Table(name = "USERS")
public class User implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
private String name = "";
#ManyToMany(cascade = CascadeType.ALL)
#JoinTable(
name = "USER_DEGREE",
joinColumns = #JoinColumn(name = "USER_FK"),
inverseJoinColumns = #JoinColumn(name = "DEGREE_FK")
)
private List<Degree> degreeList = new ArrayList<>();
public User setDegreeList(List<Degree> degreeList) {
this.degreeList = traitList;
return this;
}
public User setId(long id) {
this.id = id;
return this;
}
public User setName(String name) {
this.name = name;
return this;
}
}
and degree class have 3 attribute id, title, point.
in my controller i want when use #RequestBody for get user json in body, get all user degrees.
for example my controller :
#RequestMapping(value = "/user/add",method = RequestMethod.POST)
#ResponseBody
public Object sendTechnicalMessage(
HttpServletRequest request,
#RequestBody User user
){
return userService.createNewUser(request,user);
}
and my json body like this :
{
name:"abc",
degreeList:[1,2,4,6] // or [{id:1},{id:2},{id:4}]
}
how can do this ?
2 ways:
You can create a DTO class with field Set<Long> instead List<Degree>, convert User object to this UserDTO object and return it.
You can use this User class but with a specific Serializator. For this annotate the field with #JsonSerialize(using = SomeSerializer.class) and implement this serializer implementing JsonSerializer<Long> (or Set<Long> - I cannot say now, this is just idea).
Note: remember, that #ManyToMany fields by default are lazy (and almost always must be lazy) so use #Transactional to get a collection without exception.
I need to check if user's email is already added to list of students and if so, connect this user with course.
java.lang.UnsupportedOperationException
at java.util.Collections$1.remove(Collections.java:4684)
at java.util.AbstractCollection.clear(AbstractCollection.java:436)
at org.hibernate.collection.internal.PersistentSet.clear(PersistentSet.java:318)
at org.hibernate.type.CollectionType.replaceElements(CollectionType.java:581)
at org.hibernate.type.CollectionType.replace(CollectionType.java:757)
at org.hibernate.type.TypeHelper.replace(TypeHelper.java:167)
User Entity
#ManyToMany(cascade = CascadeType.MERGE)
#JoinTable(
name = "Student_Courses",
joinColumns = {#JoinColumn(name = "student_id")},
inverseJoinColumns = {#JoinColumn(name = "course_id")}
)
private Set<Course> availableCourses = new HashSet<>();
Course Entity
#ManyToMany(mappedBy = "availableCourses")
private Set<User> users = new HashSet<>();
UserService
public void bindStudentWithCoursesAfterRegistration(String email) {
User user = userRepo.findFirstByEmail(email);
List<CourseStudentEmails> studentEntriesInCourses = courseStudentEmailsRepo.findAllByEmail(email);
if (studentEntriesInCourses.size() != 0){
for (CourseStudentEmails entry : studentEntriesInCourses) {
Course course = entry.getCourse();
user.getAvailableCourses().add(course);
}
}
userRepo.save(user);//Exception throws here
}
Registration Controller
#PostMapping("/registration")
public String addUser(User user, #RequestParam(value = "checkboxTeacher", required = false) String checkboxValue, Model model) {
[//chek if user is already registered]
userRepo.save(user);
courseService.bindStudentWithCoursesAfterRegistration(user);
return "redirect:/login";
}
I guess you're missing a proper query to find appropriate CourseStudentEmails.
If you keep a collection of String as emails in your entity (CourseStudentEmails), you can query entities based on specific email as this:
#Query("SELECT c FROM CourseStudentEmails c WHERE ?1 member of c.emails")
List<CourseStudentEmails> findAllByEmail(String email);
I have a user page where I am viewing a specific users details. I want to be able to add this user as a "friend". I sort of understand the technical details I will need to do to add this user as a friend, I am just unsure how to pass the jsp value to the controller.
This is what my friend entity looks like:
#Entity
#Table(name = "friend")
public class Friend {
#Id
#GeneratedValue
private int id;
#Column(name = "friend_username")
private String friend_username;
#ManyToOne(optional=false)
#JoinColumn(name = "friend_username", referencedColumnName="username", insertable=false, updatable=false)
private User user;
public Friend() {
this.user = new User();
}
public Friend(int id, String friend_username, User user) {
super();
this.id = id;
this.friend_username = friend_username;
this.user = user;
}
In my JSP file I am viewing a user's details like this:
<tr>
<td><b>Username:</b></td>
<td>${user.username}</td>
</tr>
<tr>
<td><b>Email:</b></td>
<td>${user.email}
<tr>
<td><b>Name:</b></td>
<td>${user.name}
How could I pass the user.username value to the controller? I know i'll be able to get the current username by using Principal. If anyone could help or has any examples I would much appreciate it.
I have a 'User' class:
public class User implements Serializable {
#Id
#GeneratedValue
int id;
String nome;
#Column(unique = true)
String email;
#ManyToMany
#JoinTable (name = "user_roles", joinColumns=
{ #JoinColumn (name = "user_id")}, inverseJoinColumns=
{ #JoinColumn (name = "role_id")})
private List<Role> roles;
I have a 'Role' class:
public class Role implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue
int id;
#Column(unique = true)
String role;
Relationship :An User has roles, and roles has users. I have a many-to-many relationship here. The relation is unidirectional, and my dominant entity is User.
In my database has a table named "user_roles", created automatically. I have a User registered with two roles.
When I retrieve the User from my database using a "UserDao" class, and I try to access the roles of the user, I get NullPointerException. I got all others informations (name, email, etc), but the roles I can not access.
My 'UserDao' class:
public class UserDaoImpl implements UserDao{
private static final transient Logger log = LoggerFactory.getLogger(UserDaoImpl.class);
private final String HIBERNATE_CFG = "data.cfg.xml";
#Override
public int insertUser (User user){
int code = 0;
Session session = new HibernateUtil(HIBERNATE_CFG).getSessionFactory().getCurrentSession();
try {
session.beginTransaction();
session.save(user);
session.getTransaction().commit();
code = 1;
}
catch (Exception e) {
log.error(e.getMessage());
session.getTransaction().rollback();
code = 0;
}
return code;
}
#Override
public List<User> getAll() {
Session session = new HibernateUtil(HIBERNATE_CFG).getSessionFactory().getCurrentSession();
List<User> users = null;
try
{
session.beginTransaction();
String queryString = "SELECT * FROM USER;";
SQLQuery consulta = session.createSQLQuery(queryString);
consulta.setResultTransformer(Transformers.aliasToBean(User.class));
users = (List<User>) consulta.list();
session.getTransaction().commit();
}
catch(ConstraintViolationException e)
{
log.error(e.getMessage());
session.getTransaction().rollback();
}
return users;
}
I tried using iterator:
List roles = user.getRegras();
for (Iterator it = roles.iterator(); it.hasNext();){
Role r = (Role) it.next();
out.println("Role:" + r.getRole());
}
I tried to instantiate the variable "roles" in the 'User' class like below. But every time the variable returns size 0:
#ManyToMany
#JoinTable (name = "user_roles", joinColumns=
{ #JoinColumn (name = "user_id")}, inverseJoinColumns=
{ #JoinColumn (name = "role_id")})
private List<Role> regras = new ArrayList<Role>();
I tried getting the roles from Object User, but I receive a NullPointerException when the method size() is called.
List<Role> roles = (List<Role>)user.getRoles();
out.println("size roles: " + roles.size());
I'm trying to acess my table 'user_roles' to retrieve the data from database, but I can't dothis. Can anyone help me?
Thanks in Advance
You're using a SQL query to load the users, and mapping the result to the entity. So what you get is detached entities, containing only what the SQL query returned.
Learn to use JPQL/HQL, and to load entities, which will then be managed:
String queryString = "SELECT u FROM User u";
Query consulta = session.createQuery(queryString);
users = (List<User>) consulta.list();