مثال JDBC Spring

تقنية Spring JDBC هي موضوع هذا البرنامج التعليمي. القواعد البيانات هي جزء أساسي من معظم تطبيقات الشركات. لذلك عندما يتعلق الأمر بإطار Java EE ، فإن وجود تكامل جيد مع JDBC مهم جدًا.

تقنية Spring JDBC

يقدم إطار الربيع تكاملًا ممتازًا مع واجهة برمجة التطبيقات JDBC ويوفر فئة الأداة JdbcTemplate التي يمكننا استخدامها لتجنب التعامل مع الشيفرة المكررة من منطق عمليات قاعدة البيانات مثل فتح/إغلاق الاتصال، ResultSet، PreparedStatement، وما إلى ذلك. دعونا نلقي نظرة أولاً على تطبيق مثال بسيط باستخدام Spring JDBC، ثم سنرى كيف يمكن لفئة JdbcTemplate مساعدتنا في كتابة شيفرة قابلة للتوسيع بسهولة، دون القلق حول إغلاق الموارد بشكل صحيح أم لا. يعد استخدام بيئة تطوير Spring Tool Suite لتطوير تطبيقات الربيع مفيدًا للغاية، لذا سنستخدم STS لإنشاء تطبيق Spring JDBC الخاص بنا. ستظهر هيكل المشروع النهائي كما في الصورة أدناه. قم بإنشاء مشروع Spring Maven بسيط من قائمة STS، يمكنك اختيار أي اسم تريده أو الالتزام باسم مشروعي ك SpringJDBCExample.

تبعيات Spring JDBC

أولاً وقبل كل شيء، نحتاج إلى تضمين Spring JDBC وبرامج تشغيل قواعد البيانات في ملف pom.xml لمشروع Maven الخاص بي. يبدو ملف pom.xml النهائي لي كما يلي.

<project xmlns="https://maven.apache.org/POM/4.0.0" xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="https://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>org.springframework.samples</groupId>
	<artifactId>SpringJDBCExample</artifactId>
	<version>0.0.1-SNAPSHOT</version>

	<properties>

		<!-- Generic properties -->
		<java.version>1.6</java.version>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
		<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>

		<!-- Spring -->
		<spring-framework.version>4.0.2.RELEASE</spring-framework.version>

		<!-- Logging -->
		<logback.version>1.0.13</logback.version>
		<slf4j.version>1.7.5</slf4j.version>

	</properties>

	<dependencies>
		<!-- Spring and Transactions -->
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-context</artifactId>
			<version>${spring-framework.version}</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-tx</artifactId>
			<version>${spring-framework.version}</version>
		</dependency>

		<!-- Spring JDBC Support -->
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-jdbc</artifactId>
			<version>${spring-framework.version}</version>
		</dependency>
		
		<!-- MySQL Driver -->
		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
			<version>5.0.5</version>
		</dependency>

		<!-- Logging with SLF4J & LogBack -->
		<dependency>
			<groupId>org.slf4j</groupId>
			<artifactId>slf4j-api</artifactId>
			<version>${slf4j.version}</version>
			<scope>compile</scope>
		</dependency>
		<dependency>
			<groupId>ch.qos.logback</groupId>
			<artifactId>logback-classic</artifactId>
			<version>${logback.version}</version>
			<scope>runtime</scope>
		</dependency>

	</dependencies>
</project>

الجزء الأكبر منه يتم إنشاؤه تلقائيًا بواسطة STS، ومع ذلك قمت بتحديث إصدار Spring Framework لاستخدام الإصدار الأحدث 4.0.2.RELEASE. كما قمنا بإضافة الفنون الضرورية spring-jdbc و mysql-connector-java. الأول يحتوي على فئات دعم Spring JDBC والثاني هو مشغل قاعدة البيانات. أنا استخدم قاعدة بيانات MySQL لأغراض الاختبارات الخاصة بنا، لذا قمت بإضافة تبعيات ملفات JConnector لـ MySQL. إذا كنت تستخدم نظام قواعد بيانات علاقية مختلفًا ، فيجب عليك إجراء التغييرات المقابلة في التبعيات.

مثال على Spring JDBC – إعداد قاعدة البيانات

لنقم بإنشاء جدول بسيط سنستخدمه في تطبيقنا كمثال على عمليات CRUD.

CREATE TABLE `Employee` (
  `id` int(11) unsigned NOT NULL,
  `name` varchar(20) DEFAULT NULL,
  `role` varchar(20) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

مثال على Spring JDBC – فئة النموذج

سنستخدم نمط DAO Pattern لعمليات JDBC، لذا دعونا نقوم بإنشاء شجرة جافا ستمثل جدول الموظفين لدينا.

package com.journaldev.spring.jdbc.model;

public class Employee {

	private int id;
	private String name;
	private String role;
	
	public int getId() {
		return id;
	}
	public void setId(int id) {
		this.id = id;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public String getRole() {
		return role;
	}
	public void setRole(String role) {
		this.role = role;
	}
	
	@Override
	public String toString(){
		return "{ID="+id+",Name="+name+",Role="+role+"}";
	}
}

مثال على Spring JDBC – واجهة DAO وتنفيذها

لنمتلك نمط DAO، سنقوم أولاً بإنشاء واجهة تعلن عن جميع العمليات التي نرغب في تنفيذها.

package com.journaldev.spring.jdbc.dao;

import java.util.List;

import com.journaldev.spring.jdbc.model.Employee;

//عمليات CRUD
public interface EmployeeDAO {
	
	//Create
	public void save(Employee employee);
	//Read
	public Employee getById(int id);
	//Update
	public void update(Employee employee);
	//Delete
	public void deleteById(int id);
	//الحصول على الكل
	public List getAll();
}
package com.journaldev.spring.jdbc.dao;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

import javax.sql.DataSource;

import com.journaldev.spring.jdbc.model.Employee;

public class EmployeeDAOImpl implements EmployeeDAO {

	private DataSource dataSource;

	public void setDataSource(DataSource dataSource) {
		this.dataSource = dataSource;
	}

	@Override
	public void save(Employee employee) {
		String query = "insert into Employee (id, name, role) values (?,?,?)";
		Connection con = null;
		PreparedStatement ps = null;
		try{
			con = dataSource.getConnection();
			ps = con.prepareStatement(query);
			ps.setInt(1, employee.getId());
			ps.setString(2, employee.getName());
			ps.setString(3, employee.getRole());
			int out = ps.executeUpdate();
			if(out !=0){
				System.out.println("Employee saved with id="+employee.getId());
			}else System.out.println("Employee save failed with id="+employee.getId());
		}catch(SQLException e){
			e.printStackTrace();
		}finally{
			try {
				ps.close();
				con.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}
		}
	}

	@Override
	public Employee getById(int id) {
		String query = "select name, role from Employee where id = ?";
		Employee emp = null;
		Connection con = null;
		PreparedStatement ps = null;
		ResultSet rs = null;
		try{
			con = dataSource.getConnection();
			ps = con.prepareStatement(query);
			ps.setInt(1, id);
			rs = ps.executeQuery();
			if(rs.next()){
				emp = new Employee();
				emp.setId(id);
				emp.setName(rs.getString("name"));
				emp.setRole(rs.getString("role"));
				System.out.println("Employee Found::"+emp);
			}else{
				System.out.println("No Employee found with id="+id);
			}
		}catch(SQLException e){
			e.printStackTrace();
		}finally{
			try {
				rs.close();
				ps.close();
				con.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}
		}
		return emp;
	}

	@Override
	public void update(Employee employee) {
		String query = "update Employee set name=?, role=? where id=?";
		Connection con = null;
		PreparedStatement ps = null;
		try{
			con = dataSource.getConnection();
			ps = con.prepareStatement(query);
			ps.setString(1, employee.getName());
			ps.setString(2, employee.getRole());
			ps.setInt(3, employee.getId());
			int out = ps.executeUpdate();
			if(out !=0){
				System.out.println("Employee updated with id="+employee.getId());
			}else System.out.println("No Employee found with id="+employee.getId());
		}catch(SQLException e){
			e.printStackTrace();
		}finally{
			try {
				ps.close();
				con.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}
		}
	}

	@Override
	public void deleteById(int id) {
		String query = "delete from Employee where id=?";
		Connection con = null;
		PreparedStatement ps = null;
		try{
			con = dataSource.getConnection();
			ps = con.prepareStatement(query);
			ps.setInt(1, id);
			int out = ps.executeUpdate();
			if(out !=0){
				System.out.println("Employee deleted with id="+id);
			}else System.out.println("No Employee found with id="+id);
		}catch(SQLException e){
			e.printStackTrace();
		}finally{
			try {
				ps.close();
				con.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}
		}
	}

	@Override
	public List<Employee> getAll() {
		String query = "select id, name, role from Employee";
		List<Employee> empList = new ArrayList<Employee>();
		Connection con = null;
		PreparedStatement ps = null;
		ResultSet rs = null;
		try{
			con = dataSource.getConnection();
			ps = con.prepareStatement(query);
			rs = ps.executeQuery();
			while(rs.next()){
				Employee emp = new Employee();
				emp.setId(rs.getInt("id"));
				emp.setName(rs.getString("name"));
				emp.setRole(rs.getString("role"));
				empList.add(emp);
			}
		}catch(SQLException e){
			e.printStackTrace();
		}finally{
			try {
				rs.close();
				ps.close();
				con.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}
		}
		return empList;
	}

}

تتيح تنفيذ عمليات CRUD فهمًا بسيطًا. إذا كنت ترغب في معرفة المزيد حول مصدر البيانات، يرجى قراءة مثال على مصدر البيانات JDBC.

مثال على Spring JDBC – تكوين الفاصل

إذا نظرت إلى جميع الفئات أعلاه، فإنها جميعها تستخدم واجهة برمجة التطبيقات JDBC القياسية ولا يوجد إشارة إلى إطار Spring JDBC. تظهر فئات إطار Spring JDBC عند إنشاء ملف تكوين Spring Bean وتعريف الفاصل. سنقوم بإنشاء مصدر البيانات في ملف سياق Spring Bean وتعيينه في فئة تنفيذ DAO الخاصة بنا. يبدو ملف تكوين Spring Bean لدي مثلما فيما يلي.

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="https://www.springframework.org/schema/beans"
	xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="https://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd">

	<bean id="employeeDAO" class="com.journaldev.spring.jdbc.dao.EmployeeDAOImpl">
		<property name="dataSource" ref="dataSource" />
	</bean>
	
	<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">

		<property name="driverClassName" value="com.mysql.jdbc.Driver" />
		<property name="url" value="jdbc:mysql://localhost:3306/TestDB" />
		<property name="username" value="pankaj" />
		<property name="password" value="pankaj123" />
	</bean>

</beans>

أولاً، نقوم بإنشاء كائن مصدر البيانات من فئة DriverManagerDataSource. توفر هذه الفئة التنفيذ الأساسي لمصدر البيانات الذي يمكننا استخدامه. نمرر عنوان URL لقاعدة بيانات MySQL واسم المستخدم وكلمة المرور كخصائص إلى كائن المصدر. ثم يتم ضبط كائن المصدر إلى كائن EmployeeDAOImpl ونكون جاهزين مع تنفيذ Spring JDBC. التنفيذ مرتبط بشكل فضفاض وإذا أردنا التبديل إلى تنفيذ آخر أو الانتقال إلى خادم قاعدة بيانات آخر، كل ما نحتاجه هو إجراء التغييرات المناسبة في تكوينات البن. هذه هي واحدة من المزايا الرئيسية التي يوفرها إطار Spring JDBC.

فئة اختبار Spring JDBC

لنكتب فئة اختبار بسيطة للتأكد من أن كل شيء يعمل بشكل صحيح.

package com.journaldev.spring.jdbc.main;

import java.util.List;
import java.util.Random;

import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.journaldev.spring.jdbc.dao.EmployeeDAO;
import com.journaldev.spring.jdbc.model.Employee;

public class SpringMain {

	public static void main(String[] args) {
		// احصل على سياق Spring
		ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("spring.xml");
		
		// احصل على كائن EmployeeDAO
		EmployeeDAO employeeDAO = ctx.getBean("employeeDAO", EmployeeDAO.class);
		
		// قم بتشغيل بعض الاختبارات لعمليات JDBC CRUD
		Employee emp = new Employee();
		int rand = new Random().nextInt(1000);
		emp.setId(rand);
		emp.setName("Pankaj");
		emp.setRole("Java Developer");
		
		//Create
		employeeDAO.save(emp);
		
		//Read
		Employee emp1 = employeeDAO.getById(rand);
		System.out.println("Employee Retrieved::"+emp1);
		
		//Update
		emp.setRole("CEO");
		employeeDAO.update(emp);
		
		// احصل على الكل
		List empList = employeeDAO.getAll();
		System.out.println(empList);
		
		//Delete
		employeeDAO.deleteById(rand);
		
		// أغلق سياق Spring
		ctx.close();
		
		System.out.println("DONE");
	}

}

I am using Random Class to generate random number for employee id. When we run above program, we get following output.

Mar 25, 2014 12:54:18 PM org.springframework.context.support.ClassPathXmlApplicationContext prepareRefresh
INFO: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@4b9af9a9: startup date [Tue Mar 25 12:54:18 PDT 2014]; root of context hierarchy
Mar 25, 2014 12:54:18 PM org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
INFO: Loading XML bean definitions from class path resource [spring.xml]
Mar 25, 2014 12:54:19 PM org.springframework.jdbc.datasource.DriverManagerDataSource setDriverClassName
INFO: Loaded JDBC driver: com.mysql.jdbc.Driver
Employee saved with id=726
Employee Found::{ID=726,Name=Pankaj,Role=Java Developer}
Employee Retrieved::{ID=726,Name=Pankaj,Role=Java Developer}
Employee updated with id=726
[{ID=726,Name=Pankaj,Role=CEO}]
Employee deleted with id=726
Mar 25, 2014 12:54:19 PM org.springframework.context.support.ClassPathXmlApplicationContext doClose
INFO: Closing org.springframework.context.support.ClassPathXmlApplicationContext@4b9af9a9: startup date [Tue Mar 25 12:54:18 PDT 2014]; root of context hierarchy
DONE

مثال Spring JdbcTemplate

إذا نظرت إلى فئة تنفيذ DAO، ستجد الكثير من الشفرة القاعدية حيث نقوم بفتح وإغلاق الاتصالات والاستعلامات المُعدة ونتائج البيانات. يمكن أن يؤدي هذا إلى تسرب الموارد إذا نسي شخص ما إغلاق الموارد بشكل صحيح. يمكننا استخدام فئة org.springframework.jdbc.core.JdbcTemplate لتجنب هذه الأخطاء. تعتبر فئة Spring JdbcTemplate الفئة المركزية في حزمة Spring JDBC الأساسية وتوفر العديد من الطرق لتنفيذ الاستعلامات وتحليل نتائج البيانات تلقائيًا للحصول على الكائن أو قائمة من الكائنات. كل ما نحتاجه هو توفير الوسيطات كمصفوفة كائنات وتنفيذ واجهات رد الاتصالات مثل PreparedStatementSetter و RowMapper لتعيين الوسيطات أو تحويل بيانات ResultSet إلى كائنات فول. دعنا نلقي نظرة على تنفيذ آخر لـ EmployeeDAO حيث سنستخدم فئة Spring JdbcTemplate لتنفيذ أنواع مختلفة من الاستعلامات.

package com.journaldev.spring.jdbc.dao;

import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import javax.sql.DataSource;

import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;

import com.journaldev.spring.jdbc.model.Employee;

public class EmployeeDAOJDBCTemplateImpl implements EmployeeDAO {

	private DataSource dataSource;

	public void setDataSource(DataSource dataSource) {
		this.dataSource = dataSource;
	}
	
	@Override
	public void save(Employee employee) {
		String query = "insert into Employee (id, name, role) values (?,?,?)";
		
		JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
		
		Object[] args = new Object[] {employee.getId(), employee.getName(), employee.getRole()};
		
		int out = jdbcTemplate.update(query, args);
		
		if(out !=0){
			System.out.println("Employee saved with id="+employee.getId());
		}else System.out.println("Employee save failed with id="+employee.getId());
	}

	@Override
	public Employee getById(int id) {
		String query = "select id, name, role from Employee where id = ?";
		JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
		
		// باستخدام فئة RowMapper المجهولة، يمكننا إنشاء RowMapper منفصل لإعادة الاستخدام
		Employee emp = jdbcTemplate.queryForObject(query, new Object[]{id}, new RowMapper(){

			@Override
			public Employee mapRow(ResultSet rs, int rowNum)
					throws SQLException {
				Employee emp = new Employee();
				emp.setId(rs.getInt("id"));
				emp.setName(rs.getString("name"));
				emp.setRole(rs.getString("role"));
				return emp;
			}});
		
		return emp;
	}

	@Override
	public void update(Employee employee) {
		String query = "update Employee set name=?, role=? where id=?";
		JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
		Object[] args = new Object[] {employee.getName(), employee.getRole(), employee.getId()};
		
		int out = jdbcTemplate.update(query, args);
		if(out !=0){
			System.out.println("Employee updated with id="+employee.getId());
		}else System.out.println("No Employee found with id="+employee.getId());
	}

	@Override
	public void deleteById(int id) {

		String query = "delete from Employee where id=?";
		JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
		
		int out = jdbcTemplate.update(query, id);
		if(out !=0){
			System.out.println("Employee deleted with id="+id);
		}else System.out.println("No Employee found with id="+id);
	}

	@Override
	public List getAll() {
		String query = "select id, name, role from Employee";
		JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
		List empList = new ArrayList();

		List> empRows = jdbcTemplate.queryForList(query);
		
		for(Map empRow : empRows){
			Employee emp = new Employee();
			emp.setId(Integer.parseInt(String.valueOf(empRow.get("id"))));
			emp.setName(String.valueOf(empRow.get("name")));
			emp.setRole(String.valueOf(empRow.get("role")));
			empList.add(emp);
		}
		return empList;
	}

}

النقاط المهمة للاطلاع عليها في الشفرة أعلاه لـ Spring JdbcTemplate هي:

  • استخدام مصفوفة الكائنات لتمرير وسيطات PreparedStatement، يمكننا أيضًا استخدام تنفيذ PreparedStatementSetter ولكن تمرير مصفوفة الكائنات يبدو أسهل للاستخدام.
  • لا يوجد شفرة متعلقة بفتح وإغلاق الاتصالات أو البيانات أو نتائج البيانات. كل ذلك يتم التعامل معه داخليًا بواسطة فئة Spring JdbcTemplate.
  • تنفيذ فئة RowMapper المجهولة لتعيين بيانات ResultSet إلى كائن فول Employee في الطريقة queryForObject().
  • تطبيق الطريقة queryForList() يُرجع قائمة Map حيث تحتوي الخريطة على بيانات الصف المطابقة مع مفتاح اسم العمود والقيمة من صف قاعدة البيانات.

لاستخدام تنفيذ Spring JdbcTemplate ، كل ما نحتاج إليه هو تغيير فئة employeeDAO في ملف تكوين Spring Bean كما هو مبين أدناه.

<bean id="employeeDAO" class="com.journaldev.spring.jdbc.dao.EmployeeDAOJDBCTemplateImpl">
	<property name="dataSource" ref="dataSource" />
</bean>

عند تشغيل الفئة الرئيسية، سيكون إخراج تنفيذ Spring JdbcTemplate مشابهًا لتنفيذ JDBC العادي الذي تم رؤيته أعلاه. هذا كل شيء بالنسبة لبرنامج تعليمي حول Spring JDBC، قم بتنزيل المشروع العيني من الرابط أدناه وقم بتجربته لتعلم المزيد.

تنزيل مشروع Spring JDBC

Source:
https://www.digitalocean.com/community/tutorials/spring-jdbc-example