جاوا و تکنولوژی های آن

java programming language

در این وبلاگ به بررسی نکات موجود در جاوا و تکنولوژی های آن می پردازیم

طبقه بندی موضوعی


در این بخش به بررسی ماژول های Spring JDBC و استفاده از آن میپردازیم


مجموعه کلاس ها در JDBC به 4 بخش کلی تقسیم و پکیج بندی میشوند :


- core : کلاس های در برگیرنده عملیات اصلی مانند JdbcTemplate, SimpleJdbcInsert, SimpleJdbcCall and NamedParameterJdbcTemplate در این پکیج قرار میگیرند


- datasource : کلاس های ابزارهای کاربردی برای دسترسی به datasource ها در این مجموعه پکیج قرار میگیرند


- object : کلاس های این پکیج برای دسترسی به DB به روش Object-Oriented این اجازه را به ما میدهد که کوئری ها و result ها را در قالب شی گرایی اجرا کنیم و نتایج کوئری ها را بین ستون های جداول و فیلد های ابجکت ها متناظر (Map) میکند


- support : کلاس های دیگری که برای کارکرد بقیه کلاس ها مورد استفاده هستند در این پکیج قرار دارند مانند کلاس های ارائه کننده خطای SQLException که در هنگام عملیات احتمالا رخ خواهد داد



تنظیمات اولیه :


در کلاس تنظیمات ما یک Bean از نوع DataSource داریم که در آن مشخصات dataresource ای که میخواهیم به آن متصل شویم را ست میکنیم و نقش driver را بر عهده دارد  :

در اینجا ما از دیتابیس mysql استفاده کرده ایم 


@Configuration
@ComponentScan("com.baeldung.jdbc")
public class SpringJdbcConfig {
    @Bean
    public DataSource mysqlDataSource() {
        DriverManagerDataSource dataSource = new DriverManagerDataSource();
        dataSource.setDriverClassName("com.mysql.jdbc.Driver");
        dataSource.setUrl("jdbc:mysql://localhost:3306/springjdbc");
        dataSource.setUsername("guest_user");
        dataSource.setPassword("guest_password");
 
        return dataSource;
    }
}



روش ست کردن تنظیمات در XML :


<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
  destroy-method="close">
    <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
    <property name="url" value="jdbc:mysql://localhost:3306/springjdbc"/>
    <property name="username" value="guest_user"/>
    <property name="password" value="guest_password"/>
</bean>


و اگر میخواستیم از Embedded Database مانند H2 استفاده کنیم :


@Bean
public DataSource dataSource() {
    return new EmbeddedDatabaseBuilder()
      .setType(EmbeddedDatabaseType.H2)
      .addScript("classpath:jdbc/schema.sql")
      .addScript("classpath:jdbc/test-data.sql").build();
}



API هایی که میتوانیم توسط آنها با JDBC کار کنیم :


- JdbcTemplate

- NamedParameterJdbcTemplate

- SimpleJdbcTemplate

- SimpleJdbcInsert

- SimpleJdbcCall


JdbcTemplate و اجرا کردن کوئری ها :


JdbcTemplate اصلی ترین API برای دسترسی به عملیات :


- ایجاد و بستن connection بصورت خودکار

- مدیریت کردن Exception ها و دادن اطلاعات مفید هنگامی که خطایی رخ دهد 

- کلیه عملیات CRUD دیتابیس را میتوانیم با این API انجام دهیم 

- اجرای statement ها و store procedure ها 

- دریافت result و پیمایش اطلاعات روی ResultSet 


این API متد های زیر را دارد که هر کدام کاربرد خاص خود را دارد :


(public int update(String query

برای insert update delete استفاده میشود


(public int update(String query,Object... args

برای insert update delete همراه با PreparedStatement و آرگومان ورودی استفاده میشود


(public void execute(String query

برای اجرای کوئری های DDL استفاده میشود


(public T execute(String sql, PreparedStatementCallback action

اجرای کوئری همراه با PreparedStatement  و مقدار برگشتی از نوع ابجکت


(public T query(String sql, ResultSetExtractor rse

برای دریافت رکورد ها با استفاده از ResultSetExtractor 


(public List query(String sql, RowMapper rse

برای دریافت لیست ابجکتی از رکورد ها که توسط RowMapper تناظر آنها تعریف شده است و بصورت ابجکت در آمده است



برای راحتی کار یک کلاس DAO طراحی میکنیم و کوئری هایی که میخواهیم را در آن پیاده سازی میکنیم و بعد از متد هایش استفاده میکنیم :


import org.springframework.jdbc.core.JdbcTemplate;  
  
public class EmployeeDao {  
private JdbcTemplate jdbcTemplate;  
  
public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {  
    this.jdbcTemplate = jdbcTemplate;  
}  
  
public int saveEmployee(Employee e){  
    String query="insert into employee values(  
    '"+e.getId()+"','"+e.getName()+"','"+e.getSalary()+"')";  
    return jdbcTemplate.update(query);  
}  
public int updateEmployee(Employee e){  
    String query="update employee set   
    name='"+e.getName()+"',salary='"+e.getSalary()+"' where id='"+e.getId()+"' ";  
    return jdbcTemplate.update(query);  
}  
public int deleteEmployee(Employee e){  
    String query="delete from employee where id='"+e.getId()+"' ";  
    return jdbcTemplate.update(query);  
}  
  
}  




یک کوئری select ساده :

int result = jdbcTemplate.queryForObject(
    "SELECT COUNT(*) FROM EMPLOYEE", Integer.class);


یک کوئری insert ساده :


public int addEmplyee(int id) {
    return jdbcTemplate.update(
      "INSERT INTO EMPLOYEE VALUES (?, ?, ?, ?)", id, "Bill", "Gates", "USA");
}



کوئری زدن با پارامتری های دارای نام :


برای ساپورت از named parameters یک JDBC Template دیگری بنام NamedParameterJdbcTemplate وجود دارد که خود نوعی JdbcTemplate است و با استفاده از آن میتوان بجای استفاده از علامت سوال در کوئری ? از نام پارامتر ها استفاده کنیم و هنگام پاس دادن مقادیر بجای استفاده از ایندکس عددیی انها از نام انها استفاده کنیم مقادیر ار میتوانیم توسط یک Map بگیریم و بعد پاس بدیم  :


public class EmpDao {  
NamedParameterJdbcTemplate template;  
  
public EmpDao(NamedParameterJdbcTemplate template) {  
        this.template = template;  
}  
public  void save (Emp e){  
String query="insert into employee values (:id,:name,:salary)";  
  
Map<String,Object> map=new HashMap<String,Object>();  
map.put("id",e.getId());  
map.put("name",e.getName());  
map.put("salary",e.getSalary());  
  
template.execute(query,map,new PreparedStatementCallback() {  
    @Override  
    public Object doInPreparedStatement(PreparedStatement ps)  
            throws SQLException, DataAccessException {  
        return ps.executeUpdate();  
    }  
});  
}  
}  


یا توسط ابجکت MapSqlParameterSource مقادیر را ست کنیم و بعد پاس بدیم به متد کوئری :


SqlParameterSource namedParameters = new MapSqlParameterSource().addValue("id", 1);
return namedParameterJdbcTemplate.queryForObject(
  "SELECT FIRST_NAME FROM EMPLOYEE WHERE ID = :id", namedParameters, String.class);


کلاس MapSqlParameterSource  مسئول قرار دادن مقادیر برای پارامتر ها است و در نهایت یکی از پارامترهای ورودی کوئری خواهد بود



کوئری زدن با استفاده از property های یک Entity :


گاهی مقادیر ما مربوط به property های یک کلاس Entity است و برای دریافت پارامتر های آن نیاز به کلاس دیگری برای map کردن داریم که کلاس BeanPropertySqlParameterSource  مسئول ان است :


Employee employee = new Employee();
employee.setFirstName("James");
 
String SELECT_BY_ID = "SELECT COUNT(*) FROM EMPLOYEE WHERE FIRST_NAME = :firstName";
 
SqlParameterSource namedParameters = new BeanPropertySqlParameterSource(employee);
return namedParameterJdbcTemplate.queryForObject(
  SELECT_BY_ID, namedParameters, Integer.class);




برقراری تناظر (mapping) بین Result و Object :


با استفاده از پیاده سازی از روی اینترفیس RowMap میتوانیم تناظر بین Result و Object را تعریف کنیم :


public class EmployeeRowMapper implements RowMapper<Employee> {
    @Override
    public Employee mapRow(ResultSet rs, int rowNum) throws SQLException {
        Employee employee = new Employee();
 
        employee.setId(rs.getInt("ID"));
        employee.setFirstName(rs.getString("FIRST_NAME"));
        employee.setLastName(rs.getString("LAST_NAME"));
        employee.setAddress(rs.getString("ADDRESS"));
 
        return employee;
    }
}


و با پاس کردن RowMap ساخته شده به Query API میتوانیم نتایج را در قالب Object داشته باشیم :


String query = "SELECT * FROM EMPLOYEE WHERE ID = ?";
List<Employee> employees = jdbcTemplate.queryForObject(
  query, new Object[] { id }, new EmployeeRowMapper());




انتقال Exception های JDBC :


Spring برای ارتباط با JDBC یک لایه wrapper ایجاد کرده است و در این حالت باید بتواند Exception های ایجاد شده را بدرستی انتقال دهد از این رو یک سلسله وراثت که پدر تمام آنها کلاس DataAccessException است قابل دسترس و استفاده خواهد بود و حتی Raw Exception ها نیز قابل انتقال و دریافت هستند 

و ما میتوانیم با استفاده از کلاس پدر DataAccessException  و فرزندان آن فرآیند رسیدگی به خطاهای تولید شده را انجام دهیم


همینطور کلاس SQLErrorCodeSQLExceptionTranslator که یک پیاده سازی از SQLExceptionTranslator است جهت پیاده سازی رسیدگی به خطای خاص نیز قابل استفاده است 

مثلا در مثال زیر خطای کد 23505 که مربوط به کلید تکراری در H2 است را پیاده سازی کرده ایم :


public class CustomSQLErrorCodeTranslator extends SQLErrorCodeSQLExceptionTranslator {
    @Override
    protected DataAccessException
      customTranslate(String task, String sql, SQLException sqlException) {
        if (sqlException.getErrorCode() == 23505) {
          return new DuplicateKeyException(
            "Custom Exception translator - Integrity constraint violation.", sqlException);
        }
        return null;
    }
}


و جهت استفاده کافی است در jdbc template آنرا ست کنیم :


CustomSQLErrorCodeTranslator customSQLErrorCodeTranslator = 
  new CustomSQLErrorCodeTranslator();
jdbcTemplate.setExceptionTranslator(customSQLErrorCodeTranslator);





اجرای عملیات JDBC با کلاسهای SimpleJdbc : 



SimpleJdbcInsert :


این کلاس جهت عملیات insert ایجاد شده است و بسادگی کافی است تنها نام جدول و نام ستون و مقادیر را تعیین کنیم 


SimpleJdbcInsert simpleJdbcInsert = 
  new SimpleJdbcInsert(dataSource).withTableName("EMPLOYEE");
public int addEmplyee(Employee emp) {
    Map<String, Object> parameters = new HashMap<String, Object>();
    parameters.put("ID", emp.getId());
    parameters.put("FIRST_NAME", emp.getFirstName());
    parameters.put("LAST_NAME", emp.getLastName());
    parameters.put("ADDRESS", emp.getAddress());
 
    return simpleJdbcInsert.execute(parameters);
}


و اگر P.key از نوع Auto Generation بود و میخواستیم بعد از ایجاد رکورد P.key را داشته باشیم به اینصورت کانفیگ و عمل insert را اجرا میکردیم :


SimpleJdbcInsert simpleJdbcInsert = new SimpleJdbcInsert(dataSource)
                                        .withTableName("EMPLOYEE")
                                        .usingGeneratedKeyColumns("ID");
 
Number id = simpleJdbcInsert.executeAndReturnKey(parameters);
System.out.println("Generated id - " + id.longValue());


همچنین همانند قبل میتوانیم پارامتر ها را بوسیله BeanPropertySqlParameterSource هم ارسال کنیم 





اجرای Stored Procedure با SimpleJdbcCall :


SimpleJdbcCall simpleJdbcCall = new SimpleJdbcCall(dataSource)
                             .withProcedureName("READ_EMPLOYEE");
public Employee getEmployeeUsingSimpleJdbcCall(int id) {
    SqlParameterSource in = new MapSqlParameterSource().addValue("in_id", id);
    Map<String, Object> out = simpleJdbcCall.execute(in);
 
    Employee emp = new Employee();
    emp.setFirstName((String) out.get("FIRST_NAME"));
    emp.setLastName((String) out.get("LAST_NAME"));
 
    return emp;
}




عملیات Batch :


برای اجرای عملیات گروهی میتوانیم از دو کلاس استفاده کنیم :


کلاس JdbcTemplate و متد ()batchUpdate برای اجرا عملیات ساده :


public int[] batchUpdateUsingJdbcTemplate(final List<Employee> employees) {
    return jdbcTemplate.batchUpdate("INSERT INTO EMPLOYEE VALUES (?, ?, ?, ?)",
        new BatchPreparedStatementSetter() {
            @Override
            public void setValues(PreparedStatement ps, int i) throws SQLException {
                ps.setInt(1, employees.get(i).getId());
                ps.setString(2, employees.get(i).getFirstName());
                ps.setString(3, employees.get(i).getLastName());
                ps.setString(4, employees.get(i).getAddress();
            }
            @Override
            public int getBatchSize() {
                return employees.size();
            }
        });
}




عملیات گروهی با NamedParameterJdbcTemplate و متد ()batchUpdate :


که از روش قبلی ساده تر است و نیازی به پیاده سازی اینترفیس برای جای گزاری پارامترها ندارد و پارامتر ها توسط یک Prepared Statement داخلی ست میشوند 

SqlParameterSource[] batch = SqlParameterSourceUtils.createBatch(employees.toArray());
int[] updateCounts = namedParameterJdbcTemplate.batchUpdate(
    "INSERT INTO EMPLOYEE VALUES (:id, :firstName, :lastName, :address)", batch);
return updateCounts;



استفاده از Spring JDBC با Spring Boot :


Spring Boot به ما کمک میکند که تنظیمات اولیه راحت تری داشته باشیم و Application را سریعتر دولوپ کنیم 


ابتدا وابستگی هایش را به پروژه اضافه میکنیم :


<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <scope>runtime</scope>
</dependency>



ست کردن راحت تنظیمات اتصال به دیتابیس :


spring.datasource.url=jdbc:mysql://localhost:3306/springjdbc
spring.datasource.username=guest_user
spring.datasource.password=guest_password











نظرات  (۰)

هیچ نظری هنوز ثبت نشده است

ارسال نظر

ارسال نظر آزاد است، اما اگر قبلا در بیان ثبت نام کرده اید می توانید ابتدا وارد شوید.
شما میتوانید از این تگهای html استفاده کنید:
<b> یا <strong>، <em> یا <i>، <u>، <strike> یا <s>، <sup>، <sub>، <blockquote>، <code>، <pre>، <hr>، <br>، <p>، <a href="" title="">، <span style="">، <div align="">
تجدید کد امنیتی