Of course, it could be a lot better, as right now it is using the file on the filesystem, without the changes made in the editor. But for now it suits me just fine.
Had an issue with how to model some classes that were entities and therefore tables in the database.
I thought I'd write a little blog about it, to get my thoughts in order. The subject matter is not that difficult, once you see how it works.
A guild has ranks, and a person who is a member of a guild can be assigned to a certain rank. In the database that would look like follows. (Courtesy of Mysql-Workbench2)
As you can see there are a number of primary keys, and foreign keys.
The guild is identified by its name.
The guildrank is identified by its ranknumber as well as the guild to which it belongs. Therefore the primary key is a composite of both ranknumber and guild.
The guildrank therefore also has a foreign key constraint to the Guild.
The user is identified by its name.
The user can be a member of a guild, though not mandatory. So there's a foreign key reference to the guild.
The user can have a rank within the guild, though not mandatory. So there's a foreign key reference to the guildrank, by means of the two fields guild and rank.
Note: One of the things that are not currently modelled in this database schema is that it should be impossible to have a guildrank, without being in a guild. The combination rank with an empty guild should be impossible.
Aggregation and Composition
We see here an example of both Aggregation and Composition. User and Guild is an example of an aggregation, they are things of themselves but have a relation. Guild and Guildrank is an example of a composition. The guildrank cannot exist without the guild, and they form a parent-child relationship.
The guild
First the easy one, the Guild. This is the only entity that is standalone, i.e. not dependent on any other entity. This makes it quite easy. It just contains collections of ranks (One guild has potentially many ranks, so OneToMany) and members (One guild has potentially many members, so OneToMany).
These collections are not required, but are convenient.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
As a guildrank has a composite primary key, we require a separate object to store the primary key composite. This is the case for most (all?) ORMs as the 'findByIdentifier' method takes an object.
Once again, seeing as a guildrank is used in exactly one guild, it is the reverse of the relation in the Guild, so ManyToOne.
Many users are able to have the same rank in the guild, and therefore this is a OneToMany relation.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
The User can be in a guild and can have a guildrank.
One user can have at most one guild, so it is the reverse of the relation in the guild, therefore ManyToOne.
One user can have at most one guildrank, so it is the reverse of the relation in the guildrank, therefore ManyToOne.
It seems the hard part is taken care of by the fact that there is a annotation @JoinColumns that is able to take care of table relations to more than one field at the same time.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Note: You might end up with a warning/error like the following:
Exception [EclipseLink-48] (Eclipse Persistence Services - 2.5.0.v20130507-3faac2b): org.eclipse.persistence.exceptions.DescriptorException
Exception Description: Multiple writable mappings exist for the field [User.guild]. Only one may be defined as writable, all others must be specified read-only.
Mapping: org.eclipse.persistence.mappings.ManyToOneMapping[guildrank]
Descriptor: RelationalDescriptor(User --> [DatabaseTable(User)])
In order to prevent this, either the guild or the guild-part of the guildrank will have to be read-only. This is done for the guildrank part, by specifying insertable = false, updatable = false in the JoinColumn.
Note
In general I dislike the use of composite primary keys, and I favour the use of identification by meaningless numbers. That way the name of the guild, for example, is not spread out all over the database in essence duplicating information and making it almost impossible to change. This is also called Database Normalization3.
In Netbeans you can select Actions on your project to perform. There is a coupling between the action and the goals in Maven that are executed3.
These can be changed by going to your Netbeans Project Properties (right-click your project, select properties) - select "Actions" - select "Generate Javadoc".
Then add the plantuml Maven goal, com.github.jeluard:plantuml-maven-plugin:generate. You're likely to end up with the following:
My project "karchangame" is Ant-based, basically because when you create a new project in Netbeans, the Ant configuration is the default.
This has worked well for a long time, until I decided recently to upgrade some of the libraries that I use. Now, in Ant, you just download the libraries you need and put the jar-files in your classpath.
That works fine if your libraries are not complicated. But I noticed that some of my libraries are now dependant on yet other libraries.
In short, I just spent an hour in getting the libraries I need, then getting the required libraries of those libraries, ad infinitum.
Maven takes care of this whole slog, by putting the responsibility for defining the required libraries for a framework/library squarely on the shoulders of that framework/library.
What I was stuck with was finding the best way of changing my Ant-based project into a Maven-based project.
Moving from Ant to Maven
The easiest way that I could come up with is to create a brand new Maven-based project. The original was a Web Application, so the new Maven project should also be a Web Application. As far as I could tell every possibility for a new ant-based project is also available as a new maven-based project.
And then start moving files over to the appropriate place in the new Maven structure.
I really like the fact that Git actually detects these moves instead of like in the old days, when a move was an explicit delete and create of two non-related files, making you lose your entire history of that file.
The difference in the directory structure is as follows:
You do notice that Maven actually has a more layered structure, whereas Netbeans Ant basically dumps everything in the root.
So, the move basically entailed the following:
From ant
To Maven
build.xml
pom.xml
-
nb-configuration.xml
nbproject
-
lib
- (actually stored in your m2 repo)
src/conf
src/main/resources
src/java
src/main/java
web
src/main/webapp
test
src/test/java
build
target
dist/karchangame.war
target/karchangame-1.0-SNAPSHOT.war
Pom.xml
I only needed to make a few changes to my pom.xml file, in order to get all the dependencies sorted out.
JMockit
Needed to add JMockit, or my testcode didn't compile.
To effect this globally, we'll need to change it in a gconf database. The database used are dependent on who is logged on, but we don't want that.
The file /etc/gconf/2/path will show in which paths the database is consulted. The paths are in order of precedence. This means if an entry is found in one of the first databases, the entry is ignored in one of the latter databases.
By default there is a Mandatory Source, a User Source and a Defaults Source5. They are:
xml:readonly:/etc/gconf/gconf.xml.mandatory
xml:readwrite:$(HOME)/.gconf
xml:readonly:/etc/gconf/gconf.xml.defaults
If you wish you can set the logon message manually, using the gconf-editor tool. If you start it up as root, you will be able to select under "File" different profiles, namely the "Defaults" one and the "Mandatory" one.
Redhat Enterprise Linux 7
The new new way! People are migrating from GConf (gconftool-2, gconf-editor) over to GSettings3 and dconf (dconf-tool, dconf-editor).
Fedora 20
The workaround for Fedora, because the new new way doesn't work6.
Create file /etc/dconf/db/gdm.d/01-mysettings:
[org/gnome/login-screen]
banner-message-enable=true
banner-message-text='hostname: wiggins\n“How often have I said to you that when you have eliminated the impossible,\n whatever remains, however improbable, must be the truth?”\n\n- Sherlock Holmes, The Sign of the Four (1890)'
Don't forget to run, to recreate the database with the new settings:
rm /etc/dconf/db/gdm
dconf update
References
[1] Linux: Display a login banner gfor Gnome (GDM) manager