remagine
혼자서 웹 서비스 만들어보기 - 7 본문
혼자서 웹 서비스 만들어보기 - 7
https://github.com/remagine/webNovel/tree/develop
1. 태그 검색 만들기
게시물에는 Tag라는 컬럼이 있습니다. 해당 컬럼은 현재 String(Varchar) 타입 컬럼입니다.
보통 Tag로 검색을 할 때 어떻게 구현할지 생각해 봤습니다.
apink라는 단어로 검색을 한다고 해보겠습니다.
만일 Tag가 String 컬럼이라면
"apink, sistar, twice, exo" 이런 식으로 저장이 되어있을 것 입니다.
검색을 구현한다면
select * from story where tag ilike "%apink%"
apink를 검색하기 위해선 like 검색에 %를 앞뒤로 붙여줘야 합니다.
이렇게 검색하면 일단 index는 태울 수 없기에 사이트가 커지면 속도가 엄청 느리게 될 것 입니다.
현재 Postgresql은 Array 컬럼을 지원하고 있습니다.
그리고 Gin index라는 index와
range 검색어인 @> 연산자를 지원합니다.
참조 :: https://www.postgresql.org/docs/9.3/static/functions-range.html
이렇게 되면
"apink, sistar, twice, exo" 는
[apink,sister,twice,exo] 형태로 저장이 됩니다.
index를 태울 수 있는 장점이 있지만
apink를 검색하려면 apink로 검색해야지 apin을 like검색으로 할 수는 없습니다.
현재 String(Varchar)타입인 Tag컬럼을 String[]로 변경해 보려고 합니다.
기존 tag 컬럼의 type을 변경하는 것은 불가능함으로
tags라는 컬럼을 character varying[] length 300으로 잡고 생성합니다.
2. Entity 변경
tag 컬럼을 tags컬럼으로 바꿔줘야 겠네요.
일반적인 JPA는 array형식의 컬럼을 받지 않습니다.
import java.io.Serializable; import java.sql.Array; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Types; import org.hibernate.HibernateException; import org.hibernate.engine.spi.SessionImplementor; import org.hibernate.usertype.UserType; public class StringArrayType implements UserType { private final int[] arrayTypes = new int[] { Types.ARRAY }; @Override public int[] sqlTypes() { return arrayTypes; } @Override public Class<String[]> returnedClass() { return String[].class; } @Override public boolean equals(Object x, Object y) throws HibernateException { return x == null ? y == null : x.equals(y); } @Override public int hashCode(Object x) throws HibernateException { return x == null ? 0 : x.hashCode(); } @Override public Object nullSafeGet(ResultSet rs, String[] names, SessionImplementor session, Object owner) throws HibernateException, SQLException { // get the first column names if (names != null && names.length > 0 && rs != null && rs.getArray(names[0]) != null) { String[] results = (String[]) rs.getArray(names[0]).getArray(); return results; } return null; } @Override public void nullSafeSet(PreparedStatement st, Object value, int index, SessionImplementor session) throws HibernateException, SQLException { // setting the column with string array if (value != null && st != null) { String[] castObject = (String[]) value; Array array = session.connection().createArrayOf("text", castObject); st.setArray(index, array); } else { st.setNull(index, arrayTypes[0]); } } @Override public Object deepCopy(Object value) throws HibernateException { return value == null ? null : ((String[]) value).clone(); } @Override public boolean isMutable() { return false; } @Override public Serializable disassemble(Object value) throws HibernateException { return (Serializable) value; } @Override public Object assemble(Serializable cached, Object owner) throws HibernateException { return cached; } @Override public Object replace(Object original, Object target, Object owner) throws HibernateException { return original; } } | cs |
위와 같은 custom Type클래스를 지정해 줘야 JPA는 컬럼을 인식하고 값을 던져줄 수 있습니다.
현재 lib중에 위와같은 클래스를 구현한 것이 있습니다.
@Entity @Table(name = "story") public class Story { @Id @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "seq_story") @SequenceGenerator(name = "seq_story", sequenceName = "story_id_seq", allocationSize = 1) private Integer id; @Column(name = "title") private String title; @Column(name = "description") private String description; @Column(name = "foreword") private String foreword; @Column(name = "character") private String character; @Column(name = "tags") @Type(type = "com.iropke.common.hibernate.ArrayType") private String[] tags; @Column(name = "co_author") private String coAuthor; @Column(name = "cover_image") private String coverImage; @Column(name = "views", columnDefinition = "Integer default 0") private Integer views; @Enumerated(EnumType.STRING) @Column(name = "state") private State state; @Temporal(TemporalType.TIMESTAMP) @Column(name = "create_at", insertable = false, updatable = false) private Date createAt; @ManyToOne @JoinColumn(name="member") private Member member; @OneToMany @Where(clause = "state not in ('deleted')") @OrderBy(clause = "id asc") @JoinColumn(name = "story") private List<Chapter> chapterList; public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } public String getDescription() { return description; } public void setDescription(String description) { this.description = description; } public String getForeword() { return foreword; } public void setForeword(String foreword) { this.foreword = foreword; } public String getCharacter() { return character; } public void setCharacter(String character) { this.character = character; } public String[] getTags() { return tags; } public void setTags(String[] tags) { this.tags = tags; } public String getCoAuthor() { return coAuthor; } public void setCoAuthor(String coAuthor) { this.coAuthor = coAuthor; } public String getCoverImage() { return coverImage; } public void setCoverImage(String coverImage) { this.coverImage = coverImage; } public Integer getViews() { return views; } public void setViews(Integer views) { this.views = views; } public State getState() { return state; } public void setState(State state) { this.state = state; } public Date getCreateAt() { return createAt; } public void setCreateAt(Date createAt) { this.createAt = createAt; } public Member getMember() { return member; } public void setMember(Member member) { this.member = member; } public List<Chapter> getChapterList() { return chapterList; } public void setChapterList(List<Chapter> chapterList) { this.chapterList = chapterList; } } | cs |
@Type(type = "com.iropke.common.hibernate.ArrayType")을 주목하시고 추가해 주시면 됩니다.
3. 검색 구현하기
SearchController를 구현해 보도록 하겠습니다.
'개인프로젝트 - 웹소설 사이트' 카테고리의 다른 글
혼자서 웹 서비스 만들어보기 - 6 (0) | 2017.06.28 |
---|---|
혼자서 웹 서비스 만들어보기 - 5 (0) | 2017.06.26 |
혼자서 웹 서비스 만들어보기 - 4 (0) | 2017.06.23 |
혼자서 웹 서비스 만들어보기 - 3 (0) | 2017.06.15 |
혼자서 웹 서비스 만들어보기 - 2 (0) | 2017.06.13 |