Select language: English | Chinese
BabyFish is a Java Framework that can let developers create smart data structure only with declartion code. It have many functionalities, please view the following picture to know its general architecture.
In the picture, 5 functionalities are marked by red star:
, they are the most most important 5 functionalities of BabyFish.BabyFish1.1 requires Java8.
Document | Description |
---|---|
tutorial.html(English) | Describes some important functions in detail, with both characters and illustrations (Please use high version IE or other browsers to open it). |
tutorial_zh_CN.html(Chinese) | |
demo-guide.html(English) | Introduces each demo one by one, and gives the recommended reading order (Please use high version IE or other browsers to open it). |
demo-guide_zh_CN.html(Chinese) |
-
-
- Better way to support I18N. Be different with "java.util.ResourceBundle" that reports the errors at runtime, all the errors of "Typed18N" will be reported at compilation-time (This functionality requires the compilation-time byte-code instrument).
- Better way to support event nodification, like the delegate of .NET Framework. It's more simple, More economical and more powerful than the suggestion of Java Bean Standard (This functionality requires the compilation-time byte-code instrument).
-
The developers often override the method "public boolean equals(Object o)",
they often have two choices to check whether type of argument is same with the current object(this):
- Too strict way: if (this.getClass() != o.getClass()) return false;
- Too lax way: if (!(o instanceof ${ThisClass})) return false;
- This functionality is used to visit a object graph by depth first way or breadth first way, during the visiting, it supports many context variables.
-
-
"X" means "eXtensible", X Collection Framework extends the interfaces of Java Collection Framework and gives a new implementation to support some incredible functionalities.
Here we only disuccss two most important functionalities of X Collection Framework (Please see demo-guide to know all functionalities).
- Like "org.apache.commons.collections4.BidiMap", X Collection Framework support BidiMap to let "java.util.Map" supports unique constraint of value(not key); In a similar way, BidiList is supported too.
-
1.2.1.2. Unstable Collection Elements
Java Framework supports hash structure and red black tree via HashSet, HashMap, TreeSet and TreeMap, their benefit is high peformance but defect is an object cannot be changed since it has been added into collections as Set element or Map key.
X Collection Framework supports "Unstable Collection Elements", an object can still be changed even if it has been added into Set as element added into Map as key, because all the sets and maps will be adjusted automatically when this object is changed.
Collection Materialize of "Unstable Collection Elements" Set Unstable elements Map Unstable keys BidiList Unstable elements BidiMap Unstable keys Unstable values
-
"MA" means "Modification Aware", MA Collection Framework extends X Collection Framework to support the modification event nodification. For each modified element or key-value pair, a couple of events will be triggered, one is triggered before the modification and the other one is triggered after the modification. This functionality looks like the row level trigger of RDMBS very much.
Here we only disuccss two most important functionalities of MA Collection Framework (Please see demo-guide to know all functionalities).
- For BabyFish Collection Framework, not only the explicit collection modification API invocation can change the data of collection object, but also the collection object can be modified by the system automatically. The most typical representative is automatic collection adjusting caused by "Unstable Collection Elements". Events can still be triggered by the modification that isn't caused by developer and is automatic, implicit.
-
1.2.2.2 Bubble Event
Java Colletion Framework support collection views, for example:
- "java.util.NavigableMap" supports methods headMap, tailMap, subMap and descendingMap.
- "java.util.NavigableSet" supports methods headSet, tailSet, subSet, descendingSet and descendingIterator.
- "java.util.List" supports methods subList and listIterator.
- "java.util.Map" supports keySet, methods values and entrySet.
- "java.util.Collection" supports method iterator.
MA Collection Framework supports "Bubble Event", when the collection view is changed by the developer, the event will be triggered by the view itself; then the event will be bubbled to its parent view, then a new event will be triggered by the parent view. By parity of reasoning, finally, the event will bubbled to root and new event will be triggered by original collection.
-
As we know, ORM frameworks often support a technology called "Lazy Loading". Lazy collection is fake collection without data, it can become the real collection by IO reading when it's used by developer at first time. This functionality is very powerful, it's very wasteful if it can only be used by ORM frameworks.
In order to let this powerful functionality can be used anywhere(Not only by ORM implementation), babyfish support Lazy Collection Framework which is abstract and universal.
No demo for it, it's not API of babyfish, it's SPI of babyfish.
-
Java Collection Framework has very powerful tool class "java.util.Collections", this class supports many static methods to create magical collection proxies.
BabyFish Collection Framework extends the interfaces of Java Collection Framework, similarly, it must support its own tool class too: "org.babyfish.collection.MACollections".
-
-
ObjectModel is the core functionality of BabyFish, it the reason why I created this framework.
ObjectModel is used to support "Smart Data Structcure", it can be splitted to two parts
- ObjectModel4Java: Smart data structure for java language, this is being discussed by this chapter.
- ObjectModel4JPA: Smart data structure for JPA entity classes, this will be discussed by next chapter.
ObjectModel4Java has many functionalities, this document only introduces two of them: 1, Consistency of bidirectional association; 2, Unstable objects. (Please see demo-guide to know all functionalities).
Java developers often declare the data classes without any logic except getters and setters (Bluntly, they're C-structures), the data structure described by these simple classes does not have enough conveniences and intelligent. For a simple example, there is a bidirectional association between two objects, when one side of that association is modified by the developer, the other side won't be changed, so the data structure become inconsistent, we can navigate to object B from object A, but cannot navigate to object A from object B.
ObjectModel4Java adds the intelligent into data structure, but it does not increase the code complexity, after all, the classes without any logic except getters and setters is so easy to be developed that they are accepted by every one. ObjectModel4Java only requires the developers to add a little annotations to those simple classes, after the compilation-time bytecode instrument, they will become powerful data classes.
Let's see an example, declare the bidirectional association between department and employees.
-
One department has many employees.
@Model //Uses ObjectModel4Java, requires compilation-time bytecode instrument. public class Department {
//One side of bidirectional association, //this field is the mirroring of "Employee.department". @Association(opposite = "department") private List<Employee> employees; //List has order. The getters and setters are omitted
}
-
Each employee belongs to a department, and it also know its position in the list of its department.
@Model //Uses ObjectModel4Java, requires compilation-time bytecode instrument. public class Employee {
//Other side of bidirectional association, //this field is the mirroring of "Department.employees". @Association(opposite = "employees") private Department department; //Employee belongs to a department, if not, it's null // This field is attached to "Employee.department", it's the index of current object in list of // parent department object. If this object does not belong to any department object, it's -1. @IndexOf("department") private int index; The getters and setters are omitted
}
Now, there are 6 objects, their variable names are department1, department2, employee1, employe2, employee3 and employee4, this is the data stucure:
Department object Employee object Variable name employees Variable name department index department1 [ employee1, employee2 ] employee1 department1 0 employee2 department1 1 department2 [ employee3, employee4 ] employee3 department2 0 employee4 department2 1 Then the developer modifies the "employees" collection of "department1", like this
department1.getEmployees().add(0, employee3);
This code inserts the "employee3" into the head of "department1.employees", In order to preserve the consistency of data strucure, ObjectModel4Java will do these works:- Remove "employee3" from the collection "department2.employees" automatically.
- Assign the "employee3.department" to be "department1" automatically.
- Increase "employee1.index" automatically, from 0 to 1.
- Increase "employee2.index" automatically, from 1 to 2.
- Decrease "employee4.index" automatically, from 1 to 0.
Finally, the data structure become:
Department Object Employee Object Variable name employees Variable name department index department1 [ employee3, employee1, employee2 ] employee1 department1 1 employee2 department1 2 employee3 department1 0 department2 [ employee4 ] employee4 department2 0 -
In previous demo, the collection field uses "java.util.List", of course, "java.util.Set" and "java.util.Map" can be used too. In this demo, we use "java.util.Set" to decribe the bidirectional many-to-many association between companies and investors.
-
A company can be jointly funded by multiple investors.
@Model //Uses ObjectModel4Java, requires compilation-time bytecode instrument. public class Company {
@Scalar //Scalar, not association. private String shortName; @Association(opposite = "companies") // The annotation "@ComparatorRule" means this set collection uses the field "Investor.name" // to calculate the hashCode of investor and check whether two investors are equal. @ComparatorRule(properties = @ComparatorProperty("name")) private Set<Investor> investors; The getters and setters are omitted
}
-
An investor can invest in multiple companies.
@Model //Uses ObjectModel4Java, requires compilation-time bytecode instrument. public class Investor {
@Scalar //Scalar, not association. private String name; @Association(opposite = "investors") private Set<Company> companies; The getters and setters are omitted
}
Now there are 3 object; one of them is an "Company" object whose variable name is "apple", the other two are "Investor" objects whose variable name are "steve" and "sulley". The data structure is:
Company object Investor object Variable name shortName investors Variable name name companies apple Apple [ steve, sculley ] steve Steve [ apple ] sculley Sculley [ apple ] Now, the develop execute this code:
sculley.setName("Steve");
After execute this code, The value of "sculley.name" will be same with the value of "steve.name", both of them are "Steve"; But the collection field investors" of "Company" uses the annotation@ComparatorRule to let this collection cannot contains tow Investor objects with the same name. This is a paradoxical situation.
Fortunately, the field investors" of "Company" supports "Unstable Collection Elements"; that will cause the following effects.
- In order to preserve the unique contraint of the set field, the object "steve" will be crowded out from the collection investors of object "apple" automatically because of "Unstable Collection Elements".
- the object "steve" is longer the investor of "apple", in order to preserve the consistency of bidirectional assocation. the objet "apple" will be removed from the collection companies of object "steve" auotmatically.
Finally, the data structure become:
Company object Investor object Variable name shortName investors Variable name name companies apple Apple [ sculley ] steve Steve [] sculley Steve [ apple ] -
A company can be jointly funded by multiple investors.
-
-
-
2.1. ObjectModel4JPA
ObjectModel4JPA is an enhancement of ObjectModel4Java, it has the same functionalities with ObjectModel4Java, but their objectives are not same, it allows the JPA entity classes to use the ObjectMode, not for the ordinary java classes; their declaration modes are different too, please see demo-guide to know more details.
Be same with ObjectModel4Java, ObjectModel4JPA requires the compilation-time bytecode instrument of Maven plugin too.
-
- In ObjectModel4Java, The collections used by the association field can preserve the consistency of bidirectional assocation, of course, they has the all the functionalities of X Collection Framework and MA Collection Framework too. ObjectModel4JPA has the same functionalities with ObjectModel4Java, The its collections must have those functionalities too.
- ObjectModel4JPA is designed for JPA/Hibernate, so its collections must support the the functionality "Lazy Loadding", like the protogenous JPA/Hibernate.
In order to let the collections of ObjectModel support all of those functionalities, BabyFish support its own Hibernate Collection Framework by extending Lazy Collection Framework.
No demo for it because it's an internal module of babyfish-hibernate-extension. The developer only need to know that the collections of ObjectModel4JPA have both the functionalities of ObjectModel4Java's collections and the functionaltities of Hibernate's collections.
-
In order to add some new functionalities, BabyFish extends the API of JPA and Hibernate. This deocument only introduces two of them: QueryPath and Oracle Optimization (Please see demo-guide to know all functionalities).
-
2.3.1. Query Path
In order to support "dynamic eager fetching"( Association fetches is specified by the arguments of UI or business logic layer, not by the hard code in the data accessing layer), BabyFish supports "Query Path", it looks like these technologies
very much. This functionality is more simpler, more beautiful and more powerful than javax.persistence.EntityGraph which is provided since JPA2.1, please forget it and use Query Path in practical projects.Comparing with Including functionality of Active Record or Including functionality of ADO.NET Entity Framework, The Query Path has 3 advantages.
- In Active Record or ADO.NET Entity Framework, Include Paths are string which isn't type-safe, the errors of Include Paths won't reported until run or test the application; In BabyFish, Query Paths are created by the "Query Path meta-model" whose source code is automatically generated at compilation-time by maven plugin, the errors of Query Paths will be reported as compilation error.
- Not only the object associations graph with unlimited depth and breadth can be fetched dynamically, but also the lazy scalar field(Not not always but often is lob field) can be fetched dynamically.
- It can also be used to sort the objects of query result and sort associations collections.
-
Hibernate has a performance issue: When the query has both collection fetches and paging filter, Hibernate cannot generate paging restriction in SQL statement, all the matched rows will be selected and the paging filter will be applied in memory. This is a big problem so Hibernate writes this line in log file:
"firstResult/maxResults specified with collection fetch; applying in memory!"BabyFish can resolve this problem when the database is Oracle, please uses the dialect provided by BabyFish, such as:
-
-
BabyFish uses the LGPL-3.0 license so that it can be used in commercial projects, please see http://opensource.org/licenses/LGPL-3.0 to know more.
Thank two great frameworks: ASM and ANTLR
- Aug 2008: I have some ideas, I decided to create an open source framework by using all of my spare time.
- Oct 2015: This first version 1.0.0.Alpha is finished, and it's published on github.
- Jun 2016: The version 1.1.0 is finished.
Tao Chen, [email protected]
2016-06-25, ChengDu, China