How can I to refactor this code and decouple method? - java

I have set of entities(templates)
I need to clone full this set and create same set with new Ids.
I have 3 methods like this(Entity One is User):
private Map<String, String> createUsers() {
Map<String, String> userIds = new HashMap<>();
Iterator<User> users = userService.findAllUsers();
while (users.hasNext()) {
User user = users.next();
String oldId = user.getId();
user.setId(generateNewId());
user.setName(generateNewName());
userService.saveUser(user);
userIds.put(oldId, user.getId());
}
return userIds;
}
And two similar methods(Entity two is Person and Entity Three is Book). In each method, I create new entity and store old and new IDS. After that, I clone Orders and relink old id and new id. I do it like this:
if (userIds.containsKey(order.getUserId())) {
order.setUserId(userIds.get(order.getUserId()));
}
if (personIds.containsKey(order.getPersonId())) {
order.setPersonId(personIds.get(order.getPersonId()));
}
This structure is ugly and I want to refactor it. But I have not ideas.

Related

How to figure out what object is in a abstract list?

I have an abstract class called sessions. Lectures and tutorials extend sessions. Then I have a class called enrollment which holds a list of sessions (Lectures & tutorials). How can I loop through the session list in Enrolment and return a list of Lectures only from the session list?
My next question is should I instead store 2 lists. One list of Lectures and one list of Tutorials, instead of 1 session list? This is because the sessions list is useless to me and I have to loop through it each time to get information about lectures and tutorials. Is there a way I am missing to get all the lectures objects? I am new to java.
public class Enrolment {
private List<Session> sessions;
public Enrolment() {
this.sessions = new ArrayList<>();
}
public addSession(Session session) {
this.sessions.add(session);
}
}
public class Session {
private int time;
public Session(int time) {
this.time = time;
}
}
public class Lecture extends Session {
private String lecturer;
public Lecture(int time, String lecturer) {
super(time);
this.lecturer = lecturer;
}
}
public class Tutorial extends Session {
private String tutor;
private int tutorScore;
public Tutorial(int time, String tutor, int tutorScore) {
super(time);
this.tutor = tutor;
this.tutorScore = tutorScore;
}
}
public class test {
public static void main(String[] args) {
Enrolment newEnrolment = new Enrolment();
Lecture morningLec = new Lecture(900, "Dr. Mike");
newEnrolment.addSession(morningLec);
Tutorial afternoonTut = new Tutorial(1400, "John Smith", 3);
newEnrolment.addSession(afternoonTut);
Lecture middayLec = new Lecture(1200, "Mr. Micheals");
newEnrolment.addSession(middayLec);
Tutorial NightTut = new Tutorial(1900, "Harry Pauls", 4);
newEnrolment.addSession(NightTut);
}
}
Stream the sessions list and use instanceof to filter the Lectures type objects
List<Lecture> l = sessions.stream()
.filter(Lecture.class::isInstance)
.map(Lecture.class::cast)
.collect(Collectors.toList());
By using for loop use two different lists for each type
List<Lecture> l = new ArrayList<>();
List<Tutorial> t = new ArrayList<>();
for (Session s : sessions) {
if (s instanceof Lecture) {
l.add((Lecture) s);
}
else if(s instanceof Tutorial) {
t.add((Tutorial) s);
}
}
Maybe you should store in two lists, just like:
public class Enrolment {
private List<Lecture> lectures;
private List<Tutorial> tutorials;
public Enrolment() {
this.lectures = new ArrayList<>();
this.tutorials = new ArrayList<>();
}
public void addSession(Session session) {
if (session instanceof Lecture) {
lectures.add((Lecture) session);
} else if (session instanceof Tutorial) {
tutorials.add((Tutorial) session);
}
}
public List<Lecture> getLectures() {
return lectures;
}
public List<Tutorial> getTutorials() {
return tutorials;
}
public List<Session> getAllSessions() {
ArrayList<Session> sessions = new ArrayList<>(lectures);
sessions.addAll(tutorials);
return sessions;
}
}
Is that what you need?
My next question is should I instead store 2 lists. One list of
Lectures and one list of Tutorials, instead of 1 session list? This is
because the sessions list is useless to me and I have to loop through
it each time to get information about lectures and tutorials. Is there
a way I am missing to get all the lectures objects?
You answered yourself to your problem.
When you start to write too complex/boiler plate code to make things that should be simple such as iterating on a list of objects that you have just added, it is a sign that you should step back and redesign the thing.
By introducing Enrolment.addSession(Session session),
you introduced an undesirable abstraction :
public class Enrolment {
private List<Session> sessions;
public Enrolment() {
this.sessions = new ArrayList<>();
}
public addSession(Session session) {
this.sessions.add(session);
}
}
You don't want to handle uniformally Lecture and Tutorial from the Enrolment point of view, so just don't merge them in the same List only because these rely on the same interface (Session).
Abstraction has to be used when it is required and not systematically because that is possible.
Don't you add all objects in a List of Object because all is Object ? No.
Instead of, create this distinction both from the API method and from its implementation :
public class Enrolment {
private List<Conference> conferences = new ArrayList<>();
private List<Tutorial> tutorials = new ArrayList<>();
public addConference(Conference conference) {
this.conferences.add(conference);
}
public addTutorial(Tutorial tutorial) {
this.tutorials.add(tutorial);
}
}
And use it :
Lecture morningLec = new Lecture(900, "Dr. Mike");
newEnrolment.addLecture(morningLec);
Tutorial afternoonTut = new Tutorial(1400, "John Smith", 3);
newEnrolment.addTutorial(afternoonTut);
Note that you could have a scenario where you need to manipulate uniformally Tutorial and Lecture for some processings but that for others you want to distinguish them.
In this case, you have some common ways :
instanceOf : easy to use but also easy to make a code brittle. For example, later you could add a new subclass in the Session hierarchy and without be aware of it, instances of this subclass could be included or excluded in some processing without that the compiler warns you.
provide a abstract method that returns a boolean or an enum to convey the nature of the object (ex: isLecture()). More robust than instanceOf since the compiler constraints you to override the method but it may also lead to error prone code if multiple subclasses are added and that the filters are not only on Lecture but Lecture and another type. So I would favor this way while the filtering condition stays simple.
define three lists : one for lectures, another for conferences and another that contains all of these that should be handled uniformally. More complex way but more robust way too. I would favor it only for cases with complex/changing filtering conditions.
List<Lecture> l = newEnrolment.getSessions()
.stream()
.filter(s-> s.getClass().equals(Lecture.class))
.map(session -> (Lecture) session)
.collect(Collectors.toList());
!!! Don't use typeof
Quoting from your question,
This is because the sessions list is useless to me.
So, this is probably not the right place to have the list(?).
My preference would be to have
public interface Enrolment {
public abstract addSession(Session session);
public abstract getSessions();
}
And List<LectureEnrolment> and List<TutorialEnrolment> must be in their respective classes.(I have renamed Lecture to LectureEnrolment and Tutorial to TutorialEnrolment)
main() must have something like,
Enrolment lectureEnrolment= new LectureEnrolment()
Enrolment tutorialEnrolement = new TutorialEnrolment()
call the respective addSession() or getSession() depending on requirement.
change private List<Session> sessions; to public List<Session> sessions; in class Enrolment
public static void main(String[] args) {
....
var lecturesAndTutorials = newEnrolment.sessions.where(x => x.getType() == typeof(Lecture) || x.getType() == typeof(Tutorial));
....
}

put in hashmap using two lists and database

I have two classes, User and Customer, using hibernate, I have mapped these tables, now I want to retrieve the data from the database, where I can put the values in hashmap such that for each user, if a particular customer exists in the mapping table, then the key of map is set as the customer name, and value to true or false, depending upon the mapping table.
I have retrieved both the lists:
static List<User> listUsers = new ArrayList<User>();
static List<Customer> listCustomers = new ArrayList<Customer>();
List<UserTO> list = new ArrayList<UserTO>();
public static List<Customer> getListOfCustomers() {
HibernateUtil.openSession();
Session session = HibernateUtil.getSession();
Transaction tx = null;
try {
tx = session.getTransaction();
tx.begin();
listCustomers = session.createQuery("from Customer").list();
tx.commit();
} catch (Exception e) {
if (tx != null) {
tx.rollback();
}
e.printStackTrace();
} finally {
session.close();
}
return listCustomers;
}
(similarly list of users)
in UserTO class, I have:
public class UserTO implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue
private Long id;
private String userId;
private Map<String, Boolean> map = new HashMap<String, Boolean>();
(getter and setter)
I tried doing this:
public static void execute() {
getListOfUsers();
getListOfCustomers();
for (User user : listUsers) {
UserTO u = new UserTO();
Map<String, Boolean> map = new HashMap<>();
for (Customer customer : listCustomers) {
if (customer.getCompanyName() == user.getCustomers(customer)) {
map.put(customer.getCompanyName(), true);
} else {
map.put(customer.getCompanyName(), false);
}
}
user.getUserId();
u.setUserId(user.getUserId());
u.setMap(map);
listUsers.add(user);
}
}
which gives me Concurrent Modification Exception
I don't know where I am doing wrong, and what should I do next.
you are adding user to listUsers when you are iterating over this listuser. This results in the given exception.
Use list.add(u);
Reason for ConcurrentModificationException is you are trying to add to the list at the same time when you are iterating like:
for (User user : listUsers) {
....
listUsers.add(user);
One way to solve this would be to create a temporary list and keep adding to that list and after your for loop, use addAll method on listUsers to add all your users that you added in the loop.
Note: Inorder to select the data, you don't need transaction as select wont do any side effect to your table.
What you are trying to do is that you are getting a User object from the list, and then again adding it to the list. It wont work for the following reasons:
You are iterating the list and adding in the same loop which is not allowed and throws ConcurrentModificationException.
Also you are adding in every iteration. Which means that the list will grow with every iteration and your loop will never end. First you should remove the object and then again add it in the same place.
Solution:
public static void execute() {
getListOfUsers();
getListOfCustomers();
for (int i=0;i<listUsers.size();i++) {
User user = listUsers.remove(i);
UserTO u = new UserTO();
Map<String, Boolean> map = new HashMap<>();
for (Customer customer : listCustomers) {
if (customer.getCompanyName() == user.getCustomers(customer)) {
map.put(customer.getCompanyName(), true);
} else {
map.put(customer.getCompanyName(), false);
}
}
user.getUserId();
u.setUserId(user.getUserId());
u.setMap(map);
listUsers.add(i,user);
}
}
P.S. - But still I don't get that why there is a need to add an object which already exists in the list without any change.
You are getting concurrent modification exception as HashMap is not thread safe. Use concurrentHashMap instead. Thanks hope it helps.

Add all elements to the another List in java?

I am trying to implement "add to cart" concept like any e-commerce website on the basis of "productId". when i am calling this controller, it's add only last element which user added to the list instead of all product list.
#RequestMapping("/addcart/{list.productId}")
public String addCart(#PathVariable("list.productId") Integer productId, Model m, HttpSession session) {
List<Product> cartlist = productDao.findById(productId);
List<Product> useradd = new ArrayList<>();
for (Product e : cartlist) {
useradded.add(e);
}
session.setAttribute("sessioncartview", useradded);
return "redirect:/";
}
i found only one product which was last added, not all product list. in "useradded". i want to add all elements in the list i.e. "useradded" object and after that bind with session. how can i do that ?
Because you are creating a new useradded list every time when this method is called.
Instead of doing
List<Product> useradd = new ArrayList<>();
you can get the useradd from the session and only create a new instance if it is not there.
List<Product> useradd = session.get("sessioncartview");
if (useradd == null) {
useradd = new ArrayList<>();
}

Play-2.2 Example #ManyToMany Relationship Java

I was unable to find a complete example for designing a ManyToMany relationship with Play-2.2 in Java.
My Problem is, i can't get the Form-to-Model binding to work on my ManyToMany Relationship...
There are many users. A user can have many jobs.
There are many jobs. A job can "have" many members/participants(users).
Job.java
#Entity
public class Job extends TimmeeyModel {
#Constraints.Required
#Column(unique = true)
public String jobName;
#ManyToMany(mappedBy="jobs")
public List<User> members = new ArrayList<User>();
public static Finder<Long,Job> find = new Finder<Long, Job>(Long.class,Job.class);
#Override
public Job create(){
this.save();
return this;
}
#Override
public Job update(Long id){
Job oldJob = find.byId(id);
oldJob.setJobName(this.getJobName());
oldJob.setMembers(this.getMembers());
oldJob.save();
oldJob.refresh();
return oldJob;
}
#Override
public void delete(){
this.delete();
}
public String toString(){
String userString ="";
for (User user : members) {
userString+=user.getUsername() +", ";
}
return jobName + ", " + id +" " + userString;
}
}
User.java
#Entity
public class User extends TimmeeyModel {
#Constraints.Required
#Column(unique = true)
public String username;
#ManyToMany(cascade=CascadeType.PERSIST)
public List<Job> jobs = new ArrayList<Job>();
public static Finder<Long, User> find = new Finder<Long, User>(Long.class, User.class);
public String getEmail(){
return this.username + "#pool.math.tu-berlin.de";
}
#Override
public User create(){
//jobs.add(Job.find.all().get(0));
this.save();
return this;
}
#Override
public User update(Long id){
User user = find.ref(id);
user.setUsername(this.getUsername());
user.setUsername(this.getUsername());
user.save();
user.refresh();
return user;
}
public void delete(){
this.delete();
}
public String toString(){
String jobString = "";
for (Job job : jobs) {
jobString += job.toString() + ", ";
}
return "User Id: " + id + " "+username + "Jobs: " + jobString;
}
UserController.java
public static Result showCreate(){
return ok(views.html.user.add.render(Job.find.all(), userForm));
}
public static Result create(){
Map<String, String> newData = new HashMap<String, String>();
Form<User> filledForm = userForm.bind(newData);
if(filledForm.hasErrors()) {
return badRequest("Leider nein" + filledForm.toString());
} else {
String logString = filledForm.toString();
User user = filledForm.get();
user.create();
return ok(user.toString() + logString);
}
}
The view scala template
#form(routes.Users.create()) {
#inputText(userForm("username"))
#for((value,index) <- jobs.zipWithIndex ) {
<input type="checkbox" name="jobs[]" value="#value.getId()">#value.getJobName()</input>
}
i just cant get it to work. After submitting the Joblist of a user stays empty. But when i set the list "hardcoded" inside the user object, it stays there and works like expected. SO i don't think it is related to some db/config issue. SO i suspect there is something wrong while binding the form-data to the User-Model.
I also tried different version of the form like
#value.getJobName()
#value.getJobName()
#value.getJobName()
#value.getJobName()
Nothing works. So has anyone an idea what i doing wrong, or what i don't get about play!? Maybe it is impossible to do this without writing HUGE ammounts of boilerplate code in every Controller where i want to handle models with manyToMany relationships?
Because i know i could parse the form data in the controller "by hand" and force-put the jobs-List into the users. But this can not be the right way for such a MVC framework like play :-)
Thank you
By the way, there is not even ONE example/sample in the Play-Documentation/GitHub where the term "ManyToMany" occurs...
Really i just need some not totally fucked way to handle ManyToMany Relations ( if i can't use checkboxes.. Okay, if i should use something else, OK.... I just need some way for it to work)
I answered this question myself. In short: It is NOT possible with Play! to autobind Objects from their identifiers to a List.
Maybe you will read something about custom Databinder or some magical form helpers.
The CustomDatabinder is good when you want to get a SINGLE complex Object mapped for example
User.java
public Job mainJob;
If you now enter the id of a Job into the User form, a custom Databinder is able to bind the data (if you provided one).
But this will not work for List
In fact the only thing is to use a little boilerplate code.
I did it that way
User user = filledForm.get();
List<Job> jobs = new ArrayList<Job>();
for (Job job : user.getJobs()) {
jobs.add(Job.find.byId(job.getId()));
}
user.create();
The form binder will generate a List of Jobs for the jobs List in the User. But these jobs are just "stubs" i had too iterate through the generated Jobs, and build a list with the real ones by searching for them in the Database..
It's not pretty, but also not too ugly.
Maybe this explanation can help others.

Generic querydsl orderBy dynamic path generation with left joins

I've run into a problem while using JPA with Querydsl and Hibernate for data storage management. The sample model is as follows:
#Entity
public class User {
....
#ManyToOne
#JoinColumn(name = "CATEGORY_ID")
private Category category;
}
#Entity
public class Category {
..
private String acronym;
#OneToMany(mappedBy = "category")
List<User> userList;
}
In my Spring MVC webapp I have a search form with User parameters and orderBy select. The orderBy select can be either User property or Category property. The orderBy parameters are stored as Map (f.e. {"login" : "adm", {"firstName" : "John"}. The search function receives the search parameters (as string) and the map above with order specification. The simplified code for ordering is as follows:
Map<String, String> orderByMap = new HashMap<String, String>();
orderByMap.put("firstName", "asc");
orderByMap.put("unit.acronym", "desc");
PathBuilder<User> pbu = new PathBuilder<User>(User.class, "user");
....
for (Map.Entry<String, String> order : orderByMap.entrySet())
{
// for simplicity I've omitted asc/desc chooser
query.orderBy(pbu.getString(order.getKey()).asc());
}
The problem starts when I want to introduce sorting by Category's parameter, like {"category.acronym", "desc"}. As explained here, the above code will make querydsl to use cross join with Category table and omitt the Users without Categories, which is not expected behavior.
I know, I have to introduce the left join with Categories and use the alias for the sorting to make it work, hovewer I'm looking for efficient way to do it dynamically. Stripping each String looking for category or any other entity (like "user.category.subcategory.propetry") will introduce a lot of ugly code and I'd rather not do that.
I'd appreciate the help with some more elegant solution.
I added now a protoype of the implementation to the test side of Querydsl https://github.com/mysema/querydsl/issues/582
I will consider a direct integration into Querydsl if this a common use case
public class OrderHelper {
private static final Pattern DOT = Pattern.compile("\\.");
public static PathBuilder<?> join(JPACommonQuery<?> query, PathBuilder<?> builder, Map<String, PathBuilder<?>> joins, String path) {
PathBuilder<?> rv = joins.get(path);
if (rv == null) {
if (path.contains(".")) {
String[] tokens = DOT.split(path);
String[] parent = new String[tokens.length - 1];
System.arraycopy(tokens, 0, parent, 0, tokens.length - 1);
String parentKey = StringUtils.join(parent, ".");
builder = join(query, builder, joins, parentKey);
rv = new PathBuilder(Object.class, StringUtils.join(tokens, "_"));
query.leftJoin((EntityPath)builder.get(tokens[tokens.length - 1]), rv);
} else {
rv = new PathBuilder(Object.class, path);
query.leftJoin((EntityPath)builder.get(path), rv);
}
joins.put(path, rv);
}
return rv;
}
public static void orderBy(JPACommonQuery<?> query, EntityPath<?> entity, List<String> order) {
PathBuilder<?> builder = new PathBuilder(entity.getType(), entity.getMetadata());
Map<String, PathBuilder<?>> joins = Maps.newHashMap();
for (String entry : order) {
String[] tokens = DOT.split(entry);
if (tokens.length > 1) {
String[] parent = new String[tokens.length - 1];
System.arraycopy(tokens, 0, parent, 0, tokens.length - 1);
PathBuilder<?> parentAlias = join(query, builder, joins, StringUtils.join(parent, "."));
query.orderBy(parentAlias.getString(tokens[tokens.length - 1]).asc());
} else {
query.orderBy(builder.getString(tokens[0]).asc());
}
}
}
}

Categories

Resources