- This topic has 20 replies, 2 voices, and was last updated 17 years, 1 month ago by
Mork.
-
AuthorPosts
-
Hello,
I’ve completed the excellent, but basic, EchoMessage Hibernate (one table) tutorial and have gotten Hibernate working up to a point in my own application.
I’m now wondering how I can use MyEclipse to automatically detect and implement a one to many relationship.
In my case, I have two tables a Users table and a UserVistHistory table. When a user visits a Website, his id and password get looked up using hibernate. This works now.
However, I also want to add a record to the UserVisitHistory table each time the user logs in.
In this case, I may not even need a one to many since I could just populate a separate UserVisitHistory.java class and persist it with the associated UserVisitHistoryDao.java class, right?
But, what about if I want to do a query to show all the visits a particular user has had?
I think what I would do would be to add a UserVisitHistory List field to the Users class and populate the list of user Visit History objects. Then, somehow, I need to tell Hiberate to persist, not only the user data, but also the contained List of UserVisitHistory.
I realize this would be done via the mapping files, but good examples are hard to come by for basic understanding…
So can anyone help me figure out how to scale this part of Hibernate using MyEclipse?
Thanks in advance.
M
April 28, 2008 at 1:53 pm #284508
Riyad KallaMemberMork,
As long as you setup these two tables correctly, with the correct relationship and foreign keys (e.g. Visit table has an FK back to user.id field) when you reverse engineer these tables together in MyEclipse, it will generate everything you just mentioned above for you. So you can either add more visits, or just call user.getVisits() and it will return a List or a Set (I forget which) of all the records. Also you’ll have accompanying DAOs for the entities also.You can also do this with JPA and EJB3s in MyEclipse.
April 28, 2008 at 3:55 pm #284515There must be some type of naming convention I’m not using.
The two tables each have primary keys (requirement).
The UserVisitHistory also has a foreign key back to the users table.
However, when I dragged and dropped the user visit history table into the reverse engineer area, it didn’t create any “user” structure as class fields.
Nor did the “AbstractUsers.java” class get updated.
Do the foreign keys have to have the same exact names? (ID = ID)
Thanks in advance again.
M
April 30, 2008 at 5:53 am #284537I think there needs to be some documentation (possibly I’ve missed it?) that describes exactly how to create a one to many and also a many to many relationship using Hibernate in MyEclipse.
I deleted both my “users” and “usersvisithistory” entries in the project and then dragged both tables into the reverse engineer area of the hibernate perspective (designer of hibernate config file).
MyEclipse re-created the two abstract classes, the actual classes, and the Dao classes, but these were totally unconnected. Since the relationship between users and uservisithistory is 1:M, I expected to see a List or some entry added to the users class.
I checked all the (A>B) and (B<A) boxes and other things I could see (like enable M:M detection, but checked on one test and un-checked on another test), but there must be some type of assumed naming convention or something (that’s not documented?).
Here are the generated fields in the abstract users class:
private Integer usrPk;
private Byte usrIsPaidUser;
private Byte usrAccountExpires;
private Date usrAccountExpireDate;
private String usrFname;
private String usrMi;
private String usrLname;
private String usrEmail;
private String usrPhone;
private Date usrLastlogindate;
private Byte usrAccountactive;
private String usrLogin;
private String usrPassword;
——————————————————
Here are the generated fields in the abstract uservisithistory class:
(this table has a different three character prefix for each field name)
private Integer uvhPk;
private Integer uvhUsrPk; // ** THIS IS THE FK back to the user class!
private Date uvhDateTimeOfVisit;
private String uvhPageFrom;
————————-
As you can see, MyEclipse is completely unaware, or so it seems, about the relationship between the two tables.
How do I clue MyEcipse in that this is a 1:M relationship for Hibernate reverse engineering?
I hope you have some additional tutorials soon covering these basic aspects of using your product’s features.
Thanks again.
M
April 30, 2008 at 11:04 am #284550
Riyad KallaMemberMork,
The generated Hibernate classes don’t use annotations to describe the relationships, that’s done in the hbm.xml files (JPA will use annotations). Can you pop open the two generated hbm.xml files and see if the relatonships are realized there with the correct one-to-many tags?NOTE: It’s not the same thing, but I just walked my way through reverse-engineering the EMPLOYEE and OFFICE tables from the CLASSICCARS schema in the MyEclipse Derby DB, and an Employee has a m2o relationship with Office, I’m posting the folllowing shots below just for comparison sake, I used all the default settings.
Attachments:
You must be logged in to view attached files.April 30, 2008 at 4:31 pm #284558Hi R,
Yup, as I expected, neither of the hbm files had any one-to-many mappings (see below).
Would you expect this since, if the Java classes MYE creates don’t have data structures like Lists, etc. how would Hibernate persist these values???
I dragged the two tables into the reverse engineering area at the same time.
I’m confused how to use MyEclipse to do this “simple” 1:M setup. There must be some implicit assumption about field names or something…
—–
<?xml version=”1.0″ encoding=”utf-8″?>
<!DOCTYPE hibernate-mapping PUBLIC “-//Hibernate/Hibernate Mapping DTD 3.0//EN”
“http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd”>
<!–
Mapping file autogenerated by MyEclipse Persistence Tools
–>
<hibernate-mapping>
<class name=”TestProject.Uservisithistory” table=”uservisithistory” catalog=”users”>
<id name=”uvhPk” type=”java.lang.Integer”>
<column name=”uvh_pk” />
<generator class=”assigned” />
</id>
<property name=”uvhUsrPk” type=”java.lang.Integer”>
<column name=”uvh_usr_pk” not-null=”true” />
</property>
<property name=”uvhDateTimeOfVisit” type=”java.util.Date”>
<column name=”uvh_dateTimeOfVisit” length=”19″ not-null=”true” />
</property>
<property name=”uvhPageFrom” type=”java.lang.String”>
<column name=”uvh_pageFrom” length=”45″ not-null=”true” />
</property>
</class>
</hibernate-mapping>———————-
<?xml version=”1.0″ encoding=”utf-8″?>
<!DOCTYPE hibernate-mapping PUBLIC “-//Hibernate/Hibernate Mapping DTD 3.0//EN”
“http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd”>
<!–
Mapping file autogenerated by MyEclipse Persistence Tools
–>
<hibernate-mapping>
<class name=”TestProject.Users” table=”users” catalog=”users”>
<id name=”usrPk” type=”java.lang.Integer”>
<column name=”usr_pk” />
<generator class=”assigned” />
</id>
<property name=”usrIsPaidUser” type=”java.lang.Byte”>
<column name=”usr_isPaidUser” not-null=”true” />
</property>
<property name=”usrAccountExpires” type=”java.lang.Byte”>
<column name=”usr_accountExpires” not-null=”true” />
</property>
<property name=”usrAccountExpireDate” type=”java.util.Date”>
<column name=”usr_accountExpireDate” length=”19″ />
</property>
<property name=”usrFname” type=”java.lang.String”>
<column name=”usr_fname” length=”20″ not-null=”true” />
</property>
<property name=”usrMi” type=”java.lang.String”>
<column name=”usr_mi” length=”1″ />
</property>
<property name=”usrLname” type=”java.lang.String”>
<column name=”usr_lname” length=”25″ not-null=”true” />
</property>
<property name=”usrEmail” type=”java.lang.String”>
<column name=”usr_email” length=”45″ />
</property>
<property name=”usrPhone” type=”java.lang.String”>
<column name=”usr_phone” length=”25″ />
</property>
<property name=”usrLastlogindate” type=”java.util.Date”>
<column name=”usr_lastlogindate” length=”19″ />
</property>
<property name=”usrAccountactive” type=”java.lang.Byte”>
<column name=”usr_accountactive” not-null=”true” />
</property>
<property name=”usrLogin” type=”java.lang.String”>
<column name=”usr_login” length=”45″ not-null=”true” />
</property>
<property name=”usrPassword” type=”java.lang.String”>
<column name=”usr_password” length=”20″ not-null=”true” />
</property>
</class>
</hibernate-mapping>May 2, 2008 at 6:21 am #284608I was hoping to have heard something back from support by now, but in any case, I did an additional experiment with no improvement from MyEclipse.
I renamed the field in the uservisithistory table thinking that there’s an implicit (hidden) requirement that the field names must match between the tables for MyEclipse to figure out it’s a one to many relationship.
The result? No difference. I drag both tables into the reverse engineering area, but MyEclipse can’t figure out they’re related (1:M). I do no have any foreign key constraints created.
There *really* needs to be some documentation created for this part of MyEclipse.
This should not be this difficult and take days to figure out.
Thanks in advance.
M
P.S. Also, MyEclipse is stuck on thinking an XMLfile is for Struts, so everytime I click one, it says it can’t open it and I have to do an Open…with. Happens with every xml file.
May 2, 2008 at 11:25 am #284618
Riyad KallaMemberI do no have any foreign key constraints created.
Wait… you have no foreign key constraints in your database defined? You can confirm this by right-clicking on your schema in the DB Explorer, and selecting “New ER Diagram”, then select the tables you are trying to rev-eng and verify that no connections are drawn between them.
P.S. Also, MyEclipse is stuck on thinking an XMLfile is for Struts
Hop on over to Windows > Preferences > General > Editors > File Associations, scroll down to *.xml and reset the default XML file editor mapping. I guess it got changed somehow.
May 2, 2008 at 12:06 pm #284620No I never use foreign key constraints…they too often get in the way. Most developers I’ve worked with over the years are the same way.
I was hoping there’d be a wizard or something were I could tell MyE that…”hey, this FK is 1:M and maps back to this table to this field (for the actual PK)”.
Are you indicating that I’m required to use FK Constraints for MyE to figure this 1:M out?
Thanks in advance.
M
May 2, 2008 at 12:16 pm #284621
Riyad KallaMemberNo I never use foreign key constraints…
Ahh, I should have clarified that from the get-go, my appologies for the wasted time. All of our reverse-engineering tools, with respect to entity relationships, key these relationships off of exactly how they are defined in the database itself. Without that information, there is no way for the tools to know how A should be related to B and so on, and using naming conventions doesn’t work either because that would require massive assumptions on our part about how people organize their schemas.
Are you indicating that I’m required to use FK Constraints for MyE to figure this 1:M out?
Yes exactly. We will honor any relationships you define in your DB with the appropriate Hibernate, JPA or EJB3 code we generate. If there are no relationships defined in your DB, then we don’t define any in the generated code.
I was hoping there’d be a wizard or something were I could tell MyE that…”hey, this FK is 1:M and maps back to this table to this field (for the actual PK)”.
I’m sorry, there isn’t. If you want to do this you would need to code up the relationships by hand in the hbm.xml files to enforce the relationships.
Out of curiosity… if you are generating persistence code that enforces relationships, and won’t function without those relationships being honored anyway… why not just push those constraints down into the DB itself, and let MyEclipse generate all that for you? Just a thought, I understand if other restrictions may not allow this.
May 2, 2008 at 1:09 pm #284629Well this clears up the problems, thanks.
To answer your question, using FK constraints tends to often get in the way since the DB “inserts itself” into operations. I’d rather do “RI” (referential integrity) in code and transactions rather than in the DB itself.
Maybe MyE could read the metadata in some future release and give a reasonable dialog to the user (wizard) to help set things up.
It’s a shame it’s all (FK constraints) or nothing (set up the mapping relationships yourself) without some type of wizard option between these two extremes.
Thanks as always for your excellent replies and help!!!
Best,
M
May 2, 2008 at 2:12 pm #284631
Riyad KallaMemberMork I know what you mean, there is room for improvement here and I’ll report this to the persistence team and see if they can figure out ways to improve it in the future.
Again sorry for the long diagnostic time, didn’t mean to eat up so much of your time with that.
May 2, 2008 at 5:41 pm #284638Actually, I want to thank you for helping me and spending all the time you did (posting things, trying things, etc.).
You’ve *always* been superb.
Please have a great weekend!!!!!!!
Thanks again.
–M
May 5, 2008 at 10:57 am #284673
Riyad KallaMemberMork,
I copied over your reply from here:I guessed you meant to post it at the end of this thread instead, I’m just moving it here for anybody else following this thread: https://www.genuitec.com/forums/topic/having-trouble-setting-up-hibernate-with-myeclipse/#post-284642
I meant to also ask you if the setup you created to test the one-to-many relationship actually populated the “many” side?
I created a foreign key constraint and got the expected elements in the config files, but the many side isn’t populated.
The user table code is working fine, but the many table (uservisithistory) doesn’t get populated.
I think it may be in the mapping files.
I have the PK generation set to automatic in MySQL with no nulls, etc.
Here are the updated mapping files (and the code that doesn’t work):
Users Table
code:
<hibernate-mapping>
<class name=”TestProject.Users” table=”users” catalog=”users”>
<id name=”usrPk” type=”java.lang.Integer”>
<column name=”usr_pk” />
<generator class=”increment” />
</id>
<property name=”usrIsPaidUser” type=”java.lang.Byte”>
<column name=”usr_isPaidUser” not-null=”true” />
</property>
<property name=”usrAccountExpires” type=”java.lang.Byte”>
<column name=”usr_accountExpires” not-null=”true” />
</property>
<property name=”usrAccountExpireDate” type=”java.util.Date”>
<column name=”usr_accountExpireDate” length=”19″ />
</property>
<property name=”usrFname” type=”java.lang.String”>
<column name=”usr_fname” length=”20″ not-null=”true” />
</property>
<property name=”usrMi” type=”java.lang.String”>
<column name=”usr_mi” length=”1″ />
</property>
<property name=”usrLname” type=”java.lang.String”>
<column name=”usr_lname” length=”25″ not-null=”true” />
</property>
<property name=”usrEmail” type=”java.lang.String”>
<column name=”usr_email” length=”45″ />
</property>
<property name=”usrPhone” type=”java.lang.String”>
<column name=”usr_phone” length=”25″ />
</property>
<property name=”usrLastlogindate” type=”java.util.Date”>
<column name=”usr_lastlogindate” length=”19″ />
</property>
<property name=”usrAccountactive” type=”java.lang.Byte”>
<column name=”usr_accountactive” not-null=”true” />
</property>
<property name=”usrLogin” type=”java.lang.String”>
<column name=”usr_login” length=”45″ not-null=”true” />
</property>
<property name=”usrPassword” type=”java.lang.String”>
<column name=”usr_password” length=”20″ not-null=”true” />
</property>
<set name=”uservisithistories” inverse=”true”>
<key>
<column name=”usr_pk” not-null=”true”>
<comment>foreign key back to users table.</comment>
</column>
</key>
<one-to-many class=”TestProject.Uservisithistory” />
</set>
</class>
</hibernate-mapping>Here is the mapping for the user visit history (many side) table:
code:
<hibernate-mapping>
<class name=”TestProject.Uservisithistory” table=”uservisithistory” catalog=”users”>
<id name=”uvhPk” type=”java.lang.Integer”>
<column name=”uvh_pk” />
<generator class=”increment” />
</id>
<many-to-one name=”users” class=”TestProject.Users” fetch=”select”>
<column name=”usr_pk” not-null=”true”>
<comment>foreign key back to users table.</comment>
</column>
</many-to-one>
<property name=”uvhDateTimeOfVisit” type=”java.util.Date”>
<column name=”uvh_dateTimeOfVisit” length=”19″ />
</property>
<property name=”uvhPageFrom” type=”java.lang.String”>
<column name=”uvh_pageFrom” length=”45″ not-null=”true” />
</property>
</class>
</hibernate-mapping>The code to create the many side I do inside a test “main” method like this:
(see: “// add new visit history record” below)code:
// transaction instance created earlier…
Users newUser = new Users();
newUser.setUsrAccountactive((byte) 1);newUser.setUsrAccountExpires((byte)0);
// // set expiration date from one year from now
Calendar now = Calendar.getInstance();
now.add(Calendar.YEAR, 1);
Date dateToStore = now.getTime();
newUser.setUsrAccountExpireDate(dateToStore);// set user first name and last name
newUser.setUsrFname(“admin”);
newUser.setUsrLname(“admin”);// user doesn’t pay.
newUser.setUsrIsPaidUser((byte) 0);// set user login/password
newUser.setUsrLogin(“admin@test.net”);
newUser.setUsrPassword(“123456”);// add new visit history record
// ****** CODE BELOW DOES NOT WORK -> No Population in many tableUservisithistory uvh = new Uservisithistory();
uvh.setUsers(newUser);
uvh.setUvhDateTimeOfVisit(dateToStore);
uvh.setUvhPageFrom(“home page”);
HashSet<Uservisithistory> set = new HashSet <Uservisithistory>();
set.add(uvh);newUser.setUservisithistories(set);
// save user
dao.save(newUser);
transaction.commit();}
The strange thing to me is that the mapping file I generated has a “Users” element in the many side.
I think the reason it might not be populating the many side is that it needs the foreign key to insert into the user visit history table. However, since the PK in the users table gets generated by MySQL automatically, I’m not sure how this works or why the code isn’t populating the many side.
Does the code, having a Set in the users table, and a Users object in the uservisithistory table, look correct?
Any advice/suggestions would be greatly appreciated.
Since there isn’t an example for MyEclipse on this common usage, I’m trying to verify what I’ve done so far makes sense.
Thanks.
M
May 5, 2008 at 12:20 pm #284679
Riyad KallaMemberMork,
Can you try uvh.setUservisithistories().add when adding the visit, and not setting a new hashset? Did that help? -
AuthorPosts