Java数据库访问方案探究

举报
Jet Ding 发表于 2020/09/30 17:10:38 2020/09/30
【摘要】 在我们书写程序的过程中,操作数据库是非常重要的一环,本文我们会探讨一下在Java编程语言中有哪些比较有代表性的技术用以访问数据库的数据。

【引言】

在我们书写程序的过程中,操作数据库是非常重要的一环,本文我们会探讨一下在Java编程语言中有哪些比较有代表性的技术用以访问数据库的数据。

 

Apache Commons DbUtils

【简介】

Commons DbUtils库提供了一组工具类,旨在使JDBC的操作使用更加简单。我们知道,在JDBC操作中,资源清理部分是最无趣且容易出错的工作。Commons DbUtils将所有的清理任务从代码中抽象出来,让你在使用JDBC的时候,只需要做你真正想做的事情:

查询和更新数据。

 

使用DbUtils的一些优点是:

 

l  不存在资源泄露的可能性。正确的JDBC编码并不难,但很费时,也很繁琐。这往往会导致连接泄漏,可能难以追踪。

l  更干净,更清晰的持久化代码。在数据库中用于数据预留操作所需的代码量大大减少。你可以更专注于业务逻辑代码,而不会因为资源清理而变得杂乱无章。

l  ResultSets数据自动填充JavaBean属性。 你不需要通过调用setter方法将列值手动复制到Bean实例中。ResultSet的每一行数据可以用一个完整填充的Bean实例来表示。

【设计初衷】

DbUtils的设计是为了:

l  短小精悍 -- 能够在短时间内理解整个包的内容。

l  透明 – DbUtils只做两件事情:查询和清理。

l  快速 -- 你不需要创建无数个临时对象来使用DbUtils

 

它不是:

l  一个对象/关系型的桥梁。 DbUtils是为希望使用JDBC的开发者而准备的。

l  一个数据访问对象(DAO)框架 - DbUtils可以用来构建一个DAO框架。

l  一个面向对象的一般数据库对象的抽象,如表、列或PrimaryKey

l  一个任何类型的重量级框架----这里的目标是成为一个简单直接且易于使用的JDBC帮助库。

 

【使用案例】

DbUtils的核心类/接口是QueryRunnerResultSetHandler

        // 创建一个ResultSetHandler实现,把第一行变成一个Object[]

        ResultSetHandler<Object[]> h = new ResultSetHandler<Object[]>() {

            public Object[] handle(ResultSet rsthrows SQLException {

                if (!rs.next()) {

                    return null;

                }

 

                ResultSetMetaData meta = rs.getMetaData();

                int cols = meta.getColumnCount();

                Object[] result = new Object[cols];

 

                for (int i = 0; i < cols; i++) {

                    result[i] = rs.getObject(i + 1);

                }

 

                return result;

            }

        };

 

        // 从给定的数据源来创建一个QueryRunner

        QueryRunner run = new QueryRunner(dataSource);

 

        // 执行查询并从处理程序中获取结果

        Object[] result = run.query("SELECT * FROM Person WHERE name=?", h, "Xiao Ming");

 

插入或更新数据

        QueryRunner run = new QueryRunner( dataSource );

        try

        {

            // 执行SQL更新语句,并返回更新的数量

            int inserts = run.update"INSERT INTO Person (name,height) VALUES (?,?)",

                                      "Xiao Ming"1.82 );

        

            int updates = run.update"UPDATE Person SET height=? WHERE name=?",

                                      2.05"Xiao Ming" );

        }

        catch(SQLException sqle) {

            // 处理异常

        }

 

异步执行调用

        ExecutorCompletionService<Integerexecutor = new ExecutorCompletionService<Integer>(

                Executors.newCachedThreadPool());

        AsyncQueryRunner asyncRun = new AsyncQueryRunner(dataSource);

 

        try {

            // 为更新调用创建一个Callable

            Callable<Integercallable = asyncRun.update("UPDATE Person SET height=? WHERE name=?"2.05"Xiao Ming");

            // Callable提交给执行者

            executor.submit(callable);

        } catch (SQLException sqle) {

            // 处理异常

        }

 

        // 等待某个时机执行,或在另一线程中执行

        try {

            // 获取更新的结果

            Integer updates = executor.take().get();

        } catch (InterruptedException ie) {

            // 处理异常

        }

 

 

JavaBean数据返回

单一对象

        QueryRunner run = new QueryRunner(dataSource);

 

        // 使用BeanHandler实现来转换第一行数据为一个Bean示例

        ResultSetHandler<Personh = new BeanHandler<Person>(Person.class);

        

        // 执行语句,返回一个JavaBean实例对象

        Person p = run.query(

            "SELECT * FROM Person WHERE name=?", h, "Xiao Ming"); 

 

对象列表

        QueryRunner run = new QueryRunner(dataSource);

 

        // 使用BeanListHandler实现来转换所有的行为一个对象列表

        ResultSetHandler<List<Person>> h = new BeanListHandler<Person>(Person.class);

 

        // 执行SQL语句并将结果返回到对象列表

        List<Personpersons = run.query("SELECT * FROM Person", h);

 

 

 

License

Apache License 2.0

【最新版本】

1.7 2017720

【官方网站】

http://commons.apache.org/proper/commons-dbutils/

 

 

MyBatis

【简介】

MyBatis 是一款优秀的持久层框架,它支持自定义 SQL、存储过程以及高级映射。MyBatis 免除了几乎所有的 JDBC 代码,设置参数以及获取结果集的工作。 MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJOPlain Old Java Objects,普通老式 Java 对象)为数据库中的记录。

【安装】

        <dependency>

            <groupId>org.mybatis</groupId>

            <artifactId>mybatis</artifactId>

            <version>3.5.2</version>

        </dependency>

         

        <dependency>

            <groupId>org.mybatis</groupId>

            <artifactId>mybatis-spring</artifactId>

            <version>2.0.2</version>

        </dependency>

 

XML配置使用案例】

调用例子:

        Reader reader = Resources.getResourceAsReader("SqlMapConfig.xml");

        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);     

        SqlSession session = sqlSessionFactory.openSession();

        

        //Create a new student object

        Student student = new Student("Xiao Ming""test@gmail.com" ); 

              

        //Insert student data      

        session.insert("Student.insert", student);

        System.out.println("record inserted successfully");

        session.commit();

        session.close();

 

学生类定义:

    public class Student {

        private int id;

        private String name;

        private String email

     }

 

SqlMapConfig.xml:

 

<?xml version = "1.0" encoding = "UTF-8"?>

 

<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd">

<configuration>

        

   <environments default = "development">

      <environment id = "development">

         <transactionManager type = "JDBC"/> 

            

         <dataSource type = "POOLED">

            <property name = "driver" value = ""/>

            <property name = "url" value = ""/>

            <property name = "username" value = ""/>

            <property name = "password" value = ""/>

         </dataSource>   

         

      </environment>

   </environments>

    

   <mappers>

      <mapper resource = "mybatis/Student.xml"/>

   </mappers>

   

</configuration>

 

Student.xml

<?xml version = "1.0" encoding = "UTF-8"?>

 

<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"  "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

    

<mapper namespace = "Student">

 

   <insert id = "insert" parameterType = "Student">

      INSERT INTO STUDENT (NAME, EMAIL ) VALUES (#{name}, #{email});

            

      <selectKey keyProperty = "id" resultType = "int" order = "AFTER">

         select last_insert_id() as id

      </selectKey>   

            

   </insert>

        

</mapper>

 

【注释使用案例】

注释配置

@Configuration

@MapperScan("your-package")

public class PersistenceConfig {

 

    @Bean

    public DataSource dataSource() {

     ...

    }

 

    @Bean

    public SqlSessionFactory sqlSessionFactory() throws Exception {

        SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();

        factoryBean.setDataSource(dataSource());

        return factoryBean.getObject();

    }

}

 

 

基本对象操作

    @Insert("Insert into person(name) values (#{name})")

    public Integer save(Person person);

     

    @Update("Update Person set name= #{name} where personId=#{personId}")

    public void updatePerson(Person person);

     

    @Delete("Delete from Person where personId=#{personId}")

    public void deletePersonById(Integer personId);

     

    @Select("SELECT person.personId, person.name FROM person 

      WHERE person.personId = #{personId}")

    Person getPerson(Integer personId);

       

 

多对多关联

    @Select("select addressId, streetAddress, personId from address 

    where personId=#{personId}")

 

    public Address getAddresses(Integer personId);

 

    @Results(value={@Result(property="addresses",javaType=List.class,column="personId",many=@Many(select="getAddresses"))})

 

返回Map

    @Select("select * from Person")

    @MapKey("personId")

    Map<IntegerPersongetAllPerson();

 

 

动态SQL

public class MyBatisUtil {

  

    // ...

  

    public String getPersonByName(String name){

        return new SQL() {{

            SELECT("*");

            FROM("person");

            WHERE("name like #{name} || '%'");

        }}.toString();

    }

}

 

 

    @SelectProvider(type=MyBatisUtil.class, method="getPersonByName")

    public Person getPersonByName(String name);

 

 

调用存储过程

    @Select(value = "{CALL getPersonByProc(#{personId,mode=IN,jdbcType=INTEGER})}")

 

    @Options(statementType = StatementType.CALLABLE)

    public Person getPersonByProc(Integer personId);

 

License

Apache License 2.0

【最新版本】

3.5.4 202039

【官方网站】

https://mybatis.org/mybatis-3

 

MyBatis-Plus

【简介】

MyBatis-Plus(简称 MP)是一个 MyBatis 的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。

 

MyBatis-Plus可以自动注入基本的SQL片段,有一个强大而灵活的where条件封装器,使用它可以为你节省大量的开发时间。

 

MyBatis-Plus有许多有用的插件(如代码生成器、自动分页、性能分析等),它基本上提供了您需要的一切。

 

【安装】

 

        <dependency>

            <groupId>com.baomidou</groupId>

            <artifactId>mybatis-plus-boot-starter</artifactId>

            <version>3.3.1.tmp</version>

        </dependency>

 

【配置】

 

@SpringBootApplication

@MapperScan("com.baomidou.mybatisplus.samples.quickstart.mapper")

public class Application {

 

    public static void main(String[] args) {

        SpringApplication.run(QuickStartApplication.class, args);

    }

 

}

 

【使用案例】

@Data

public class User {

    private Long id;

    private String name;

    private Integer age;

    private String email;

}

 

public interface UserMapper extends BaseMapper<User> {

 

}

 

 

@Autowired

private UserMapper userMapper;

 

@Test

public void testSelect() {

    System.out.println(("----- selectAll method test ------"));

    List<UseruserList = userMapper.selectList(null);

    Assert.assertEquals(5userList.size());

    userList.forEach(System.out::println);

}

 

 

License

Apache License 2.0

【最新版本】

3.3.1.tmp 2020127

【官方网站】

https://mp.baomidou.com/

 

Hibernate

Hibernate ORM使开发人员能够更容易地编写使用数据库访问的应用程序。作为一个对象/关系型映射(ORM)框架,Hibernate关注的是数据的持久性,这一点特别适用于关系型数据库。

 

【安装】

        <dependency>

            <groupId>org.springframework.boot</groupId>

            <artifactId>spring-boot-starter-data-jpa</artifactId>

        </dependency>

 

【使用案例】

创建JPA实体类

@Entity

@Table(name="TBL_EMPLOYEES")

public class EmployeeEntity {

 

    @Id

    @GeneratedValue

    private Long id;

     

    @Column(name="first_name")

    private String firstName;

     

    @Column(name="last_name")

    private String lastName;

     

    @Column(name="email", nullable=false, length=200)

    private String email;

     

    @Override

    public String toString() {

        return "EmployeeEntity [id=" + id + ", firstName=" + firstName + 

                ", lastName=" + lastName + ", email=" + email   + "]";

    }

}

创建JPA存储库

@Repository

public interface EmployeeRepository extends JpaRepository<EmployeeEntityLong> {

 

}

使用存储库

    @Autowired

    EmployeeRepository repository;

 

 

        Optional<EmployeeEntityemp = repository.findById(2L);

 

        logger.info("Employee id 2 -> {}"emp.get());

 

License

LGPL2.1

【最新版本】

5.4.13 2020326

【官方网站】

https://github.com/hibernate/hibernate-orm

 

JdbcTemplate

Spring JDBC中的所有类都分为四个独立的包。

 

core - JDBC的核心功能。这个包下的一些重要类包括JdbcTemplateSimpleJdbcInsertSimpleJdbcCallNamedParameterJdbcTemplate

 

datasource - 用于访问数据源的实用类。它也有各种数据源实现,用于测试Jakarta EE容器外的JDBC代码。

 

object - 以面向对象的方式访问DB。它允许执行查询并将结果作为业务对象返回。它还可以将查询结果映射到业务对象的列和属性之间。

 

support--支持coreobject下的类。如:SQLException支持。

 

【安装】

        <dependency>

            <groupId>org.springframework.boot</groupId>

            <artifactId>spring-boot-starter-jdbc</artifactId>

        </dependency>

 

【配置】

@Configuration

@ComponentScan("your package")

public class SpringJdbcConfig {

    @Bean

    public DataSource mysqlDataSource() {

        DriverManagerDataSource dataSource = new DriverManagerDataSource();

        dataSource.setDriverClassName("");

        dataSource.setUrl("");

        dataSource.setUsername("");

        dataSource.setPassword("");

 

        return dataSource;

    }

}

 

 

【使用案例】

 

基本查询

        int result = jdbcTemplate.queryForObject(

            "SELECT COUNT(*) FROM EMPLOYEE"Integer.class);

 

 

插入数据

    public int addEmplyee(int id) {

        return jdbcTemplate.update(

          "INSERT INTO EMPLOYEE VALUES (?, ?, ?, ?)", id, "Bill""Gates""USA");

    }

 

有命名参数的查询

例子1

        SqlParameterSource namedParameters = new MapSqlParameterSource().addValue("id"1);

        return namedParameterJdbcTemplate.queryForObject(

          "SELECT FIRST_NAME FROM EMPLOYEE WHERE ID = :id", namedParameters, String.class);

 

例子2

        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);

 

 

 

将查询结果映射到Java对象

 

 

public class EmployeeRowMapper implements RowMapper<Employee> {

    @Override

    public Employee mapRow(ResultSet rsint rowNumthrows 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;

    }

}

        String query = "SELECT * FROM EMPLOYEE WHERE ID = ?";

        List<Employeeemployees = jdbcTemplate.queryForObject(

          query, new Object[] { id }, new EmployeeRowMapper());

 

 

异常处理

 

public class CustomSQLErrorCodeTranslator extends SQLErrorCodeSQLExceptionTranslator {

    @Override

    protected DataAccessException

      customTranslate(String taskString sqlSQLException sqlException) {

        if (sqlException.getErrorCode() == 23505) {

          return new DuplicateKeyException(

            "Custom Exception translator - Integrity constraint violation.", sqlException);

        }

        return null;

    }

}

 

 

        CustomSQLErrorCodeTranslator customSQLErrorCodeTranslator = 

        new CustomSQLErrorCodeTranslator();

        jdbcTemplate.setExceptionTranslator(customSQLErrorCodeTranslator);

 

 

使用SimpleJdbc类的JDBC操作

添加数据

例子1

 

        SimpleJdbcInsert simpleJdbcInsert = 

        new SimpleJdbcInsert(dataSource).withTableName("EMPLOYEE");

 

    public int addEmplyee(Employee emp) {

        Map<StringObjectparameters = new HashMap<StringObject>();

        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);

    }

 

 

例子2

        SimpleJdbcInsert simpleJdbcInsert = new SimpleJdbcInsert(dataSource).withTableName("EMPLOYEE")

                .usingGeneratedKeyColumns("ID");

 

        Number id = simpleJdbcInsert.executeAndReturnKey(parameters);

        System.out.println("Generated id - " + id.longValue());

 

 

使用SimpleJdbcCall访问存储过程

        SimpleJdbcCall simpleJdbcCall = new SimpleJdbcCall(dataSource)

        .withProcedureName("READ_EMPLOYEE");

 

    public Employee getEmployeeUsingSimpleJdbcCall(int id) {

        SqlParameterSource in = new MapSqlParameterSource().addValue("in_id", id);

        Map<StringObjectout = simpleJdbcCall.execute(in);

     

        Employee emp = new Employee();

        emp.setFirstName((String) out.get("FIRST_NAME"));

        emp.setLastName((String) out.get("LAST_NAME"));

     

        return emp;

    }

 

 

批量作业

使用JdbcTemplate的基本批量操作

 

    public int[] batchUpdateUsingJdbcTemplate(List<Employeeemployees) {

        return jdbcTemplate.batchUpdate("INSERT INTO EMPLOYEE VALUES (?, ?, ?, ?)",

            new BatchPreparedStatementSetter() {

                @Override

                public void setValues(PreparedStatement psint ithrows SQLException {

                    ps.setInt(1employees.get(i).getId());

                    ps.setString(2employees.get(i).getFirstName());

                    ps.setString(3employees.get(i).getLastName());

                    ps.setString(4employees.get(i).getAddress();

                }

                @Override

                public int getBatchSize() {

                    return 50;

                }

            });

    }

 

 

使用NamedParameterJdbcTemplate的批量操作

 

        SqlParameterSource[] batch = SqlParameterSourceUtils.createBatch(employees.toArray());

        int[] updateCounts = namedParameterJdbcTemplate.batchUpdate(

            "INSERT INTO EMPLOYEE VALUES (:id, :firstName, :lastName, :address)", batch);

        return updateCounts;

 

 

License

Apache License 2.0

【最新版本】

5.2.5.RELEASE 2020324

【官方网站】

https://github.com/spring-projects/spring-framework

 

Spring Data JDBC

Spring Data JDBCSpring Data家族的一部分,它使得JDBC存储库的实现变得容易。该模块提供了基于JDBC的数据访问层的增强支持。它使得构建使用数据访问技术的Spring应用程序更加容易。

 

Spring Data JDBC的目标是简单。为了实现这个目标,它不提供缓存、惰性加载、后写或许多其他JPA的特性。这使得Spring Data JDBC成为一个简单的、有限的、有些争议的ORM

 

总量根

Spring数据存储库的灵感来源于Eric Evans在《Domain Driven Design》一书中描述的存储库。这样做的一个结果是,每个总量根都有一个存储库。总量根是同一本书中的另一个概念,它是一个实体,这个实体控制着其他实体的生命周期,而这些实体共同构成了一个总量。总量是你的模型的一个子集,它在方法调用到你的总量根这个过程中是保持一致的。

 

Spring Data JDBC鼓励按照上面这些想法来建模。

 

特点

l  用于简单聚合的CRUD操作与可定制的命名策略。

l  支持@Query注释。

l  支持MyBatis查询。

l  事件。

l  通过引入@EnableJdbcRepositories,实现了基于JavaConfig的版本库配置。

【安装】

        <dependency>

            <groupId>org.springframework.data</groupId>

            <artifactId>spring-data-jdbc</artifactId>

            <version>${version}.RELEASE</version>

        </dependency>

 

【配置】

@Configuration

@EnableJdbcRepositories

class ApplicationConfig extends AbstractJdbcConfiguration {

 

  @Bean

  public DataSource dataSource() {

    return …;

  }

 

  @Bean

  public NamedParameterJdbcTemplate namedParameterJdbcTemplate(DataSource dataSource) {

    return new NamedParameterJdbcTemplate(dataSource);

  }

}

 

【使用案例】

public interface PersonRepository extends CrudRepository<PersonLong> {

 

    @Query("SELECT * FROM person WHERE lastname = :lastname")

    List<PersonfindByLastname(String lastname);

  

    @Query("SELECT * FROM person WHERE firstname LIKE :firstname")

    List<PersonfindByFirstnameLike(String firstname);

  }

  

  @Service

  public class MyService {

  

    private final PersonRepository repository;

  

    public MyService(PersonRepository repository) {

      this.repository = repository;

    }

  

    public void doWork() {

  

      repository.deleteAll();

  

      Person person = new Person();

      person.setFirstname("Jens");

      person.setLastname("Schauder");

      repository.save(person);

  

      List<PersonlastNameResults = repository.findByLastname("Schauder");

      List<PersonfirstNameResults = repository.findByFirstnameLike("Je%");

   }

  }

  

License

Apache License 2.0

 

【最新版本】

1.1.6.RELEASE 2020330

【官方网站】

https://spring.io/projects/spring-data-jdbc

 

Spring Data JPA

Spring Data JPASpring Data家族的一部分,它可以轻松实现基于JPA的存储库。这个模块处理了对基于JPA的数据访问层的增强支持。它使得构建使用数据访问技术的Spring驱动的应用程序更加容易。

 

在相当长的一段时间里,实现一个应用程序的数据访问层一直是很麻烦的。为了执行简单的查询、分页和审计,需要编写太多的模板代码。Spring Data JPA的目的是改善数据访问层的实现。作为开发者,你可以编写你的存储库接口,包括自定义的查找器方法,Spring将自动提供实现。

 

特点

 

l  支持基于SpringJPA构建资源库的复杂支持。

l  支持Querydsl谓词,从而支持类型安全的JPA查询。

l  域类的透明审计

l  支持分页显示,动态查询执行,能够集成自定义的数据访问代码

l  在引导时验证@Query注释的查询的验证

l  支持基于XML的实体映射

l  通过引入@EnableJpaRepositories来配置基于JavaConfig的版本库。


在上面的内容里,我们探讨了如下几种访问数据库的技术: DbUtils, MyBatis, Mybatis Plus, Hibernate, JdbcTemplateSpring Data JDBC, Spring Data JPA。 其中DbUtils相对老旧一些,因此如果在选取技术时在意社区活跃度的话,可以规避一下这个程序库。

 


【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。