Статьи

Использование пула соединений Tomcat JDBC в автономном приложении Java

Это гостевая статья от нашего партнера по W4G Кларенса Хо, автора Pro Spring 3 от APress. Вы можете найти код купона на скидку для книги в конце статьи, только для читателей Java Code Geeks ! Наслаждайтесь!

При использовании пула соединений JDBC в автономных приложениях Java, которые требуют доступа к данным, большинство разработчиков будут использовать commons-dbcp или c3p0 . В этом руководстве мы обсудим использование пула соединений JDBC в веб-контейнере Apache Tomcat в автономных приложениях Java.

Одной из новых функций Tomcat 7 является пул соединений tomcat-jdbc, который заменяет пул соединений commons-dbcp. Основные преимущества tomcat-jdbc перед commons-dbcp и другими библиотеками пулов соединений перечислены ниже:
  • Поддержка сред с высокой степенью одновременности и многоядерных / процессорных систем.
  • Commons-dbcp однопоточный и медленный
  • Commons-dbcp сложный (более 60 классов), в то время как ядро ​​tomcat-jdbc содержит только 8 классов
  • Поддержка асинхронного поиска соединения
  • Поддержка XA-соединения
  • Объект пула соединений предоставляет MBean, который можно зарегистрировать для мониторинга
  • Поддерживались большинство атрибутов в common-dbcp, а также множество улучшенных атрибутов.
  • Поддержка JDBC-перехватчиков
Подробное описание и документацию по настройке см. На официальной странице документации на веб-сайте Apache Tomcat.
В этом руководстве мы продемонстрируем использование tomcat-jdbc при разработке простого автономного Java-приложения для доступа к данным. Это приложение будет использовать следующие рамки и библиотеки:
  • Spring Framework 3.1.1
  • Hibernate 4.1.3
  • Spring Data JPA 1.1.0
  • Tomcat JDBC Connection Pool 7.0.27
  • База данных H2 1.3.167
  • Гуава 12.0
Образец был разработан с использованием SpringSource Tool Suite, а ZIP-архив можно скачать в конце этой статьи. С другой стороны, в этом руководстве предполагается, что у вас уже есть понимание по разработке приложений JPA с использованием Spring и Hibernate.

зависимости

Зависимости проекта управлялись Maven. Ниже приведен фрагмент из файла POM (pom.xml) проекта.

Листинг 1 — Зависимости проекта

001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073
074
075
076
077
078
079
080
081
082
083
084
085
086
087
088
089
090
091
092
093
094
095
096
097
098
099
100
<properties>
    <maven.test.failure.ignore>true</maven.test.failure.ignore>
    <spring.framework.version>3.1.1.RELEASE</spring.framework.version>
  <hibernate.version>4.1.3.Final</hibernate.version>
  <spring.data.jpa.version>1.1.0.RELEASE</spring.data.jpa.version>
  <tomcat.dbcp.version>7.0.27</tomcat.dbcp.version>
  <h2.version>1.3.167</h2.version>
  <slf4j.version>1.6.4</slf4j.version>
  <log4j.version>1.2.16</log4j.version>
  <guava.version>12.0</guava.version>
</properties>
 
<dependencies>
 
 <!-- Hibernate -->
 
  <dependency>
      <groupId>org.hibernate</groupId>
      <artifactId>hibernate-entitymanager</artifactId>      
      <version>${hibernate.version}</version>
  </dependency>
 
 <!-- Spring Framework -->
 
  <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context-support</artifactId>
      <version>${spring.framework.version}</version>
  </dependency>
 
  <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-aop</artifactId>
      <version>${spring.framework.version}</version>
  </dependency>
 
  <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-orm</artifactId>
      <version>${spring.framework.version}</version>
  </dependency>
 
 <!-- Spring Data JPA -->
 
  <dependency>
      <groupId>org.springframework.data</groupId>
      <artifactId>spring-data-jpa</artifactId>
      <version>${spring.data.jpa.version}</version>
  </dependency>
 
 <!-- Tomcat DBCP -->
 
  <dependency>
      <groupId>org.apache.tomcat</groupId>
      <artifactId>tomcat-jdbc</artifactId>
      <version>${tomcat.dbcp.version}</version>
  </dependency>
           
 <!-- Logging -->
 
  <dependency>
      <groupId>org.slf4j</groupId>
      <artifactId>slf4j-api</artifactId>
      <version>${slf4j.version}</version>
  </dependency>
 
  <dependency>
      <groupId>org.slf4j</groupId>
      <artifactId>jcl-over-slf4j</artifactId>
      <version>${slf4j.version}</version>
      <scope>runtime</scope>
  </dependency>
 
  <dependency>
      <groupId>org.slf4j</groupId>
      <artifactId>slf4j-log4j12</artifactId>
      <version>${slf4j.version}</version>
      <scope>runtime</scope>
  </dependency>
 
  <dependency>
      <groupId>log4j</groupId>
      <artifactId>log4j</artifactId>
      <version>${log4j.version}</version>
  </dependency>
        
 <!-- Others -->
 
  <dependency>
      <groupId>com.h2database</groupId>
      <artifactId>h2</artifactId>
      <version>${h2.version}</version>
  </dependency>
 
  <dependency>
      <groupId>com.google.guava</groupId>
      <artifactId>guava</artifactId>
      <version>${guava.version}</version>
  </dependency>
</dependencies>

Доменная объектная модель

Объектная модель представляет собой простую модель контактной информации. Каждый контакт имеет свое имя, фамилию и дату рождения. Кроме того, каждый контакт будет связан с нулевым или большим количеством увлечений (например, плавание, бег трусцой, чтение и т. Д.). В DOM есть 2 основных класса, а именно классы Contact и Hobby. Перечисление 2 и 3 показывает листинг кода классов соответственно.

Листинг 2 — класс Contact

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
@Entity
@Table(name = "contact")
public class Contact {
 
 private Long id;
 private int version;
 private String firstName;
 private String lastName;
 private Date birthDate;
 private Set<Hobby> hobbies = new HashSet<Hobby>();
  
 @Id
 @GeneratedValue(strategy=GenerationType.IDENTITY)
 @Column(name = "ID")
 public Long getId() {
  return id;
 }
  
 public void setId(Long id) {
  this.id = id;
 }
  
 @Version
 @Column(name = "VERSION")
 public int getVersion() {
  return version;
 }
  
 public void setVersion(int version) {
  this.version = version;
 }
  
 @Column(name = "FIRST_NAME")
 public String getFirstName() {
  return firstName;
 }
  
 public void setFirstName(String firstName) {
  this.firstName = firstName;
 }
 
 @Column(name = "LAST_NAME")
 public String getLastName() {
  return lastName;
 }
  
 public void setLastName(String lastName) {
  this.lastName = lastName;
 }
  
 @Column(name = "BIRTH_DATE")
 @Temporal(TemporalType.DATE)
 public Date getBirthDate() {
  return birthDate;
 }
  
 public void setBirthDate(Date birthDate) {
  this.birthDate = birthDate;
 }
  
 @ManyToMany
 @JoinTable(name = "contact_hobby_detail",
       joinColumns = @JoinColumn(name = "CONTACT_ID"),
       inverseJoinColumns = @JoinColumn(name = "HOBBY_ID"))
 public Set<hobby> getHobbies() {
  return this.hobbies;
 }
 
 public void setHobbies(Set<hobby> hobbies) {
  this.hobbies = hobbies;
 }  
     
 public String toString() { 
  return "Contact - Id: " + id + ", First name: " + firstName
    + ", Last name: " + lastName + ", Birthday: " + birthDate;
 
  
}

Листинг 3 — класс Hobby

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
@Entity
@Table(name = "hobby")
public class Hobby {
 
 private String hobbyId;
 private Set<Contact> contacts = new HashSet<Contact>();
 
 public Hobby() {
 }
 
 public Hobby(String hobbyId) {
  this.hobbyId = hobbyId;
 }
 
 public Hobby(String hobbyId, Set<Contact> contacts) {
  this.hobbyId = hobbyId;
  this.contacts = contacts;
 }
 
 @Id
 @Column(name = "HOBBY_ID")
 public String getHobbyId() {
  return this.hobbyId;
 }
 
 public void setHobbyId(String hobbyId) {
  this.hobbyId = hobbyId;
 }
 
 @ManyToMany
 @JoinTable(name = "contact_hobby_detail",
       joinColumns = @JoinColumn(name = "HOBBY_ID"),
       inverseJoinColumns = @JoinColumn(name = "CONTACT_ID"))
 public Set<Contact> getContacts() {
  return this.contacts;
 }
 
 public void setContacts(Set<Contact> contacts) {
  this.contacts = contacts;
 }
  
}
В листингах 2 и 3 отмечается, что между классами Contact и Hobby существуют отношения «многие ко многим».

Схема базы данных

В этом уроке мы будем использовать базу данных H2 в памяти. Есть 3 таблицы:

  • КОНТАКТ: в таблице хранятся контактные данные
  • ХОББИ: в таблице хранится список хобби, доступных для приложения.
  • CONTACT_HOBBY_DETAIL: моделирует отношения «многие ко многим» между классами контактов и хобби
В листингах 4 и 5 показано содержимое сценария создания схемы базы данных и сценария заполнения данных тестирования соответственно.

Листинг 4 — Сценарий создания схемы базы данных (schema.sql)

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
DROP TABLE IF EXISTS CONTACT;
 
CREATE TABLE CONTACT (
 ID INT NOT NULL AUTO_INCREMENT
 ,FIRST_NAME VARCHAR(60) NOT NULL
 ,LAST_NAME VARCHAR(40) NOT NULL
 ,BIRTH_DATE DATE
 ,VERSION INT NOT NULL DEFAULT 0
 ,UNIQUE UQ_CONTACT_1 (FIRST_NAME, LAST_NAME)
 ,PRIMARY KEY (ID)
);
 
CREATE TABLE HOBBY (
  HOBBY_ID VARCHAR(20) NOT NULL
 ,PRIMARY KEY (HOBBY_ID)
);
 
 
CREATE TABLE CONTACT_HOBBY_DETAIL (
  CONTACT_ID INT NOT NULL
 ,HOBBY_ID VARCHAR(20) NOT NULL
 ,PRIMARY KEY (CONTACT_ID, HOBBY_ID)
 ,CONSTRAINT FK_CONTACT_HOBBY_DETAIL_1 FOREIGN KEY (CONTACT_ID)
  REFERENCES CONTACT (ID) ON DELETE CASCADE
 ,CONSTRAINT FK_CONTACT_HOBBY_DETAIL_2 FOREIGN KEY (HOBBY_ID)
  REFERENCES HOBBY (HOBBY_ID)
);

Листинг 5 — Сценарий заполнения данных тестирования (test-data.sql)

01
02
03
04
05
06
07
08
09
10
11
12
insert into contact (first_name, last_name, birth_date) values ('Clarence', 'Ho', '1980-07-30');
insert into contact (first_name, last_name, birth_date) values ('Scott', 'Tiger', '1990-11-02');
 
insert into hobby (hobby_id) values ('Swimming');
insert into hobby (hobby_id) values ('Jogging');
insert into hobby (hobby_id) values ('Programming');
insert into hobby (hobby_id) values ('Movies');
insert into hobby (hobby_id) values ('Reading');
 
insert into contact_hobby_detail(contact_id, hobby_id) values (1, 'Swimming');
insert into contact_hobby_detail(contact_id, hobby_id) values (1, 'Movies');
insert into contact_hobby_detail(contact_id, hobby_id) values (2, 'Swimming');

Сервисный уровень

На уровне сервиса существует 2 интерфейса:

  • ContactService: предоставлять услуги по доступу к контактной информации
  • HobbyService: предоставлять услуги для доступа к информации о хобби

В листинге 6 и 7 показаны интерфейсы ContactService и HobbyService соответственно.

Листинг 6 — интерфейс ContactService

1
2
3
4
5
6
7
8
9
public interface ContactService {
 
 public List<Contact> findAll();
  
 public Contact findById(Long id);
  
 public Contact save(Contact contact);
  
}

Листинг 7 — интерфейс HobbyService

1
2
3
4
5
public interface HobbyService {
 
 public List<Hobby> findAll();
  
}

Конфигурация пружины

Давайте посмотрим на конфигурации Spring. В листинге 8 показаны конфигурации источника данных, транзакции и JPA.

Листинг 8 — Конфигурация Spring JPA (datasource-tx-jpa.xml)

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
<!--Tomcat JDBC connection pool configutation -->
<bean id="dataSource" class="org.apache.tomcat.jdbc.pool.DataSource">
   <property name="driverClassName" value="org.h2.Driver" />
   <property name="url" value="jdbc:h2:mem:testdb" />
   <property name="username" value="sa" />
   <property name="password" value="" />
</bean>
 
<!--Intialize the database schema with test data -->
<jdbc:initialize-database data-source="dataSource">
   <jdbc:script location="classpath:schema.sql"/>
   <jdbc:script location="classpath:test-data.sql"/>
</jdbc:initialize-database>
   
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
   <property name="entityManagerFactory" ref="emf"/>
</bean>
 
<tx:annotation-driven transaction-manager="transactionManager" />
 
<bean id="emf" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
   <property name="dataSource" ref="dataSource" />
   <property name="jpaVendorAdapter">
      <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"/>
   </property>
   <property name="packagesToScan" value="com.skywidesoft.tomcat.dbcp.tutorial.domain"/>
   <property name="jpaProperties">
    <props>
     <prop key="hibernate.dialect">org.hibernate.dialect.H2Dialect</prop>
     <prop key="hibernate.max_fetch_depth">3</prop>
     <prop key="hibernate.jdbc.fetch_size">50</prop>
     <prop key="hibernate.jdbc.batch_size">10</prop>
     <prop key="hibernate.show_sql">true</prop>
    </props>
   </property>
</bean
 
<context:annotation-config/>
 
<!--Spring Data JPA Repository Configuration -->
<jpa:repositories base-package="com.skywidesoft.tomcat.dbcp.tutorial.repository"
         entity-manager-factory-ref="emf"
         transaction-manager-ref="transactionManager"/>

Некоторые основные моменты конфигурации в листинге 8 перечислены ниже:

  • Для bean-компонента dataSource класс org.apache.tomcat.jdbc.pool.DataSource использовался для предоставления интерфейса JDBC DataSource для базового соединения. Вы увидите, что конфигурация в основном такая же, как при использовании commons-dbcp.
  • Тег <jdbc: initialize-database> является поддержкой Spring 3.1 для инициализации базы данных схемой базы данных и данными тестирования
  • Тег <jpa: repositories> предназначен для настройки абстракции репозитория Spring Data JPA.

В листинге 9 показана конфигурация контекста приложения Spring.

Листинг 9 — Контекст приложения Spring (app-context.xml)

1
2
3
<import resource="classpath:datasource-tx-jpa.xml"/>
 
        <context:component-scan base-package="com.skywidesoft.tomcat.dbcp.tutorial.service.jpa"/>

Абстракция репозитория Spring Data JPA

Spring Data JPA абстракция репозитория обеспечивает упрощенный подход при разработке приложений доступа к данным на основе JPA. За подробностями обращайтесь на сайт проекта. Уровень абстракции хранилища разработан с использованием интерфейса Java. В листинге 10 и 11 приведен листинг кода интерфейсов ContactRepository и HobbyRepository соответственно.

Листинг 10 — Интерфейс ContactRepository

1
2
3
public interface ContactRepository extends CrudRepository<Contact, Long>{
 
}

Листинг 11 — Интерфейс HobbyRepository

1
2
3
public interface HobbyRepository extends CrudRepository<Hobby, String>{
 
}

Обратите внимание, что интерфейс просто расширяет интерфейс CruDRepository <T, ID> Spring Data Common, который уже обеспечивает общие операции доступа к данным (например, findAll, findOne, save, delete и т. Д.).

Классы реализации JPA

Следующим шагом является разработка реализации JPA интерфейсов уровня обслуживания в листинге 6 и 7. Классы принимают аннотации Spring Framework для объявления bean-компонента Spring, автоматического подключения зависимостей, требований транзакций и т. Д. В листинге 12 и 13 показан ContactServiceImpl. и HobbyServiceImpl классы соответственно.

Листинг 12 — Класс ContactServiceImpl

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
@Service("contactService")
@Repository
@Transactional
public class ContactServiceImpl implements ContactService {
 
 final static Logger logger = LoggerFactory.getLogger(ContactServiceImpl.class);
  
 @Autowired
 private ContactRepository contactRepository;
  
 @Transactional(readOnly=true)
 public List<Contact> findAll() {
  logger.info("Finding all contacts");
  return Lists.newArrayList(contactRepository.findAll());
 }
 
 @Transactional(readOnly=true)
 public Contact findById(Long id) {
  return contactRepository.findOne(id);
 }
 
 public Contact save(Contact contact) {
  return contactRepository.save(contact);
 }
 
}

Листинг 13 — Класс HobbyServiceImpl

01
02
03
04
05
06
07
08
09
10
11
12
13
14
@Service("hobbyService")
@Repository
@Transactional
public class HobbyServiceImpl implements HobbyService {
 
 @Autowired
 private HobbyRepository hobbyRepository;
 
 @Transactional(readOnly=true)
 public List<Hobby> findAll() {
  return Lists.newArrayList(hobbyRepository.findAll());
 }
 
}

тестирование

Давайте посмотрим на приложение в действии. В листинге 14 показан класс ContactServiceTest, который просто загружает контекст приложения Spring из файла app-context.xml, ищет bean-компонент contactService и вызывает операцию findAll для извлечения всех контактов из базы данных.

Листинг 14 — Класс ContactServiceTest

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
public class ContactServiceTest {
 
 public static void main(String[] args) {
 
  GenericXmlApplicationContext ctx = new GenericXmlApplicationContext();
  ctx.load("classpath:app-context.xml");
  ctx.refresh();
   
  ContactService contactService = ctx.getBean("contactService", ContactService.class);
   
  List<Contact> contacts = contactService.findAll();
   
  for (Contact contact: contacts) {
   System.out.println(contact);
  
 
 }
 
}
Выполнение указанного выше класса приведет к следующему выводу в окне вывода консоли (другие не относящиеся к выводу опущены):

2012-05-25 13:35:43,552 INFO [com.skywidesoft.tomcat.dbcp.tutorial.service.jpa.ContactServiceImpl] -
<Finding all contacts>
2012-05-25 13:35:43,665 DEBUG [org.hibernate.SQL] - <select contact0_.ID as ID0_,
contact0_.BIRTH_DATE as BIRTH2_0_, contact0_.FIRST_NAME as FIRST3_0_,
contact0_.LAST_NAME as LAST4_0_, contact0_.VERSION as VERSION0_ from contact contact0_>
Hibernate: select contact0_.ID as ID0_, contact0_.BIRTH_DATE as BIRTH2_0_,
contact0_.FIRST_NAME as FIRST3_0_, contact0_.LAST_NAME as LAST4_0_, contact0_.VERSION as
VERSION0_ from contact contact0_
Contact - Id: 1, First name: Clarence, Last name: Ho, Birthday: 1980-07-30
Contact - Id: 2, First name: Scott, Last name: Tiger, Birthday: 1990-11-02

Из приведенного выше вывода видно, что контактная информация, которая была заполнена сценарием test-data.sql, была правильно получена из базы данных.

Вывод

В этом руководстве описывается использование пула соединений Tomcat JDBC в автономных приложениях Java. Пул соединений Tomcat JDBC является заменой пула соединений commons-dbcp, предоставляя более быстрое и многофункциональное решение для пула соединений JDBC. Его аккуратный дизайн, высокая производительность, поддержка среды с высокой степенью параллелизма и многоядерные / процессорные системы делают его убедительным выбором в качестве поставщика пула соединений JDBC как в веб-контейнере Tomcat, так и в автономных средах приложений Java.

Загрузите полный проект Eclipse Maven .

Справка: Использование пула соединений Tomcat JDBC в автономном приложении Java от нашего партнера по W4G Кларенса Хо .

Pro Spring 3 от APress. С Pro Spring 3 вы изучите основы и основные темы Spring , а также получите доступ к взглядам авторов и реальному опыту работы с remoting, Hibernate и EJB . Помимо основ, вы узнаете, как использовать Spring Framework для создания различных уровней или частей корпоративного Java-приложения, таких как транзакции, уровни веб-сайтов и презентаций, развертывание и многое другое. Полный пример приложения позволяет вам применить многие технологии и методы, описанные в этой книге, и посмотреть, как они работают вместе.

APress предоставил читателям Java Code Geeks код купона на скидку. Код купона: SPR76 и действует до 6 июля 2012 года . Код предлагает скидку 40% на электронную книгу только с сайта apress.com .