I am using Mybatis (3.2.7 version) as an ORM framework for my JAVA project.
As I'm from JPA background, I was keen to explore the LAZYLOADING supported by Mybatis.
But I couldn't get across anything substantial.
(I am configuring MYBATIS using JAVA API and annotations solely for querying purpose)
As per the Mybatis documentation: 1. lazyLoadingEnabled: default value=TRUE
Globally enables or disables lazy loading. When enabled, all relations will be lazily loaded. This value can be superseded for an specific relation by using the fetchType attribute on it.
2. aggressiveLazyLoading : default value=TRUE
When enabled, an object with lazy loaded properties will be loaded entirely upon a call to any of the lazy properties. Otherwise, each property is loaded on demand.
Using the following attributes, I tried the following code:
a. JAVA Classes :
Feedback.java
public class Feedback implements Serializable {
private static final long serialVersionUID = 1L;
private int id;
private String message;
/**
* while loading Feedback, I want sender object to be lazily loaded
*/
private User sender;
private boolean seen;
// getters and setters
}
User.java
public class User implements Serializable, {
private static final long serialVersionUID = 1L;
private int id;
private String email;
// getters and setters
}
b. DB schema:
Feedback table
Table "public.feedback"
Column | Type | Modifiers
-------------+-----------+-------------------------------------------------------
id | integer | PRIMARY KEY
seen | boolean | not null
sender_id | integer | FOREIGN KEY (sender_id) REFERENCES users(id)
message | text |
User Table:
Table "public.users"
Column | Type | Modifiers
-------------+----------+----------------------------------------------------
id | integer | PRIMARY KEY
email | text |
c. Configuring MyBatis via JAVA API:
DataSource dataSource = new PGSimpleDataSource();
((PGSimpleDataSource) dataSource).setServerName("localhost");
((PGSimpleDataSource) dataSource).setDatabaseName(dbName);
((PGSimpleDataSource) dataSource).setPortNumber(5432);
((PGSimpleDataSource) dataSource).setUser(new UnixSystem().getUsername());
((PGSimpleDataSource) dataSource).setPassword("");
TransactionFactory transactionFactory = new JdbcTransactionFactory();
Environment environment = new Environment(dbName, transactionFactory, dataSource);
Configuration configuration = new Configuration(environment);
configuration.addMapper(FeedbackMapper.class);
//
configuration.setAggressiveLazyLoading(false);
sqlSessionFactory = new SqlSessionFactoryBuilder().build(configuration);
d. Querying DB and DB Queries in Feedbackmapper:
d.1 Code in Feedbackmapper:
@Select("SELECT f.id, f.message, f.seen, f.sender_id FROM feedback f WHERE f.id= #{feedbackId}")
@Results(value = {
@Result(property = "id", column = "id"),
@Result(property = "sender", column = "sender_id", javaType = User.class, one = @One(select = "getUser", fetchType=FetchType.DEFAULT))
})
public Feedback getFeedback(@Param("feedbackId") int feedbackId);
@Select("SELECT id, email FROM users WHERE id=#{id}")
public User getUser(int id);
d.2: Code to invoke the queries in feedbackMapper
// setup Mybatis session factory and config
Feedback feedback =feedbackMapper.getFeedback(70000);
System.out.println(feedback);
But still the "sender" object is populated upon querying the getFeedback(id). I expect the sender object shouldn't be populated immediately but only when I call getSender() on the fetched feedback object . Please help.
My recent Observations:
Mybatis team has indeed got it wrong in their documentation ie in documentation:
lazyLoadingEnabled: default value=TRUE
aggressiveLazyLoading : default value=TRUE
But looking at their source code:
protected boolean lazyLoadingEnabled = false;
protected boolean aggressiveLazyLoading = true;
**However that being corrected, the results are not affected and lazy loading isnt working :( **
Lazy loading is a technique that enhances the efficiency of the program. In this section, we will discuss lazy loading in detail. Virtual proxy is the memory saving process that promotes the postponement of object creation. Observe the following program. FileName: LazyLoadingExample.java
If a property exists in more than one of these places, MyBatis loads them in the following order: Properties specified in the body of the properties element are read first, Properties loaded from the classpath resource or url attributes of the properties element are read second, and override any duplicate properties already specified,
Again though, this could have a severe impact on the behaviour of MyBatis, so use caution. MyBatis can be configured with multiple environments. This helps you to apply your SQL Maps to multiple databases for any number of reasons. For example, you might have a different configuration for your Development, Test and Production environments.
StatementHandler (prepare, parameterize, batch, update, query) The details of these classes methods can be discovered by looking at the full method signature of each, and the source code which is available with each MyBatis release.
I think I found a way to enable the lazyloading (though not cent-percent sure):
Setting : lazyLoadTriggerMethods
Description : Specifies which Object's methods trigger a lazy load
Valid Values : A method name list separated by commas
Default: equals,clone,hashCode,toString
As per the source code, this thing maps properly to what is given in documentation:
public class Configuration {
// other attributes + methods
protected Set<String> lazyLoadTriggerMethods = new HashSet<String>(Arrays.asList(new
String[] { "equals", "clone", "hashCode", "toString" }));
}
I have changed the Mybatis configuration to follows:
TransactionFactory transactionFactory = new JdbcTransactionFactory();
Environment environment = new Environment(dbName, transactionFactory, dataSource);
Configuration configuration = new Configuration(environment);
/**
*This is the cause why LAZY LOADING is working now
*/
configuration.getLazyLoadTriggerMethods().clear();
///////////////////////////////////////////////////
configuration.setLazyLoadingEnabled(true);
configuration.setAggressiveLazyLoading(false);
The queries in Mapper (mostly unchanged):
@Select("SELECT id, message, seen, sender_id
FROM feedback WHERE f.id= #{feedbackId}")
@Results(value = {
@Result(property = "id", column = "id"),
@Result(property = "sender", column = "sender_id", javaType = User.class, one = @One(select = "getUser"))
// Set fetchType as DEFAULT or LAZY or don't set at all-- lazy loading takes place
// Set fetchType as EAGER --sender Object is loaded immediately
})
public Feedback getFeedback(@Param("feedbackId") int feedbackId);
@Select("SELECT id, email FROM users WHERE id=#{id}")
public User getUser(int id);
- JAVA Code to invoke mapper
FeedbackMapper mapper = sqlSession.getMapper(FeedbackMapper.class);
Feedback feedback =mapper.getFeedback(69999);
System.out.println("1. Feedback object before sender lazily load: \n"+ feedback);
System.out.println("2. Sender loaded explicitly \n" +feedback.getSender());
System.out.println("3. Feedback object after sender loading \n" + feedback);
1. Feedback object before sender lazily load:
{id : 69999, message : message123, sender : null, seen : false}
2. Sender loaded explicitly
{id : 65538 , email: [email protected]}
3. Feedback object after sender loading:
{id : 69999, message : message123, sender : {id : 65538, email : [email protected]},
seen : false}
configuration.getLazyLoadTriggerMethods().clear();
However to lack of documentation in Mybatis, I'm not sure, whether this is associated with any drawbacks as such.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With