今天,在使用Java编写数据库应用程序时,可以使用许多替代方法。当然,JPA是这些替代品中使用最多的一种。您可以在互联网上找到许多关于JPA的文章和教程。我在本文中的目标不是教你JPA,而是在JPA之前看一下这个世界,这样你就可以理解为JPA开发的问题。
出于这个原因,为了使事情清楚,让我们定义一个简单的问题,并看看这个问题的可能解决方案。假设我们有一个应用程序,用于管理库中所有书籍的列表。为此,我们在数据库中有一个名为“Books”的表,并使用我们的应用程序在此表上执行各种CRUD(创建,读取,更新,删除)操作。
下面的第一个解决方案是使用JDBC的简单Java类,Java的基本数据库连接模型。正如您第一眼看到的那样,即使是简单的更新和简单的查询操作,也会编写很长的代码。
package com.lets.learn.jdbc;
//STEP 1 --> import libraries
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
public class JDBCExample {
// Driver
static final String dbURL = "jdbc:oracle:thin:@localhost:1521:testDB";
// Connection URL
static final String jdbcDriver = "oracle.jdbc.driver.OracleDriver";
// Database credentials
static final String userName = "testDbUser";
static final String password = "password1";
public static void main(String[] args) {
Book book = new Book(1, "J.K. ROWLING", "Harry Potter");
updateBook(book);
Book myBook = queryBook(1);
}
private static void updateBook(Book book) {
// Define variables
Connection connection = null;
PreparedStatement statement = null;
ResultSet resultSet = null;
try {
// Step 2 --> Load and register the driver
Class.forName(jdbcDriver);
// Step 3 --> Create connection
connection = DriverManager.getConnection(dbURL, userName, password);
// Step 4 --> Create statement
statement = connection.prepareStatement("UPDATE BOOKS SET AUTHOR=?,TITLE=? WHERE ID=?");
statement.setString(1, book.getAuthor());
statement.setString(2, book.getTitle());
statement.setInt(3, book.getId());
// Step 5 --> Execute query
// preparedStatement'a çevir, parametrelerin nasıl set edildiğini göster
resultSet = statement.executeQuery();
// Step 6 --> Retrieve data
// noop here
// Step 7: Clean-up the environment
resultSet.close();
statement.close();
connection.close();
} catch (SQLException se) {
// Handle errors for JDBC
se.printStackTrace();
} catch (ClassNotFoundException e) {
// Handle errors for Class.forName
e.printStackTrace();
} finally {
// use finally block to close resources
try {
if (statement != null) {
statement.close();
}
if (connection != null) {
connection.close();
}
} catch (SQLException se) {
se.printStackTrace();
}
}
}
private static Book queryBook(int id) {
// Define variables
Connection connection = null;
PreparedStatement statement = null;
ResultSet resultSet = null;
Book book = null;
try {
// Step 2 --> Load and register the driver
Class.forName(jdbcDriver);
// Step 3 --> Create connection
connection = DriverManager.getConnection(dbURL, userName, password);
// Step 4 --> Create statement
statement = connection.prepareStatement("SELECT * FROM BOOKS WHERE ID=?");
statement.setInt(1, id);
// Step 5 --> Execute query
resultSet = statement.executeQuery();
// Step 6 --> Retrieve data
if (resultSet.next()) {
// populate a java object
book = new Book();
book.setId(resultSet.getInt("ID"));
book.setTitle(resultSet.getString("TITLE"));
book.setAuthor(resultSet.getString("AUTHOR"));
}
// Step 7: Clean-up the environment
resultSet.close();
statement.close();
connection.close();
} catch (SQLException se) {
// Handle errors for JDBC
se.printStackTrace();
} catch (ClassNotFoundException e) {
// Handle errors for Class.forName
e.printStackTrace();
} finally {
// use finally block to close resources
try {
if (statement != null) {
statement.close();
}
if (connection != null) {
connection.close();
}
} catch (SQLException se) {
se.printStackTrace();
}
}
return book;
}
}
那么,这段代码片段做了什么?
· 创建数据库连接
· 更新或查询数据库上的数据
· 管理用于数据库连接的所有对象的生命周期
· 对创建或关闭连接时可能发生的异常进行异常处理
如您所见,使用JDBC编写程序涉及一些常规步骤,您不希望在每个CRUD操作中反复执行这些步骤。使用这些步骤进行连接管理时可能出现的错误(高级开发人员可能记得很清楚)有时会导致连接池问题,从而导致整个应用程序崩溃。因此,创建和关闭连接的代码块在其自己的函数中定义一次,然后,通过在必要时调用这些函数来调用这些函数以避免重复。
除此之外,我们还在上面的代码中做了一件事。它还在我们的Java数据模型和数据库中的数据模型之间进行连续转换。在读取和写入数据时,这种转换成为强制性的。在查询数据时,我们在查询中使用各种参数,这些参数的值来自我们程序中的其他Java对象。
我们从相关的Java对象中读取这些值并将它们放在查询中。运行查询后,我们从结果集中读取值并将它们写回Java对象。这个问题的解决方案并不像以前那么容易。Spring JDBC就是为了救援而来的。
Spring拥有数千名用户,使用Spring Data JDBC为这个问题提供了一个很好的解决方案。在以下示例中,您将看到Spring Data JDBC遇到相同问题的解决方案。
package com.lets.learn.springjdbc;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
public class SpringJDBCExample {
// Driver
static final String dbURL = "jdbc:oracle:thin:@localhost:1521:testDB";
// Connection URL
static final String jdbcDriver = "oracle.jdbc.driver.OracleDriver";
// Database credentials
static final String userName = "testDbUser";
static final String password = "password1";
private static DriverManagerDataSource dataSource = new DriverManagerDataSource();
private static JdbcTemplate jdbcTemplate = new JdbcTemplate(getDataSource());
public static void main(String[] args) throws Exception {
Book book = new Book(1, "J.K. ROWLING", "Harry Potter");
updateBook(book);
Book myBook = queryBook(1);
}
public static void updateBook(Book book) {
jdbcTemplate.update("UPDATE BOOKS SET AUTHOR=?,TITLE=? WHERE ID=?", book.getAuthor(), book.getTitle(), book.getId());
}
public static Book queryBook(int id) {
return jdbcTemplate.queryForObject(
"SELECT * FROM BOOKS WHERE ID = ?", new Object[] { id }, new BookRowMapper());
}
private static DriverManagerDataSource getDataSource() {
dataSource.setDriverClassName(jdbcDriver);
dataSource.setUrl(dbURL);
dataSource.setUsername(userName);
dataSource.setPassword(password);
return dataSource;
}
}
package com.lets.learn.springjdbc;
import java.sql.ResultSet;
import java.sql.SQLException;
import org.springframework.jdbc.core.RowMapper;
public class BookRowMapper implements RowMapper<Book> {
@Override
public Book mapRow(ResultSet rs, int rowNum) throws SQLException {
Book book = new Book();
book.setId(rs.getInt("ID"));
book.setTitle(rs.getString("TITLE"));
book.setAuthor(rs.getString("AUTHOR"));
return book;
}
}
当你查看代码时,我想你可能会立即注意到一些差异。这些是什么?
在每个操作之前创建连接并在操作之后关闭连接的任务现在从程序员处获取并转移到Spring Context。代码重复最小化
结果集中的对象不会手动转换为Java对象,而是使用RowMapper结构。这样,定义了如何进行转换,避免了代码重复。
但是,仍有一个未解决的问题。我们的查询仍在Java类中。必要时查找和更新这些查询 - 例如,当我们的数据库模型发生重大变化时 - 对于大规模应用程序来说是一个巨大的成本。这就是JPA的用武之地。
让我们来看看JPA同样问题的解决方案。
package com.lets.learn.jpa;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import com.lets.learn.jpa.entity.Book;
import com.lets.learn.jpa.repository.BookJpaRepository;
@SpringBootApplication
public class JpaDemoApplication implements CommandLineRunner {
@Autowired
BookJpaRepository repository;
public static void main(String[] args) {
SpringApplication.run(JpaDemoApplication.class, args);
}
@Override
public void run(String... args) throws Exception {
Book firstBook =repository.findById(10001);
// insert new book
repository.insert(new Book(1 ,"Hobbit", "J.R.R Tolkien"));
// update a book
repository.update(new Book(10003, "A Tale Of Two Cities", "Charles Dickens"));
// delete a book
repository.deleteById(10002);
//get all books
repository.findAll();
}
}
package com.lets.learn.jpa.repository;
import java.util.List;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.TypedQuery;
import javax.transaction.Transactional;
import org.springframework.stereotype.Repository;
import com.lets.learn.jpa.entity.Book;
@Repository
@Transactional
public class BookJpaRepository {
// connect to the database
@PersistenceContext
EntityManager entityManager;
public List<Book> findAll() {
TypedQuery<Book> namedQuery = entityManager.createNamedQuery("find_all_books", Book.class);
return namedQuery.getResultList();
}
public Book findById(int id) {
return entityManager.find(Book.class, id);// JPA
}
public Book update(Book book) {
return entityManager.merge(book);
}
public Book insert(Book book) {
return entityManager.merge(book);
}
public void deleteById(int id) {
Book book = findById(id);
entityManager.remove(book);
}
}
package com.lets.learn.jpa.entity;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.NamedQuery;
@Entity
@NamedQuery(name="find_all_books", query="select b from Book b")
public class Book {
@Id
@GeneratedValue
private int id;
private String title;
private String author;
public Book() {
}
public Book(int id, String title, String author) {
super();
this.id = id;
this.title = title;
this.author = author;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
}
正如您在上面的示例中所看到的,JPA:
· 允许我们为CRUD操作编写更简单,更易理解的代码
· 防止我们使用已经为常用操作提供的结构重新发明轮子
· 消除了Java对象和数据库表之间持续转换的需要
· 当数据库中的模型发生更改时,可以更轻松地将更改反映到Java层
我希望这篇文章能帮助您理解我们迄今为止用Java开发数据库应用程序的方式。