DBRow is probably the most important class in DBvolution, and you will use it a lot.
So it's important to get it right. Here are the best practices line by line.
The simple solution is to use DBTableClassGenerator as it automatically generates good code. However DBV started with the idea that DBRow would be done by hand like a normal class, and it was only after I saw 900+ views and tables that DBTableClassGenerator was invented.
Let's start with the CarCompany class from the DBV examples package:
import nz.co.gregs.dbvolution.datatypes.DBString; import nz.co.gregs.dbvolution.datatypes.DBInteger; import nz.co.gregs.dbvolution.*; import nz.co.gregs.dbvolution.annotations.*; @DBTableName("car_company") public class CarCompany extends DBRow { public static final long serialVersionUID = 1L; @DBColumn("name") public DBString name = new DBString(); @DBPrimaryKey @DBColumn("uid_carcompany") public DBInteger uidCarCompany = new DBInteger(); public CarCompany() { } public CarCompany(String anme, int id) { this.name.setValue(anme); this.uidCarCompany.setValue(id); } }
The imports right at the top should be pretty obvious: they're the DBV classes actually used in CarCompany. You can add these as you go or use the collection here as a safe starting set.
@DBTableName("car_company")
@DBTableName annotation is the first specifically DBV feature of the class, and I recommend you use it. Technically it's optional if the class name matches the database table name but even in that case it's better to specifically name the table so that refactoring doesn't accidentally break the connection.
We've renamed an enormous number of classes because the table names don't fit Java syntax or reflect the actual behaviour and @DBTableName protects the important information.
Note that @DBTableName takes a single string that is the exact table name as the database defines it. Just look it up in the schema and drop exactly what is there into @DBTableName. DBV doesn't make any changes, what you see is what you get.
public class CarCompany extends DBRow {
"Public class CarCompany" is standard java, but remember DBV requires many things to be public so it can access them and create new instances. Make sure your DBRow subclasses are public and everything will work nicely.
"extends DBRow" is incredibly important, since it's not a DBRow subclass without it and you'll miss out on all the useful methods of DBRow and DBV won't be able to create new instances and set the fields correctly.
public static final long serialVersionUID = 1L;
I have yet to find a line quite as annoying in Java as this one. However Java seems to like it. it's completely optional and nothing to do with DBV, so use it if you want.
@DBColumn("name") public DBString name = new DBString();
Technically this is one line but all the IDEs treat it as two. There are 3 sections to it:
@DBColumn("name") public DBString name = new DBString();
The first is a lot like @DBTableName, however it has acquired a lot of extra importance and you will need @DBColumn to designate fields that exist on the database. The "name" part is still optional if the field name equals the column name but it is highly recommended that you specify it anyway. Refactoring could accidentally break the class, and @DBColumn protects you from that.
@DBColumn can also be specified on JavaBean style methods, but more on that another time.
Please note that @DBTableName and @DBColumn are almost all of the configuration required by DBV. DBvolution eliminates all the convoluted configuration files used in other products.
public DBString name
The next line specifies the protection and the class of the field as well as the field name. The field name is your choice however the protection should be public and class should be a QueryableDatatype (QDT) subclass such as DBString, DBNumber, DBInteger, DBDate, or DBByteArray.
There are TypeAdaptors which remove the need for QDTs but that is a whole other post. Coming soon hopefully. For the moment 99% of your query needs is covered using QDTs so I recommend them.
= new DBString();
The third line is not strictly necessary, but lots of our tests assume it is there. The alternative is to place the initialisation into the default constructor, which works fine, but also seems like extra typing for no reason. Again TypeAdaptors change the situation a bit but early initialisation avoids null pointer exceptions so I recommend it.
@DBPrimaryKey @DBColumn("uid_carcompany") public DBInteger uidCarCompany = new DBInteger();
This section is just like the previous one except @DBPrimaryKey designates this field as the primary key. Primary key fields are the unique identifier used in databases to find the exact row referenced. DBV uses them often but they aren't strictly required. However if you have a @DBForeignKey to this class, you'll want it to have a @DBPrimaryKey field.
The simple rule is: if there is a primary key column then define it with @DBPrimaryKey on the field.
This field also uses DBInteger instead of DBString so there is that too.
public CarCompany() { }
DBvolution loves default constructors, they're all over the place. So always include one in your DBRow subclass. Sadly Java doesn't let me use the super constructor so you need to write it.
public CarCompany(String anme, int id) { this.name.setValue(anme); this.uidCarCompany.setValue(id); }
This is just a convenience constructor for the programmer and not required or used by DBV.
CarCompany is nice and short but it doesn't cover 2 things important to database programming: BLOBs and Foreign Keys.
Here's an example of the BLOB column from CompanyLogo:
@DBColumn("image_file") public DBByteArray imageBytes = new DBByteArray();
That is absolutely everything required to define a BLOB column within DBV. The only requirement beyond any other field is the use of a DBlargeObject subclass, such as DBByteArray. This example is for a file, if you're storing actual Java objects use DBJavaObject.
Here's an example of a foreign key reference from CompanyLogo:
@DBForeignKey(CarCompany.class) @DBColumn("car_company_fk") public DBInteger carCompany = new DBInteger();
The difference here is the inclusion of @DBForeignKey(CarCompany.class) which captures the FK relationship information. This specifies that the information in this field relates to the information in the primary key field (see @DBPrimaryKey above) in the CarCompany class. Note that foreign keys always refer to the primary key. Also remember the ".class" otherwise your compiler will complain.
So use @DBTableName and @DBColumn; extend DBRow; keep it public; use QueryableDatatypes; have a default constructor and everything will be fine :)
Anonymous