put in hashmap using two lists and database - java

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.

Related

How can I to refactor this code and decouple method?

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.

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));
....
}

getLinks method returns deleted Entities, how to prevent it?

Below is my code to get the list of Entity's linked. It works but the problem is that even the deleted Entity is returned, although the Entity is already emptied out and it is the only property set. Is there a way to not return the deleted entities at all? Or is there a way to filter it out?
EntityId idOfEntity = txn.toEntityId(entityId);
Entity txnEntity = txn.getEntity(idOfEntity);
EntityIterable result = txnEntity.getLinks(Arrays.asList(new String[] {linkName}));
for (Entity entity : result) {
}
When you delete an entity, it's your responsibility to check if there are incoming links to the deleted entity. Otherwise so called "phantom links" can appear. You can set -Dexodus.entityStore.debug.searchForIncomingLinksOnDelete=true (PersistentEntityStoreConfig#setDebugSearchForIncomingLinksOnDelete(true)) to debug deletion in your application. With this setting, Xodus searches for incoming links to each deleted entity and throws EntityStoreException if it finds. The setting should not be used in production environment as it significantly slows down entity deletion performance.
Here's the complete code that I have come up with:
#Override
public boolean deleteEntities(String instance, String namespace, final String entityType) {
final boolean[] success = {false};
final PersistentEntityStore entityStore = manager.getPersistentEntityStore(xodusRoot, instance);
try {
entityStore.executeInTransaction(
new StoreTransactionalExecutable() {
#Override
public void execute(#NotNull final StoreTransaction txn) {
EntityIterable result = null;
if (namespace != null && !namespace.isEmpty()) {
result =
txn.findWithProp(entityType, namespaceProperty)
.intersect(txn.find(entityType, namespaceProperty, namespace));
} else {
result =
txn.getAll(entityType).minus(txn.findWithProp(entityType, namespaceProperty));
}
final boolean[] hasError = {false};
for (Entity entity : result) {
entity.getLinkNames().forEach(linkName -> {
Entity linked = entity.getLink(linkName);
entity.deleteLink(linkName, linked);
});
// TODO: This is a performance issue
final List<String> allLinkNames = ((PersistentEntityStoreImpl) entityStore).getAllLinkNames((PersistentStoreTransaction) entityStore.getCurrentTransaction());
for (final String entityType : txn.getEntityTypes()) {
for (final String linkName : allLinkNames) {
for (final Entity referrer : txn.findLinks(entityType, entity, linkName)) {
referrer.deleteLink(linkName, entity);
}
}
}
if (!entity.delete()) {
hasError[0] = true;
}
}
success[0] = !hasError[0];
}
});
} finally {
// entityStore.close();
}
return success[0];
}

Java - Access Object's Array List through HashMap (Key is object)

I am trying to loop through a HashMap, then for each key I want to access the object (Shipment) that is associated to the key and access my array list for further analysis purposes. Each object/key in HashMap has the same array list (metricList). I cannot seem to access it, though I have checked the private/public thing. Can someone point me in the right direction?
I think I need to maybe get the class of my object and then use the method "getList"... I tried with no luck.
This is a sample of the code (removed irrelevant parts) if it helps:
This is my object:
public class Shipment{
//Members of shipment
private final String shipment;
public Date creationDate;
public int creationTiming;
public int processingTiming;
public ArrayList<Integer> metricList;
public void createArrayList() {
// create list
metricList = new ArrayList<Integer>();
// add metric to list
metricList.add(creationTiming);
metricList.add(processingTiming);
}
public ArrayList<Integer> getList() {
return metricList;
}
}
This is the class where I create a hashMap and run through different analysis:
public class AnalysisMain {
public static Map<String, Shipment> shipMap = new HashMap();
public static void main(String[] args) {
try {
... // Different calls to analysis
}
catch {}
}
}
This is where the issue occurs (it does not recognize that I already have a "metricList", asking if I want to create local variable)
public class Metric_Analysis{
public static void analyze() throws Exception{
ResultSet rs;
try {
rs = getSQL("SELECT * FROM TEST_METRICS");
}
catch(Exception e) {
//Pass the error
throw new java.lang.Exception("DB Error: " + e.getMessage());
}
Iterator<Map.Entry<String, Shipment>> iterator = shipMap.entrySet().iterator();
while(iterator.hasNext()){
Iterator<String> metricIterator = metricList.iterator();
//Above is the Array List I want to access and loop through
//I will then perform certain checked against other values on a table...
while (metricIterator.hasNext()) {
//I will perform certain things here
}
}
}
}
You need to get the List out of your Shipment.
You can access the object from the iterator with: iterator.next();
This will also set the pointer to the next Entry in your List/Map.
Change your code:
Iterator<Map.Entry<String, Shipment>> iterator = shipMap.entrySet().iterator();
while(iterator.hasNext()){
// Get the Entry from your Map and get the value from the Entry
Entry<String, Shipment> entry = iterator.next();
List<Integer> metricList = entry.getValue().getList();
Iterator<String> metricIterator = metricList.iterator();
//Above is the Array List I want to access and loop through
//I will then perform certain checked against other values on a table...
while (metricIterator.hasNext()) {
//I will perform certain things here
}
}

A different object with the same identifier value was already associated with the session:. neither merge nor saveOrUpdate works

I have a GUI where a list of teachers is shown. Two are selected by the user - they are going to be the form teachers of a new school class that gets created.
The teacher - class relationship is n-m.
School class: (it inherits its id from its group)
#Entity
#Table(name="school_classes")
#Cacheable(true)
public class SchoolClass extends Group{
#ManyToMany(cascade=CascadeType.ALL, mappedBy="classes", fetch=FetchType.EAGER)
private SortedSet<Teacher> teachers;
Teacher:
#Entity
#Table(name="teachers")
#Cacheable(true)
public class Teacher extends creator.models.school.Entity{
// name etc
#ManyToMany(cascade=CascadeType.ALL, fetch=FetchType.EAGER)
#JoinTable(name="class_teachers",
joinColumns = #JoinColumn(name="teacher_id", referencedColumnName="id"),
inverseJoinColumns = #JoinColumn(name="class_id", referencedColumnName="id"))
private SortedSet<SchoolClass> classes;
I try to create a new class like this:
String className = request.getParameter("class_name");
String id1 = request.getParameter("class_teacher1");
String id2 = request.getParameter("class_teacher2");
Teacher t1 = DataRetrieveModule.getTeacher(id1);
Teacher t2 = DataRetrieveModule.getTeacher(id2);
Layout l = new Layout();
SchoolClass newClass = new SchoolClass(className);
newClass.setLayout(l);
newClass.addTeacher(t1);
t1.addClass(newClass);
newClass.addTeacher(t2);
t2.addClass(newClass);
DataInsertionModule.insert(newClass);
This statement DataRetrieveModule.getTeacher(id1) opens a session, retrives the teacher by ID and closes it.
DataInsertionModule.insert(newClass) also opens a session, calls session.saveOrUpdate(newClass). (I also tried session.merge(newClass))
Retrieving a teacher:
public static Teacher getTeacher(String id) {
Session session = null;
Teacher t = null;
try{
sessionFactory = MyFactory.getSessionFactory();
session = sessionFactory.openSession();
t = (Teacher) session.get(Teacher.class, Long.parseLong(id));
}
catch(Exception e) {
System.out.println("in DAO:");
e.printStackTrace();
if(session!=null)
session.close();
}
finally{
if(session!=null)
session.close();
}
return t;
}
Data insertion:
public static void insert(Object o) {
Session session = null;
try
{
sessionFactory = MyFactory.getSessionFactory();
session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();
session.save(o);
tx.commit();
System.out.println("insertion done");
}
catch (Exception e)
{
System.out.println(e.getMessage());
e.printStackTrace();
}
finally
{
if(session!=null)
session.close();
}
}
But insertion never works.
There is always an object with the same id that already exists.
a different object with the same identifier value was already associated with the session: [creator.models.school.Teacher#3]
I searched on stackoverflow and have overridden my getHashCode and equals method in the class all my business objects inherit from:
#Override
public int compareTo(Entity o) {
if(this.id < o.getId())
return -1;
else if(this.id > o.getId())
return 1;
return this.getClass().getName().compareTo(o.getClass().getName());
}
#Override
public boolean equals(Object o) {
if(o instanceof Entity)
return this.compareTo((Entity)o)==0;
else return this.equals(o);
}
#Override
public int hashCode() {
char[] bla = this.getClass().getName().toCharArray();
int blue=0;
for(char c:bla)
blue = blue*10 + c;
return (int) (id+blue);
}
Furthermore I tried to change the CascadeType of ManyToMany to MERGE (reverted again).
At the moment I merge the teacher objects after retrieving both of them with session.get(..), as retrieving takes a lot of data out of the DB. There are futher ..ToMany relations. Therefore it probably might happen, that the call causes that both teachers are loaded.
public static Object merge(Object o) {
Session session = null;
Object returnVal = null;
try
{
sessionFactory = MyFactory.getSessionFactory();
session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();
returnVal = session.merge(o);
tx.commit();
}
catch (Exception e)
{
System.out.println(e.getMessage());
e.printStackTrace();
}
finally
{
if(session!=null)
session.close();
}
return returnVal;
}
.
Teacher t1 = DataRetrieveModule.getTeacher(id1);
Teacher t2 = DataRetrieveModule.getTeacher(id2);
t1= DataInsertionModule.merge(t1);
t2= DataInsertionModule.merge(t2);
Therefore I thought, that if I merge the one my get-method returned with the one that must have been loaded by the get call for the other teacher, it should work. (like here: http://www.stevideter.com/2008/12/07/saveorupdate-versus-merge-in-hibernate/) But it does not :(
Is it probably because an object of the same superclass (Object or my Entity class) has the same ID?
Please help me!
One potential situation that could lead to this error is when the id of teacher 1 and teacher 2 is the same. Since you close the session between each get they will be detached, and you would end up loading two different objects that both represent the same row in the database (they have the same primary key.) Then, when these are both going to be reattached to a session through a SchoolClass being saved, Hibernate will see two different objects both representing the same row in the database, and not know which one represent the correct state to persist to the database.
Don't know for sure if this is the case here, but you should run a debugger and check the state of the objects referenced by teacher1 and teacher2.

Categories

Resources