Пример реализации патерна DAO и VO в Java

В этой статье рассмотрим один из вариантов абстрагирования работы с базой даных, который позволит менять источник данных только изменив конфигурацию.

Способ доступа к данным зависит от источника, где они хранятся, а способ доступа к хранилищу данных сильно зависит от его типа, например доступ к реляционной базе данных и файловой имеет существенные отличия. Работа с реляционными базами данных может происходить через JDBC API. Несмотря на то, что JDBC API предоставляет стандартный механизм доступа и управления данными в реляционной базе данных, синтаксис и формат SQL запросов может быть разным для каждой СУБД. Еще большая проблема возникает когда нужно изменить тип источника данных, например из реляционной базы данных на базу данных в формате XML файлов, так что придется переписывать все модули, которые работают с источником данных, и делать это при каждой последующей смене. Чтобы избежать этих проблем стоит пользоваться образцом проектирования DAO.

Data Access Object

(DAO) – образец проектирования, который предоставляет абстрактный интерфейс к любому типу хранилища данных. То есть клиент не работает с хранилищем данных напрямую, это похоже на образец проектирования Facade, который применяется для ограничения доступа к подсистемам путем предоставлением единой точки входа. Еще одним преимуществом DAO является то, что он скрывает детали реализации доступа к источникам данных, и это позволяет изменять источник данных без влияния на бизнес логику.

dao_diagram

Архитектура патерна DAO

Бизнес-компонент — это объект, который требует доступ к источнику данных, чтобы получить и сохранять данные. Data Access Object является первичным объектом данного шаблона. Value Object абстрагирует реализацию доступа к данным для бизнес-компонента, обеспечивая прозрачный доступ к источнику данных. Источником данных может быть база данных, например, СУБД, XML документы, данные в формате JSON. Рассмотрим пример реализации DAO, который позволит изменением лишь одного параметра менять источник получения данных. Предположим что нам необходимо где-то хранить данные об адресах, при этом хранилища данных могут варьироваться.

Образец данных в формате XML
<?xml version="1.0" encoding="UTF-8" ?>

<address>
 	<id>0</id>
 	<city>Kiev</city>
<street>Khreschatyk</street>
	<district>Shevchenkivskyi</district>
</address>

Итак, необходимо спроектировать архитектуру так, чтобы она была менее чувствительна к изменениям. Прежде чем переходить к разработке самого DAO, стоит написать объект данных (VO). Его реализация является достаточно тривиальной: необходимо создать соответствующие поля и методы доступа и изменения их значений. Затем необходимо сформировать интерфейс, через который будут работать клиенты DAO. Заключительным этапом является написание конкретных реализаций для каждого типа хранилища данных.

Диаграма класов DAO

Для того чтобы добавить новый DAO в систему необходимо только написать класс, который реализует методы абстрактного параметризованого класс DAO<T>, и это не приведет к изменениям или необходимости перекомпиляции в уже существующих реализациях. То что класс DAO<T> параметризованный предоставляет возможность использовать общий интерфейс для всех типов объекта данных, то есть мы имеем возможность легко добавлять новую сущность в систему. Также достаточно удачно сюда вписывается фабрика DAOFactory, она делает инкапсулирует в себе процесс создания конкретных DAO.

СКАЧАТЬ: Исходный код
DAO.java
package dao;
import java.util.ArrayList;

public abstract class DAO<T> {
  public abstract void insert(T adr);
  public abstract T getById(int id);
  public abstract void update(T adr);
  public abstract void delete(T adr);
  public abstract ArrayList<T> getAll();
}

JDBCAddressDAO.java
package dao;

import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.logging.Level;
import java.util.logging.Logger;

import utils.ConnectionFactory;
import utils.ConnectionFactoryImpl;


public class JDBCAddressDAO extends DAO<Address> {
	@Override
    public Address getById(int id)
    {
    ConnectionFactory connFactory = new ConnectionFactoryImpl();
    final Connection con = connFactory.getConnection(); // create connection
    try (Statement stm=con.createStatement();ResultSet rs=stm.executeQuery("select * from address where id="+id);){
	while (rs.next()) {                            //if rs.next() returns false
	return new Address(id,rs.getString("city"),rs.getString("district"), rs.getString("street"));
	}
    }
	catch (SQLException e) 
	{
	e.printStackTrace();
	}
    try {
    con.close();
    } catch (SQLException ex) {
    Logger.getLogger(JDBCAddressDAO.class.getName()).log(Level.SEVERE, null, ex);
    }
    return null;    
    }
	@Override
    public void update(Address adr)
    {
    ConnectionFactory connFactory = new ConnectionFactoryImpl();
    final Connection con = connFactory.getConnection(); // create connection
    try {
	final Statement stm = con.createStatement();
	String query="UPDATE address SET city='"+adr.getCity()+"',district='"+adr.getDistrict()+"',street='"+adr.getStreet()+"'  WHERE id="+adr.getId()+" ;";
	System.out.println(query);
	stm.executeUpdate(query);
	con.close();
	}
	catch (SQLException y) {
	y.printStackTrace();	}	    	
    }
    @Override
    public ArrayList<Address> getAll() {
    ArrayList<Address> res=new ArrayList<>();
    
    ConnectionFactory connFactory = new ConnectionFactoryImpl();
    final Connection con = connFactory.getConnection(); // create connection
    try (Statement stm=con.createStatement();ResultSet rs=stm.executeQuery("select * from address  " );){
		while (rs.next()) {                            //if rs.next() returns false
			res.add(new Address(rs.getInt("id"),rs.getString("city"),rs.getString("district"), rs.getString("street")));}
	}
		catch (SQLException e) 
		{
		e.printStackTrace();
		}
        try {
            con.close();
        } catch (SQLException ex) {
            Logger.getLogger(JDBCAddressDAO.class.getName()).log(Level.SEVERE, null, ex);
        }
    return res;
    }
	@Override
	public void insert(Address adr) {
	ConnectionFactory connFactory = new ConnectionFactoryImpl();
   	final Connection con = connFactory.getConnection(); // create connection
   	    
   	try {
			final Statement stm = con.createStatement();
			String query="INSERT INTO address (city,district,street) VALUES ('"+adr.getCity()+"','"+adr.getDistrict()+"','"+adr.getStreet()+"') ";
			stm.executeUpdate(query);
			con.close();
		}
		catch (SQLException y) 
		{
		y.printStackTrace();
		}	    
	}
	@Override
	public void delete(Address adr) {
	ConnectionFactory connFactory = new ConnectionFactoryImpl();
    final Connection con = connFactory.getConnection(); // create connection
    try {
	final Statement stm = con.createStatement();
	String query="DELETE FROM address WHERE item.id="+adr.getId()+" ;";
	stm.executeUpdate(query);
	con.close();
	}
		catch (SQLException y){ 
			y.printStackTrace();
		}	    
	}
}
XMLAddressDAO.java
package dao;

import java.util.ArrayList;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import javax.xml.transform.*;
import javax.xml.transform.stream.*;
import javax.xml.transform.dom.*;




public class XMLAddressDAO extends DAO<Address>{

	private static int auto_increment=10;
	@Override
	public void insert(Address adr) {
		try{
		DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
        DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
        Document document = documentBuilder.parse("resources/address.xml");
        Element root = document.getDocumentElement();

        // Root Element
        Element rootElement = document.getDocumentElement();

        
            // server elements
            Element address = document.createElement("address");
            rootElement.appendChild(address);
            
            
            Element id = document.createElement("id");
            id.appendChild(document.createTextNode(Integer.toString(auto_increment++)));
            address.appendChild(id);

            Element city = document.createElement("city");
            city.appendChild(document.createTextNode(adr.getCity()));
            address.appendChild(city);
            
            Element street = document.createElement("street");
            street.appendChild(document.createTextNode(adr.getStreet()));
            address.appendChild(street);
            
            Element district = document.createElement("district");
            district.appendChild(document.createTextNode(adr.getDistrict()));
            address.appendChild(district);

            root.appendChild(address);
        

        DOMSource source = new DOMSource(document);

        TransformerFactory transformerFactory = TransformerFactory.newInstance();
        Transformer transformer = transformerFactory.newTransformer();
        StreamResult result = new StreamResult("resources/address.xml");
        transformer.transform(source, result);
		}
		catch(Exception e)
		{
		e.printStackTrace();	
		}
	}

	@Override
	public Address getById(int id) {
		   try{
			     DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
			     DocumentBuilder builder = factory.newDocumentBuilder();
			     Document doc = builder.parse("resources/address.xml");
			     doc.getDocumentElement().normalize();; // áåðåìî ïåðøèé åëåìåíò
			     NodeList nodeList = doc.getElementsByTagName("address");  			     
			     for (int temp = 0; temp < nodeList.getLength(); temp++) {  
			    	    Node node = nodeList.item(temp);  			    	  
			    	    if (node.getNodeType() == Node.ELEMENT_NODE) {  			    	  
			    	     Element student = (Element) node; 
			    	     if(Integer.parseInt(student.getElementsByTagName("id").item(0).getTextContent())==id){
			    	     Address tempAddress=new Address();			    	     
			    	     tempAddress.setId(id);  
			    	     tempAddress.setCity(student.getElementsByTagName("city").item(0).getTextContent());  
			    	     tempAddress.setDistrict(student.getElementsByTagName("district").item(0).getTextContent());  
			    	     tempAddress.setStreet(student.getElementsByTagName("street").item(0).getTextContent());  
			    	     return tempAddress;
			    	     }
			    	    }}     
			    }
			    catch(Exception e)
			    {
			     e.printStackTrace();
			    }
		return null;
	}

	@Override
	public void update(Address adr) {
		
		
	}

	@Override
	public void delete(Address adr) {
	
		
	}

	@Override
	public ArrayList<Address> getAll() {
		ArrayList<Address> res=new ArrayList<>();
		   try{
			     DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
			     DocumentBuilder builder = factory.newDocumentBuilder();
			     Document doc = builder.parse("resources/address.xml");
			     doc.getDocumentElement().normalize();; // áåðåìî ïåðøèé åëåìåíò
			     NodeList nodeList = doc.getElementsByTagName("address");  			     
			     for (int temp = 0; temp < nodeList.getLength(); temp++) {  
			    	    Node node = nodeList.item(temp);  			    	  
			    	    if (node.getNodeType() == Node.ELEMENT_NODE) {  			    	  
			    	     Element student = (Element) node;  			    	  
			    	     Address tempAddress=new Address();			    	     
			    	     tempAddress.setId(Integer.parseInt(student.getElementsByTagName("id").item(0).getTextContent()));  
			    	     tempAddress.setCity(student.getElementsByTagName("city").item(0).getTextContent());  
			    	     tempAddress.setDistrict(student.getElementsByTagName("district").item(0).getTextContent());  
			    	     tempAddress.setStreet(student.getElementsByTagName("street").item(0).getTextContent());  
			    	     res.add(tempAddress);		    	  
			    	    }}     
			    }
			    catch(Exception e)
			    {
			     e.printStackTrace();
			    }
		return res;
	}
}