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让你的代码更加流畅,简洁,你值得拥有。