Talk about how to use JdbcTemplate to read and write data in Spring Boot

First introduce dependencies in pom.xml.

<!--jdbc-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>

<!--h2-->
<dependency>
    <groupId>com.h2database</groupId>
    <artifactId>h2</artifactId>
    <scope>runtime</scope>
</dependency>

The sample program intends to use the h2 cache database, so it is also quoted here.

1 h2 cache database

h2 is an open source embedded (non-embedded device) database engine, developed based on Java, can be directly embedded in the application, packaged and released together with the application, and is not restricted by the platform.

After launching the application, enter http://127.0.0.1:8080/h2-console in the browser address bar to open the h2 console.

First select the console encoding format as Chinese, then enter the JDBC URL, and then click "Test Connection". If the connection is successful, it will prompt "Test Success".

Finally, click the "Connect" button to open the database console client and connect to the h2 database:

2 Initialize table structure and data

Under src/main/resources/, create a new schema.sql file to write table structure SQL. In the same directory, create a new data.sql file and write initial data SQL. So when the application starts, Spring Boot will execute these scripts.

schema.sql:

create table if not exists Book
( id varchar( 4) not null, name varchar( 25) not null, type varchar( 10) not null );

data.sql:

insert into Book
  (id, name, type)
values
  ('1', '两京十五日', '小说');
insert into Book
  (id, name, type)
values
  ('2', '把自己作为方法', '历史');
insert into Book
  (id, name, type)
values
  ('3', '正常人', '小说');

After the startup is successful, you will see the newly created tables and data in the h2 database console client.

Click Book on the left, the SQL statement to query the table will be automatically generated in the SQL input box on the right, and then click "Run" to execute it. We will see the initialized table data in the lower right corner.

3 coding

3.1 New entity class

@Data
@RequiredArgsConstructor
public class Book {

    private final String id;
    private final String name;
    private final String type;
}

The Lombok plugin is used here. Lombok is a Java utility that can be used to help us eliminate Java's verbose boilerplate code.

Java classes annotated with @Data will automatically add these methods for us after compilation:

  1. Get and set methods for all attributes;
  2. toString method;
  3. hashCode method;
  4. equals method.

The @RequiredArgsConstructor annotation will take all the uninitialized fields annotated with @NonNull and final in the class as input parameters of the constructor.

3.2 New Repository class

First define a Repository interface, and then create a new implementation class for this interface.

interface:

public interface BookRepository {

    Iterable<Book> findAll();

    Book findOne(String id);

    Book save(Book Book);
}

Implementation class:

@Repository
public class JdbcBookRepository implements BookRepository {

    private JdbcTemplate jdbc;

    @Autowired
    public JdbcBookRepository(JdbcTemplate jdbc) {
        this.jdbc = jdbc;
    }

    @Override
    public Iterable<Book> findAll() {
        return null;
    }

    @Override
    public Book findOne(String id) {
        return null;
    }

    @Override
    public Book save(Book Book) {
        return null;
    }
}

@Repository is similar to @Controller, @Service, and @Component. The purpose is to hand over objects to spring for management. @Repository is generally used in the implementation class of the persistence layer.

@Autowired annotation can inject required external resources into specified fields or methods in the form of byType.

Autowire has the following four modes:

mode Description
byName Automatic assembly based on the name of the attribute
byType Automatic assembly according to the type of attribute
constructor Similar to byType, the difference is that it is applied to the constructor parameter, and an exception will be thrown if not found
autodetect Will intelligently choose between byType and constructor

JdbcTemplate is injected here through the constructor annotated by @Autowired. This constructor assigns JdbcTemplate to an instance variable, which will be used by other methods to perform database query or update operations.

3.3 Query operation

Suppose we need to check out all the books, then you can call the JdbcTemplate List<T> query(String sql, RowMapper<T> rowMapper)method.

@Override
public Iterable<Book> findAll() {
    return jdbc.query("select id, name, type from Book",
            this::mapRowToBook);
}

private Book mapRowToBook(ResultSet rs, int rowNum) throws SQLException {
    return new Book(rs.getString("id"), rs.getString("name"),
            rs.getString("type"));
}

Here we use Java method references to write RowMapper input parameters. The advantage of this is: Compared with the original anonymous inner class, the method reference is more concise.

3.4 update()

The update() method of JdbcTemplate can be used to add or update data.

@Override
public Book save(Book book) {
    jdbc.update("insert into Book (id,name,type) values (?,?,?)",
            book.getId(),
            book.getName(),
            book.getType()
    );
    return book;
}

The update() method is defined as follows:

int update(String sql, @Nullable Object... args)

It accepts a SQL statement containing placeholders and multiple input parameters. Returns the number of records actually affected.

Create a Spring unit test class to verify the newly created save() method:

@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest
public class JdbcBookRepositoryTest {

    @Autowired
    private JdbcBookRepository jdbcBookRepository;

    @Test
    public void save() {

        Book book = new Book("4", "比利时的哀愁", "小说");
        jdbcBookRepository.save(book);
        Book find=jdbcBookRepository.findOne("4");
        assertEquals("比利时的哀愁",find.getName());
    }
}

3.5 SimpleJdbcInsert wrapper class

SimpleJdbcInsert is generally used in multi-table insert scenarios. SimpleJdbcInsert has two methods to perform data insertion operations: execute() and executeAndReturnKey(). They are accepted Map<String ,Object>as parameters, wherein the key corresponding to the column name in the data table, the value corresponding to the actual values to be inserted into the column.

Let’s take a book example. A book can contain multiple tags; and a tag can also belong to multiple books. There is a many-to-many relationship between them, so a mapping table between books and tags is established to store these relationships specifically. The details are shown in the figure below:

First, add these table structure creation statements in schema.sql:

create table if not exists Book
( id identity, name varchar( 25) not null, type varchar( 10) not null );


create table if not exists Tag
( id identity, name varchar( 25) not null);


create table if not exists Book_Tags ( book bigint not null, tag bigint not null );

alter table Book_Tags add foreign key (book) references Book( id);
alter table Book_Tags add foreign key (tag) references Tag( id);

Next, create these entity classes:

@Data
@RequiredArgsConstructor
public class Tag {

    private final Long id;
    private final String name;
}

@Data
@RequiredArgsConstructor
public class Book {

    private final String id;
    private final String name;
    private final String type;

    private List<Tag> tags = new ArrayList<>();
}

Then in the constructor of the Repository implementation class, initialize the SimpleJdbcInsert instance of each table:

@Repository
public class JdbcBookRepository implements BookRepository {

    private static final Logger log = LogManager.getFormatterLogger();

    private final SimpleJdbcInsert bookInserter;
    private final SimpleJdbcInsert tagInserter;
    private final SimpleJdbcInsert bookTagsInserter;
    private final ObjectMapper objectMapper;
    private JdbcTemplate jdbc;

    @Autowired
    public JdbcBookRepository(JdbcTemplate jdbc) {
        this.jdbc = jdbc;
        this.bookInserter = new SimpleJdbcInsert(jdbc)
                .withTableName("Book")
                .usingGeneratedKeyColumns("id");

        this.tagInserter = new SimpleJdbcInsert(jdbc)
                .withTableName("Tag")
                .usingGeneratedKeyColumns("id");

        this.bookTagsInserter = new SimpleJdbcInsert(jdbc)
                .withTableName("Book_Tags");

        this.objectMapper = new ObjectMapper();
    }
    ...
}

The withTableName() method of SimpleJdbcInsert is used to specify the table name; and the usingGeneratedKeyColumns() method is used to specify the primary key.

The specific save operation code is:

public Book saveIncludeTags(Book book) {
    //保存图书
    Map<String, Object> values = objectMapper.convertValue(book, Map.class);
    long bookId = bookInserter.executeAndReturnKey(values).longValue();

    //保存标签
    List<Long> tagIds = new ArrayList<>();
    for (Tag tag : book.getTags()) {
        values = objectMapper.convertValue(tag, Map.class);
        long tagId = tagInserter.executeAndReturnKey(values).longValue();
        tagIds.add(tagId);
    }

    //关联图书与标签
    for (Long tagId : tagIds) {
        values.clear();
        values.put("book", bookId);
        values.put("tag", tagId);
        log.info("values -> %s", values);
        bookTagsInserter.execute(values);
    }

    return book;
}
  • The SimpleJdbcInsert executeAndReturnKey () and execute () method supports Map<String, ?>the form of the reference. The difference between them is that executeAndReturnKey() will return the primary key value in the form of Number.
  • Jackson may be utilized in ObjectMapper.convertValue(Object fromValue, Class<T> toValueType)a method to convert a POJO Map object corresponding value.
  • The Number type can be converted according to the scene.

This code first saves the book and gets the book's primary key; then saves the tag to get the tag's primary key; finally, saves the book's primary key and the tag's primary key obtained earlier in the relationship table between them.

Guess you like

Origin blog.csdn.net/deniro_li/article/details/108807674