Andrzej provided an awesome answer as always, but I wanted to expand a bit further.
Your question around using an existing relationship (IE getMboSet("RELATIONSHIP")) or you build it dynamically (IE getMboSet("$tempRelation", "MboSet", "Query")) is there is no difference between the two other than the obvious (one exists in database configuration, and the other is built dynamically in code). These should be treated as equivalent. If you need the same data set in multiple places (automation scripts, UI dialogs, object structure relationships, etc.) you should create it as a relationship in Database Configuration. If you have a completely unique relationship that is only used to fulfill the one automation script, you can avoid creating it in database configuration. Just ensure that your $tempRelation name is extremely unique because if something else uses that name you'll get unexpected behaviors. There was a bug in core Maximo recently where two different things (even across different child objects) used the same $tempRelation name which led to undesirable results.
Whenever possible, you should get sets as child objects. You want the transaction management (save and or rollback if an error occurs) to be automatic with the main object. You also want it to get cleaned up when the originating MBO is cleaned up by the framework. There are ways as Andrezej called out to set the transaction to be consistent but it's adding a lot of extra code to set the transaction, save the set, cleanup, etc. that isn't necessary. And if you fail to do it, the risk of data being in a corrupt state is highly possible.
I see customers/consultants mess this up all the time. This is one of the reasons I loathe after save automation scripts because I see customers try to modify the record that has already been saved (and written to the database, though not yet committed), which requires opening a separate set and going through the save process again. It's horribly inefficient and can lead to data in a corrupt state.
Regarding close on the sets that are opened using MXServer (service.getMboSet is calling the same thing, just simplifies the logic), you want to cleanup() the set. Most people don't know this but calling close on the set is for the database connection. When people complain about database connection leaks, close() is one of the solutions that can help avoid it. It should only be called after you've fetched everything you've needed from the database. There are other practices such as not using isEmpty(), which fetches the first MBO without traversing the entire set, can help avoid the connection leaks as well.
But what's even better is cleanup(). When you are truly done processing a set, cleanup() is preferred. This is what the automation script warning framework will try to confirm exists in your code for a reason. Cleanup helps the framework release the data from memory and also closes the set. While you can call close() and cleanup(), it's not necessary to do both. Calling cleanup() will call reset() which calls resetThis() which calls the close() on the set.
And again, you should neither call close nor cleanup if you are not opening these sets using MXServer.
Regarding the moveNext and such, the concerns there are really around the UI as he mentions. It changes the current index of the set. You'll see a lot of examples I have personally posted where I've done this because it's easier for people to understand. But you can traverse in other ways like MboSetEnumeration. Or if you're in an operation where users select the values they want to process (IE a multiselect dialog) you can call set.getSelection().iterator() to iterate only the records that have been selected.
------------------------------
Steven Shull
Principal Maximo Solutions Engineer
Naviam
Cincinnati OH
------------------------------