Is it possible to select whole Java Object from Spring Form's drop down menu? I used LinkedHashMap, but it doesn't work.
I have relation Many To One between table Agent and table Roles (every Agent has one role eg. user, admin). I use hibernate so I have to operate on Object, not Id's from database. My problem is that I want to create drop down menu with list of all roles from my database and when I pick one element, this Object goes to my Agent Object and save in my database.
I have List with my Roles Objects
List<Roles> rolesList = rolesService.getAllRoles();
Which comes from this:
public List<Roles> getAllRoles() {
return session().createQuery("from Roles").list();
}
and I tried something like this:
In my AgentController:
#RequestMapping("/createagent")
public String createAgent(Model model) {
Agent agent = new Agent();
List<Roles> rolesList = rolesService.getAllRoles();
Map<Roles, String> rolesMap = new LinkedHashMap<Roles,String>();
for (int i=0; i<rolesList.size(); i++){
rolesMap.put(rolesList.get(i), rolesList.get(i).getRole());
}
model.addAttribute("rolesMap", rolesMap);
model.addAttribute("agent", agent);
return "createagent";
}
In my jsp file:
<tr><td>Roles:</td><td>
<sf:select path="roles" multiple="false">
<sf:options items="${rolesMap}"></sf:options>
</sf:select>
</td></tr>
My Roles Object:
#Entity
#Table(name = "roles")
public class Roles {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "roles_seq_gen")
#SequenceGenerator(name = "roles_seq_gen", sequenceName = "roles_id_seq")
#Column(name = "id")
private long id;
#Column(name = "role")
private String role;
It shows excactly what I want but when I click position in my drop down menu and submit it, my form don't save my Object properly. It nested it ... I don't know how to subscribe this, maybe my toString() function output clear little bit.
Agent [id=0, username=TestUsername, password=TestPassword, roles=Roles[id=0, roles=Roles[id=0, roles=user]] ...
My Agent Object:
#Entity
#Table(name="agent")
public class Agent {
#Id
#GeneratedValue(strategy=GenerationType.SEQUENCE, generator="agent_seq_gen")
#SequenceGenerator(name="agent_seq_gen", sequenceName="agent_id_seq")
#Column(name="id")
private long id;
#Column(name="username")
private String username;
#Column(name="password")
private String password;
#ManyToOne
#JoinColumn(name="roles_id")
private Roles roles;
My jUnit test runs fine, it's something wrong with my Spring Form or with my controllers... I don't know.
I would suggest to use ModelAttribute and reference the form object to the said properties, like, agent.getRoles().
createAgent(#ModelAttribute("agent") Agent agent) should work in what you're trying to accomplish.
Related
I have a entity user with self dependency. When i Map this entity to DTO I have the problem of circular dependency. .
User.class:
public class User {
#Id
#GeneratedValue
private Long id;
#NotNull
#Column(name = "first_name")
private String firstName;
#NotNull
#Column(name = "last_name")
private String lastName;
#JsonBackReference
#ManyToMany(
private List<User> friedns_of = new ArrayList<>();
#ManyToMany(cascade = CascadeType.ALL,
mappedBy = "followers")
private List<User> friends = new ArrayList<>();
UserMapper method in UserMapper:
public static UserResponse toUser(User user) {
UserResponse userResponse = new UserResponse();
userResponse.setId(user.getId());
userResponse.setFollowers(user.getFollowers().stream().map(UserMapper::toUser).toList());
userResponse.setFollowing(user.getFollowing().stream().map(UserMapper::toUser).toList());
return userResponse;
}
When i run the method toUser() I get stackOverFlowError exception caused by the infinite circular dependency. Any advise how to solve this?
One way to resolve this is to model the 'follows' relationship as a separate entity:
#Table(name="user_followers")
public class Follows {
#Id
#GeneratedValue
private Long id;
#NotNull
#Column(name = "follower_Id")
private User follower;
#NotNull
#Column(name = "user_id")
private User user;
}
Then you could give your user two one-to-many lists of these entities:
public class User {
#Id
#GeneratedValue
private Long id;
#OneToMany(mappedBy = "user_id")
private List<Follows> followers;
#OneToMany(mappedBy = "follower_Id")
private List<Follows> following;
}
EDIT: instead of the id field in Follows you could use the user_id and follower_id as a composite primary key using #Embeddable. Omitted here for brevity. See here for more details: https://www.baeldung.com/jpa-many-to-many
Since you already have a DTO of UserResponse, you are on the right path towards a correct solution. My suggestion would be to avoid #ManyToMany on an entity level, and manage followers on a service level.
This means you will have to split relation ManyToMany join column into a separate entity, such as UserFollowsEntity with fields userId and followsUserId. Then remove followers and following lists from your User entity entirely.
Now when creating UserResponse in a service, you will have to
Select the actual user from repository – userRepository.findById(userId)
Select followers – userFollowsRepository.findByFollowsUserId(userId)
Select following – userFollowsRepository.findByUserId(userId)
It is a good practice to try and avoid bidirectional in entities relationships entirely if possible.
EDIT: This will give you two lists: followers and following. You will probably want to know their user names, so what you can do is to merge followers and following lists into one, then extract all user ids from that list. Then query user repository with a list of those IDs, and just attach the required user information to your response model.
Yes it does sound like a bit more work compared to the seeming simplicity of utilizing JPA annotations, but this is the best way to avoid circular dependency as well as decouple the Follower functionality from your user entity.
I know this kind of question was answered many times and there are solutions to it, however, none of them worked for me. I tried #JsonIgnore, #JsonIgnoreProperties #JsonManagedReference/#JsonBackedReference, yet still the debugger shows that user has reference to authority, which has reference to user, which has reference to authority, which has reference to user ... Most importantly it doesn't throw any exceptions. However, I still wonder why does this happen, why it doesn't throw exceptions, and does it affect productivity
My entities are simple: there is a User
#Entity
#Table(name = "users_tb")
#NoArgsConstructor
#Getter
#Setter
public class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String username;
private String password;
#OneToMany(mappedBy = "user", fetch = FetchType.LAZY)
private List<Authority> authorities;
}
and Authority
#Entity
#Table(name = "authorities_tb")
#NoArgsConstructor
#Getter
#Setter
public class Authority {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "user_id")
private User user;
}
the code to retrieve users using JpaRepository<T, V>
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
var user = userRepository.findUserByUsername(username).orElseThrow(
() -> new UsernameNotFoundException(ERR_USERNAME_NOT_FOUND));
return new CustomUserDetails(user);
}
The debugger output state before return from loadUserByUsername:
user = {User#10781}
> id = {Long#10784}
> username = "John"
> password = "$2a$10$xn3LI/AjqicFYZFruSwve.681477XaVNaUQbr1gioaWPn4t1KsnmG"
> authorities = {PersistentBag#10788} size = 2
> 0 = {Authority#10818}
> id = {Long#10784}
> name = "READ"
> user = {User#10784}
> id = {User#10784}
> username = "John"
> password = "$2a$10$xn3LI/AjqicFYZFruSwve.681477XaVNaUQbr1gioaWPn4t1KsnmG"
> authorities = {PersistentBag#10788} size = 2
> 0 = {Authority#10818}
...
Circular dependencies aren't a problem in themselves with JPA.
There are two potential problems with them:
From a software design perspective circular dependencies create a cluster of classes that you can't easily break up.
You can easily get rid of them in your case by making the relationship a unidirectional one and replace the other direction by a query, if you really have to.
Is it worth it in your case?
It depends how closely your two entities are really related.
I'd try to avoid bidirectional relationships, because it is easy to make mistakes, like not keeping both sides of the relationship in sync.
But in most cases I wouldn't sweat it.
Most software as way more serious design issues.
The other problem occurs when something tries to navigate this loop until its end, which obviously doesn't work. The typical scenarios are:
rendering it into JSON (or XML). This is what #JsonIgnore & Co takes care of by not including properties in the JSON.
equals, hashCode, toString are often implemented to call the respective methods of all referenced objects.
Just as the JSON rendering this will lead to stack overflows.
So make sure to break the cycle in these methods as well.
JPA itself doesn't have a problem with cycles because it will look up entities in the first level cache.
Assuming you load an Authority and everything is eagerly loaded, JPA will put it in the first level cache, before checking the referenced user id. If it is present in the cache it will use that instance.
If not it will load it from the database, put it in the cache and then check for the authorities ids in the cache. It will use the ones found and load the rest.
For those it will again check the user id, but those are the user we just loaded, so it is certainly in the cache.
Therefore JPA is done and won't get lost in a cycle.
It will just skip the annotated
Try not to use the Lombok annotation #Getter and #Setter. Then generate manually getters and setters and use #JsonIgnore on the class member field and the getter, and #JsonProperty on the setter.
#JsonIgnore
private List<Authority> authorities;
#JsonIgnore
// Getter for authorities
#JsonProperty
// Setter for authorities
You can simply annotate the duplicated field with #ToString.Exclude
In you case:
#Data // this includes getter/setter and toString
#Entity
#Table(name = "users_tb")
#NoArgsConstructor
public class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String username;
private String password;
#ToString.Exclude
#OneToMany(mappedBy = "user", fetch = FetchType.LAZY)
private List<Authority> authorities;
}
#Data
#Entity
#Table(name = "authorities_tb")
#NoArgsConstructor
public class Authority {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
#ToString.Exclude
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "user_id")
private User user;
}
More info: Lombok #Data and #ToString.Exclude
I'm currently creating a microservice with spring boot and mysql to manage information about auctions. I have created a Bid-object and an Offer-object. Next to some properties of bid and offers, the most important thing here is the OneToMany-Relationship between Offer and Bid, since obviously every offer can have multiple related Bids.
I use the default JpaRepository-Interface for my database interactions, and tested my database structure by entering data and testing if I would get the correct output. This all worked fine, but when I tried to test the endpoints of my service that entered the data, I got some curious behaviour. First of all, here's my structure, so you can keep up with what I'm talking about. These are (shortened) versions of my Bid and Offer-Objects:
#Entity
#Data
public class Bid {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Integer id;
#NotNull
private String bidderUUID;
#NotNull
#JsonBackReference
#ManyToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "offerId")
private Offer offer;
#NotNull
private Integer amount;
private Boolean hasWon;
}
#Entity
#Data
public class Offer {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#NotNull
private String creatorUUID;
#JsonManagedReference
#OneToMany(mappedBy = "offer")
List<Bid> bids;
}
This is my very simple repsitory and controller:
public interface BidRepository extends JpaRepository<Bid, Long> {
}
#RestController
#RequestMapping("/bid")
public class BidController {
#Autowired
private BidRepository bidRepository;
#GetMapping("/bids")
public List<Bid> getAllBids() {
return bidRepository.findAll();
}
#PostMapping("/add")
public void createBid(#RequestBody Bid request) {
bidRepository.saveAndFlush(request);
}
}
With and offer with the id 27 in the database I proceeded to send a bid to the service.
I'm using postman to test my requests, and this is what I put in my request body, when adressing the endpoint localhost:8080/bid/add:
{
"amount": 2,
"bidderUUID": "eine uuid",
"offerId": 27
}
I received a 200 OK response, and thought, seems fine, but the data in the database is wrong, since it looks like this:
The offer is missing, even though the ID 27 definitely exists. Also, when I'm entering 27 manually and pushing it to the database, the data is correctly recognized.
I think this problem has something to do with the fact, that I expect an offer-object when posting the new bid, but only give him the ID, but when I enter the entire object, I get an org.hibernate.PersistentObjectException: detached entity passed to persist.
How can I make spring accept the id of the offer, when transmitting a new bid object?
This happed to me also, so as the solution I added the mapping column as Entity variable. In your case if I say, your Bid entity is missing the offer_id column mapping, although it mentioned the relationship. Add below entry in your Bid table as below:
#Column(name = "offer_id")
private Long offerId;
// generate setter-getter
add referencedColumn on the #JoinColumn annotation.
#JoinColumn(name = "offerId", referencedColumn = "id")
private Offer offer;
I want to ask about what is the most efficient way to search about specific data from a database without doing a for loop in all of the records?
I have a project on java spring and I have this Entity:
#Entity
#Table(name = "USERS") public class USERS {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "UID")
private Integer id;
#Column(name = "FName")
private String firstName;
#Column(name = "SName")
private String secondName;
#Column(name = "TName")
private String thirdName;
#Column(name = "LName")
private String fourthName;
#Column(name = "Email")
private String email;
#Column(name = "PW")
private String password;
#Column(name = "MNumber")
private String mobileNumber;
#Column(name = "ISDeleted")
private boolean isUserDeleted;
//---------------------- Getters and Setters ----------------------
and I made this service:
public List<USERS> findAllActive() {
List<USERS> usersList = new ArrayList<USERS>();
for (USERS users: usersRepository.findAll()){
if (!users.isUserDeleted()){
usersList.add(users);
}
}
return usersList;
}
For example; I have one property for User, if he is active or not.
So, my question; what is the most efficient way to do get specific data like retrieving all of the active users from the DB without doing a for loop like in the code above? Because if the list of users is a 1 Million or more, it could have performance issues.
Assuming that you are using JpaRepository then you can create custom query.
#Query("SELECT u FROM USERS u WHERE u.userDeleted = false")
List<USERS> findNotDeletedUsers();
and then call usersRepository.findNotDeletedUsers();
First of all, use an index on the field you want to search on (this won't help you much if the column has only two distinct values, but will make a huge difference if the value has high sparsity).
#Entity
#Table(name = "USERS",
indexes = {
// not a huge performance gain, since the column values are true/false
#Index(name = "index_by_active", columnList="ISDeleted", unique = false),
// possible huge performance gain, since only the relevant records are scanned
#Index(name = "index_by_first_name", columnList="FName", unique = false)})
public class USERS {...}
Then, define a query method that uses the indexed field (if you are using spring data it would look as follows).
public interface UsersRepository extends CrudRepository<USERS, Long> {
List<USERS> findUsersByISDeleted(boolean deleted);
List<USERS> findUsersByFName(String name);
List<USERS> findUsersByFNameAndISDeleted(String name, boolean deleted);
}
Queries on indexed fields will leverage the underlying index and provide an efficient access plan (so you won't end up scanning the whole table in order to extract a subset of entities matching a given criteria).
The solution from #Madis is okay. But if you always want to get users which are not deleted in all queries, you can specify it on Entity:
#Entity
#Table(name = "USERS")
#Where("ISDeleted = false")
public class USERS {
So now the condition "ISDeleted = false" is automatically append to all queries from the UserRepository. You can use usersRepository.findAll() instead of.
You don't need to specify any sql query or where clause. CrudRepository will do it for you automatically. Just use below code and pass true/false on need basis
List<Users> findIsUserDeleted(boolean isDeleted)
I have a customer object and inside that customer object i have a login object which contains username and password. When i do a POST the request works fine however when i try to do a PUT request it fails. It fails because it says Duplicate entry on the username.
I would like to be able to update the customer details without having to change the username. How can i achieve this.
This is my code :
UserLogin Entity :
#Entity
#Table(name = "Customer",
uniqueConstraints =
{
#UniqueConstraint(columnNames = "email"),
#UniqueConstraint(columnNames = "id"),
#UniqueConstraint(columnNames = "phoneNumber")
}
)
public class Customer implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private int customerNumber;
#OneToOne(cascade= CascadeType.ALL)
#JoinColumn(name = "loginCredentialsID")
private UserLogin userlogin;
private String phoneNumber;
private String email;
private String physicalAddress;
private String country;
... getters and setters
}
UserLogin Entity :
#Entity
#Table(name = "UserLogin",
uniqueConstraints =
{
#UniqueConstraint(columnNames = "userName")
})
public class UserLogin implements Serializable, UserDetails {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private int loginCredentialsID;
private String username;
private String password;
... getters and setters
}
CustomerService Class :
public Response updatCustomeretails(int id,Customer customer) {
customer.setCustomerNumber(id);
if( customer== null ){
throw new ResourceNotFoundException("Empty", "Missing Data");
}else {
customerRepository.save(customer);
return new Response(" Customer Updated Successfully","Thank you ");
}
When using Sping data JPA to update you should use save which you correctly did when saving on this line customerRepository.save(customer);. However when persisting data to a database in a PUT request JPA uses the keys within your entity mappings to be able to update the proper record.
So in your case you get that error when JPA tries to save a new record rather than an update to an existing record. Your intent is to update but I suspect your keys are missing or they are not properly defined so JPA tries to go and save a new record instead of updating.
So when you do the update(PUT) make sure the object you are passing has the same keys as the one you want to update.