MyBatis DAO Example Code Tutorial

r囧r小猫 2022-08-09 05:16 237阅读 0赞



MyBatis DAO Example Code Tutorial

This tutorial will show how how to integrate MyBatis with the Data Access Object pattern (DAO) and MySQL for use in Java Web Apps.

In case you’re not familiar, MyBatis is the new version of the iBatis Data Mapper Java Framework, which allows you to use a relational database with object-oriented applications.

If you’re not familiar with the DAO pattern or the benefits of using it read more about it hereGeneric implementation of the DAO pattern.

Full code of this mybatis dao example tutorial is available on github here.

Step 1 - Define the interface

Create an IParentDAO interface to define the main method calls for all objects. This interface can be used as the parent for various DAO implementations. Here are the default CRUD methods that all objects will have

  1. get(id)
  2. getByName(name)
  3. getAll()
  4. create(obj)
  5. update(object)
  6. delete(id)

Create the IParentDAO.java class

  1. public interface IParentDAO<T, PK>{
  2. publicT get(PK id) throwsPersistenceException;//get obj of type T by the primary key 'id'
  3. publicT getByName(String name) throwsPersistenceException;//get obj of type T by the 'name' field, if one exists for that table
  4. publicArrayList<T> getAll() throwsPersistenceException;//get all objects of type T
  5. publicint create(T objInstance)throws PersistenceException;//insert an object of type T into the database
  6. intupdate(T transientObject) throwsPersistenceException; //update an object of type T
  7. intdelete(PK id) throwsPersistenceException;//delete an object of type T
  8. }

Step 2 - Create the base class

Create the MyBatisDAO abstract base class. This will be the default MyBatis implementation of the DAO. You can of course write a different one for JDBC, Spring etc.Feel free to remove the logging code if you don’t need it. I usedLogback for that.

  1. import java.util.ArrayList;
  2. import org.apache.ibatis.exceptions.PersistenceException;
  3. import org.apache.ibatis.session.SqlSession;
  4. import org.apache.ibatis.session.SqlSessionFactory;
  5. import org.slf4j.Logger;
  6. import org.slf4j.LoggerFactory;
  7. /**
  8. * Class contains all the basic CRUD related methods which are inherited by all objects
  9. * Children daos should generally not overwrite these method but add extra ones as needed.
  10. */
  11. public abstract class MyBatisDAO<T, PK> implements IParentDAO<T, PK>{
  12. private static Logger log = LoggerFactory.getLogger(MyBatisDAO.class);
  13. private static final String NAMESPACE = "mappers";
  14. private SqlSessionFactory sf; //reference to mybatis session factory
  15. private Class<T> type;
  16. /**
  17. * Define prefixes for easier naming convetions between XML mapper files and the DAO class
  18. **/
  19. public static final String PREFIX_SELECT_QUERY = "get"; //prefix of select queries in mapper files (eg. getAddressType)
  20. public static final String PREFIX_INSERT_QUERY = "create"; //prefix of create queries in mapper files (eg. createAddressType)
  21. public static final String PREFIX_UPDATE_QUERY = "update"; //prefix of update queries in mapper files (eg. updateAddressType)
  22. public static final String PREFIX_DELETE_QUERY = "delete"; //prefix of delete queries in mapper files (eg. deleteAddressType)
  23. /** Default Constructor */
  24. public MyBatisDAO(Class<T> type,SqlSessionFactory sf) {
  25. this.type = type;
  26. this.sf = sf;
  27. if(sf==null)
  28. log.error("Error: Could not instantiate MyBatisDAO. Loading myBatis sessionFactory failed.");
  29. }
  30. /** Use this method to get a session factory for using in any methods impelmented in child dao classes */
  31. protected SqlSessionFactory getSessionFactory() {
  32. return sf;
  33. }
  34. /**
  35. * Default get by id method.
  36. * </br></br>
  37. * Almost all objects in the db will
  38. * need this (except mapping tables for multiple joins, which you
  39. * probably shouldn't even have as objects in your model, since proper
  40. * MyBatis mappings can take care of that).
  41. * </br></br>
  42. * Example:
  43. * </br>
  44. * If your DAO object is called CarInfo.java,
  45. * the corresponding mapper query id should be: <select id="getCarInfo" ...
  46. */
  47. public T get(PK id) throws PersistenceException {
  48. SqlSession session = sf.openSession();
  49. T obj = null;
  50. try
  51. {
  52. String query = NAMESPACE+"."+PREFIX_SELECT_QUERY+this.type.getSimpleName(); //If the object's calls name is AddressType.java, this matches the mapper query id: "namespace.getAddressType"
  53. obj = (T)session.selectOne(query,id);
  54. }
  55. finally
  56. {
  57. session.close();
  58. }
  59. return obj;
  60. }
  61. /**
  62. * Method returns all rows for this object.
  63. * </br></br>
  64. * Example:
  65. * </br>
  66. * If your DAO object is called CarInfo.java,
  67. * the corresponding mapper query id should be: <select id="getAllCarInfo" ...
  68. * </br></br>
  69. * SQL Executed: select * from [tablename]
  70. * </br></br>
  71. * Notes:
  72. * </br>
  73. * Consider overdiding this method in order to handle large numbers of objects
  74. * with multiple references.
  75. * LAZY LOADING should be enabled in this case, otherwise you might run out of memory (eg. get all UserAccounts if the table has 1,000,000 rows)
  76. * look into the aggresiveLazyLoading property
  77. * */
  78. public ArrayList<T> getAll() throws PersistenceException {
  79. SqlSession session = sf.openSession();
  80. ArrayList<T> list = null;
  81. try
  82. {
  83. String query = NAMESPACE+"."+PREFIX_SELECT_QUERY+"All"+this.type.getSimpleName();
  84. list = (ArrayList<T>)session.selectList(query);
  85. }
  86. finally
  87. {
  88. session.close();
  89. }
  90. return list;
  91. }
  92. /**
  93. * Method returns first object which matches the given name (exact match).
  94. * </br></br>
  95. * It's up to you to decide what constitutes an object's name. Typically you would have a
  96. * NAME column in the table, but not all objects have this. Generally this method should be overriden (if you need it at all)
  97. * in the child dao class.
  98. * </br></br>
  99. * Example:
  100. * </br>
  101. * If your DAO object is called CarInfo.java,
  102. * the corresponding mapper query id should be: <select id="getCarInfoByName" ...
  103. * </br></br>
  104. * SQL Executed (example): select * from [tablename] where NAME = ?
  105. *
  106. */
  107. public T getByName(String name) throws PersistenceException {
  108. SqlSession session = sf.openSession();
  109. T obj = null;
  110. try
  111. {
  112. String query = NAMESPACE+"."+PREFIX_SELECT_QUERY+this.type.getSimpleName()+"ByName";
  113. obj = (T)session.selectOne(query,name);
  114. }
  115. finally
  116. {
  117. session.close();
  118. }
  119. return obj;
  120. }
  121. /**
  122. * Method inserts the object into the table.
  123. * </br></br>
  124. * You will usually override this method, especially if you're inserting associated objects.
  125. * </br>
  126. * Example:
  127. * </br>
  128. * If your DAO object is called CarInfo.java,
  129. * the corresponding mapper query id should be: <insert id="createCarInfo" ...
  130. * </br></br>
  131. * SQL Executed (example): insert into [tablename] (fieldname1,fieldname2,...) values(value1,value2...) ...
  132. *
  133. */
  134. public int create(T o) throws PersistenceException{
  135. SqlSession session = sf.openSession();
  136. Integer status = null;
  137. try
  138. {
  139. String query = NAMESPACE+"."+PREFIX_INSERT_QUERY+o.getClass().getSimpleName();
  140. status = (Integer)session.insert(query,o);
  141. session.commit();
  142. }
  143. finally
  144. {
  145. session.close();
  146. }
  147. return status;
  148. }
  149. /**
  150. * Method updates the object by id.
  151. * </br></br>
  152. * You will usually override this method. But it can be used for simple objects.
  153. * </br>
  154. * Example:
  155. * </br>
  156. * If your DAO object is called CarInfo.java,
  157. * the corresponding mapper query id should be: <update id="updateCarInfo" ...
  158. * </br></br>
  159. * SQL Executed (example): update [tablename] set fieldname1 = value1 where id = #{id}
  160. *
  161. */
  162. public int update(T o)throws PersistenceException {
  163. SqlSession session = sf.openSession();
  164. Integer status = null;
  165. try
  166. {
  167. String query = NAMESPACE+"."+PREFIX_UPDATE_QUERY+o.getClass().getSimpleName();
  168. status = session.update(query,o);
  169. session.commit();
  170. }
  171. finally
  172. {
  173. session.close();
  174. }
  175. return status;
  176. }
  177. /**
  178. * Method deletes the object by id.
  179. * </br></br>
  180. * Example:
  181. * </br>
  182. * If your DAO object is called CarInfo.java,
  183. * the corresponding mapper query id should be: <delete id="deleteCarInfo" ...
  184. * </br></br>
  185. * SQL Executed (example): update [tablename] set fieldname1 = value1 where id = #{id}
  186. *
  187. */
  188. public int delete(PK id) throws PersistenceException{
  189. SqlSession session = sf.openSession();
  190. Integer status = null;
  191. try
  192. {
  193. String query = NAMESPACE+"."+PREFIX_DELETE_QUERY+this.type.getSimpleName();
  194. status = session.delete(query,id);
  195. session.commit();
  196. }
  197. finally
  198. {
  199. session.close();
  200. }
  201. return status;
  202. }
  203. }

Naming Convetions

You’ll notice there are four prefix constants defined in the class above.

The reason for this is to keep consistency between the sql query ids you will define in the mybatis mapper.xml files (see Step 4) and the method names defined in the MyBatisDAO class we’re implementing.

This won’t work exactly like ActiveRecord or similar frameworks where there is a pluralization engine but it will still simplify things a lot.

For example, if you have an object called Status for which you will create a DAO, you will have to define the following mybatis querries







































Java Method MyBatis Query Id Convention
dao.get(1) <select id=”getStatus” … getClassName
dao.getAll() <select id=”getAllStatus” … getAllClassName
dao.getByName(String name) <select id=”getStatusByName” … getClassNameByName
dao.create(obj) <insert id=”createStatus” … createClassName
dao.update(obj) <update id=”updateStatus” … updateClassName
dao.delete(id) <delete id=”deleteStatus” … deleteClassName

Don’t worry, this will make a lot more sense once you get to Step 4

Step 3 - Write the actual DAO classes

Now you need to implement your concrete DAO classes.

Let’s say you have a simple object called Status which maps to a simple table, and has 2 attributes, an ID and a NAME. Think of it as an object you could use to represent the status of a task.I chose this to illustrate a very basic example here

The table for this object would look like this




















ID NAME
1 Done
2 In Progress
3 Not Started

And the java class (or DTO object) would look like this (Status.java)

  1. public class Status
  2. {
  3. privateintid;
  4. privateString name;
  5. publicintgetId() {
  6. returnid;
  7. }
  8. publicvoidsetId(intid) {
  9. this.id = id;
  10. }
  11. publicString getName() {
  12. returnname;
  13. }
  14. publicvoidsetName(String name) {
  15. this.name = name;
  16. }
  17. @Override
  18. publicString toString() {
  19. return"[Status] "+ "("+ id +") " + name;
  20. }
  21. }

Writing the DAO class for the Status object now becomes trivial since you’re inheriting all the default CRUD methods from MyBatisDAO.java defined above.

Here’s how the StatusDAO.java should look:

  1. import org.apache.ibatis.session.SqlSessionFactory;
  2. import com.idleworx.mybatisdao.MyBatisDAO;
  3. import com.idleworx.mybatisdao.tests.objects.Status;
  4. public class StatusDAO extendsMyBatisDAO<Status,Integer>{
  5. //Don't forget to define the default custom constructor when implementing a new
  6. //child DAO class, and set the class type accordingly
  7. publicStatusDAO(Class<Status> type,SqlSessionFactory containerSessionFactory) {
  8. super(type,containerSessionFactory);
  9. }
  10. }

You’ll notice here that all you need to do is call the constructor from the parent MyBatisDAO.java class. Now all the default CRUD methods are available for the StatusDAO.java class.

You are free of course to add any additional methods as needed here that go beyond the CRUD methods that were defined. For example something like

getStatusForBlog(Integer blogId).

See the full code example.
Both the MyBatisDAO.java and IParentDAO.java classes are included.

Of course you will still have to define the MyBatis mapper SQL statements for the default CRUD methods defined in MyBatisDAO.java, as well as additional SQLs for other DAO methods you choose to implement. Which brings us to the next step …

Step 4 - Defining the mybatis mapper

The last major step in the puzzle, is writing the actual SQL that will implement all the default CRUD methods and any other methods you’ve implemented for the Status object.

Your code may differ of course, but note the use of the naming conventions for naming the sql statements which I talked about earlier.

That is the key to this whole thing, and it will make your life a lot easier when writing other DAOs which extend the MyBatisDAO class.

Here is an example of how the mapper file for the StatusDAO object would look like.

StatusMapper.xml

  1. <?xmlversion="1.0"encoding="UTF-8"?>
  2. <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
  3. <mappernamespace="mappers">
  4. <selectid="getStatus"parameterType="int"resultType="Status">
  5. select
  6. status_id as "id",
  7. status_name as "name"
  8. from daohelper.status where status_id = #{id}
  9. </select>
  10. <selectid="getAllStatus"parameterType="int"resultType="Status">
  11. select
  12. status_id as "id",
  13. status_name as "name"
  14. from daohelper.status order by status_id
  15. </select>
  16. <selectid="getStatusByName"parameterType="int"resultType="Status">
  17. select
  18. status_id as "id",
  19. status_name as "name"
  20. from daohelper.status where status_name = #{name}
  21. </select>
  22. <insertid="createStatus"keyColumn="status_id"useGeneratedKeys="true"parameterType="Status">
  23. insert into daohelper.status (status_name)
  24. values (#{name})
  25. </insert>
  26. <updateid="updateStatus"parameterType="Status">
  27. update daohelper.status set status_name = #{name} where status_id = #{id}
  28. </update>
  29. <deleteid="deleteStatus"parameterType="int">
  30. delete from daohelper.status where status_id = #{id}
  31. </delete>
  32. </mapper>

Note here the use of the mapper namespace which is the same as referenced by the MyBatisDAO abstract class.

It’s used for convenience in this case, and generally you should be able to use multiple mapper files with the same namespace as long as the you don’t have multiple queries with the same id across several xml mapper files (which would be a bad idea anyway)

Errors

Just a side note on some errors you may encounter.

If you call one of the default CRUD methods but don’t define the appropriate query in the mapper.xml file, you will get an error like this:

  1. org.apache.ibatis.exceptions.PersistenceException:
  2. ### Error querying database. Cause: java.lang.IllegalArgumentException: Mapped Statements collection does not contain value for mappers.getAllFrog
  3. ### Cause: java.lang.IllegalArgumentException: Mapped Statements collection does not contain value for mappers.getAllFrog

Wrapping it up

By this point I hope you’ve managed to build yourself a small but working DAO implementation based on the MyBatisDAO abstract class.

You can download the full code example for more a better overview.

It also contains a jUnit example, the mybatis configuration file (mybatis.config.xml) as well as the simple DDL statments for setting up the MySQL table.

Note:If you need to use this from a Servlet or Spring controller, see my other blog post aboutdefining a servletcontextlistener

Hope you found this useful. Improvements or suggestions are welcome.

发表评论

表情:
评论列表 (有 0 条评论,237人围观)

还没有评论,来说两句吧...

相关阅读