1.前言
有人说,程序员多掌握一种数据库,就多一门手艺。其实在我看来,多学一种数据库可以让知识更好的碰撞,你将看到一番不一样的视野。今天就开启我们的PostgreSQL之旅。
2.pom文件中的核心依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.hsqldb</groupId>
<artifactId>hsqldb</artifactId>
<scope>test</scope>
</dependency>
简单说明:(1)上面的依赖中的lombok是我最近常使用的jar,它真得帮我们省下很多getter和setter代码,多用几次,你也会和我一样喜欢上它的;(2)还有就是hsqldb是一个内嵌的内存数据库,用来测试再好不过了。
3.配置文件
(1) application.yaml
(2) application-dev.yaml
spring:
profiles:
active: dev
spring:
application:
name: postgresql-demo
datasource:
url: jdbc:postgresql://localhost:5432/test
username: postgres
password: 123456
driver-class-name: org.postgresql.Driver
jpa:
properties:
hibernate:
temp:
use_jdbc_metadata_defaults: false
dialect: org.hibernate.dialect.PostgreSQLDialect
hbm2ddl:
auto: update
(3) application-test.yaml
spring:
application:
name: postgresql-demo
jpa:
database: hsql
4.项目目录结构

5.几个核心类 java代码
(1) GeneralResource.java
@MappedSuperclass
@NoArgsConstructor
public abstract class GeneralResource {
@Id
@GenericGenerator(
name = "uuid",
strategy = "uuid2"
)
@GeneratedValue(
generator = "uuid"
)
@Column(
length = 100
)
@Getter
@Setter
private String id;
}
(2) Resource.java
@MappedSuperclass
@NoArgsConstructor
public abstract class Resource extends GeneralResource{
@Column(
length = 60,
nullable = false
)
@Getter
@Setter
private String creatorId;
@Getter
@Setter
private Long creationTime;
}
(3) User.java
@Entity
@Table(name = "t_user")
@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
@NoArgsConstructor
@AllArgsConstructor
public class User extends Resource {
@Getter
@Setter
private String name;
}
(4) UserDTOHelper.java
@Component
public class UserDTOHelper {
public UserDTO toDto(User user) {
return new UserDTO()
.id(user.getId())
.name(user.getName())
.creatorId(user.getCreatorId());
}
public List<UserDTO> toDtos(List<User> users) {
if (CollectionUtils.isEmpty(users)) {
return Collections.emptyList();
}
return users
.parallelStream()
.map(this::toDto)
.collect(toList());
}
public User toModel(@NotNull UserDTO dto) {
User user = new User();
user.setId(dto.getId());
user.setName(dto.getName());
user.setCreatorId(dto.getCreatorId());
return user;
}
}
(5) UserRepository.java
import org.springframework.data.jpa.repository.JpaRepository;
public interface UserRepository extends JpaRepository<User, String> {
}
(6) UserService.java
public interface UserService {
User createUser(@NotNull User user);
Optional<User> findById(String id);
}
(6) UserServiceImpl.java
@Service
public class UserServiceImpl implements UserService {
private final UserRepository repository;
@Autowired
public UserServiceImpl(UserRepository repository) {
this.repository = repository;
}
@Override
public User createUser(@NotNull User user) {
return repository.save(user);
}
@Override
public Optional<User> findById(String id) {
return repository.findById(id);
}
}
(7) UserDTO.java
public class UserDTO implements Serializable {
@Getter
@Setter
private String id;
@Getter
@Setter
private String name;
@Getter
@Setter
private String creatorId;
public UserDTO id(String id) {
this.id = id;
return this;
}
public UserDTO name(String name) {
this.name = name;
return this;
}
public UserDTO creatorId(String creatorId) {
this.creatorId = creatorId;
return this;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
UserDTO that = (UserDTO) o;
return Objects.equals(this.creatorId, that.creatorId) &&
Objects.equals(this.id, that.id) &&
Objects.equals(this.name, that.name);
}
@Override
public int hashCode() {
return Objects.hash(creatorId, id, name);
}
@Override
public String toString() {
return new StringJoiner(", ", this.getClass().getSimpleName() + "[", "]")
.add("creatorId = " + creatorId)
.add("id = " + id)
.add("name = " + name)
.toString();
}
}
(8) UserController.java
@RestController
public class UserController {
private final UserService service;
@Autowired
public UserController(UserService service) {
this.service = service;
}
public ResponseEntity<User> queryById(String id){
return new ResponseEntity<>(service.findById(id).orElse(new User("Unknown")), HttpStatus.OK);
}
}
(9) UserServiceTest.java
@RunWith(SpringRunner.class)
@SpringBootTest
@ActiveProfiles("test")
public class UserServiceTest {
@Autowired
private UserService userService;
@Autowired
private UserDTOHelper helper;
@Test
public void testSave() {
UserDTO dto = buildDto();
User user = helper.toModel(dto);
User savedUser = userService.createUser(user);
Optional<User> optionalUser = userService.findById(savedUser.getId());
assertTrue(optionalUser.isPresent());
assertEquals(dto.getName(), optionalUser.get().getName());
}
private UserDTO buildDto() {
return new UserDTO()
.name("Test")
.creatorId(UUID.randomUUID().toString());
}
}
6.后记
(1)做一个入门其实不是很难,但是从头开始学习一个自己从未接触到的框架也好,数据库也好,或者其他的技术也好,都不是一件容易的事。但是错误总是接踵而来很容易打击到一颗脆弱的心灵,还好再坚持一下就好了。
比如上面的代码例子其实不是很难,那是站在代码的角度,但是其中还是有不少的坑的。我下面我将演示上面代码中如果少了点什么,或者写错点什么的效果截图。

看到日志中的错误就是:
Caused by: java.sql.SQLFeatureNotSupportedException: 这个 org.postgresql.jdbc.PgConnection.createClob() 方法尚未被实作 。
尽管测试是通过了,但是有强迫症的你会没有疑问吗?为什么会出现这样的问题?!答案是我注释掉了一行配置代码

(2)还有就是一个很小的细节问题,现在Java开发主流是使用注解,但是有时我们却忘了加上去,比如我昨天就忘了在User上面加上@Table注解,当然和数据库打交道的时候,Java中的映射可能会和数据中的关键字有冲突,可以推荐表名使用一个前缀,比如t_。还有就是在使用Hibernate中的继承机制的时候,如果我们知道表的结构就容易理解这个注解@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
(3)最后还有两个小点,一个就是设计一些基础的Base类,你的代码结构将更加层次化,而且这些思想的产物是可以复用的。还有一个就是Java8汇总引入的Stream Api让你的代码更加流畅,简洁,你值得拥有。