JPA annotation중에 PrimaryKey를 나타내는 @Id가 있다.

보통 getter쪽에 @Id라는 Annotaton을 매핑하면 그에 해당하는 맴버가 PrimaryKey가 된다.

 

하지만 복수의 컬럼이 하나의 Key로 나타내고자 할때는 @Id를 복수개 만큼 설정할수있을까?

내가 알고있는 만큼의 @Id의 사용은 Persistent 에서 유일하게 1개 만을 지정할 수 있다고 알고있다.

방법이 없을까 문서를 찾아보다가 JSR - 220 Enterprise JavaBeansTM 3.0

를 찾아보았다.

 

 

1. 복수의 키 실장을 위한 Annotation

 

@Embedded 와 @EmbeddedId

 

@EmbeddedId 의 설정 요건.

1. 일반 POJO다.

2. public none-arguments Constructor가 있어야 한다.

3. implements serializable

4. equals와 hashCode를 @Override해야한다.

 

@EmbeddedId 의 설정을 위해 위의 4가지 규칙을 준수해야만한다.

 

다음과 같은 Persistent POJO가 있다고 가정하면..

 

 

@Entity

@Table(name="auth")

public class Auth implements Serializable {

       @EmbeddedId

       private Auth.PK pk;

 

       @Column(name="au_auth")

       private Object auAuth;

 

       private static final long serialVersionUID = 1L;

..............................................>

 

 

복합키에 대한 값이 내부 Class로 정의 되어 있다.

 

Auth Inner Class

@Embeddable

       public static class PK implements Serializable {

             private String auMenu;

             private String mbId;

             private static final long serialVersionUID = 1L;

 

             public PK() {

                    super();

             }

 

             public String getAuMenu() {

                    return this.auMenu;

             }

 

             public void setAuMenu(String auMenu) {

                    this.auMenu = auMenu;

             }

 

             public String getMbId() {

                    return this.mbId;

             }

 

             public void setMbId(String mbId) {

                    this.mbId = mbId;

             }

 

             @Override

             public boolean equals(Object o) {

                    if (o == this) {

                           return true;

                    }

                    if ( ! (o instanceof PK)) {

                           return false;

                    }

                    PK other = (PK) o;

                    return this.auMenu.equals(other.auMenu)

                           && this.mbId.equals(other.mbId);

             }

 

             @Override

             public int hashCode() {

                    return this.auMenu.hashCode()

                           ^ this.mbId.hashCode();

             }

 

       }

 

equals와 HashCode를 override한다.

이것으로 복합키에 대한 Annotation 설정은 대략 정리 된다.

 

hashCode를 override하는 이유는 Object의 hashCode가 아닌 Serializable된 객체의 HashCode를 바라봄직하다.

 

기존에 @Id를 @Embeddable의 Type으로 본 POJO에 @EmbeddedId로써 등록을 했다.


출처 : http://blog.naver.com/paradozz?Redirect=Log&logNo=33908099







Tutorial JPA Composite Primary-Key

THERE ARE TWO NEW VERSIONS OF THIS POST.
SIMPLES COMPOSITE KEY: http://uaihebert.com/?p=1674&page=8
COMPLEX COMPOSITE KEY: http://uaihebert.com/?p=1674&page=9

Good afternoon.

Let us talk today about Composite Primary Key (Primary-Key) in a class?

What if only the ID it is not enough to define a unique record of your class? How can we make the JPA understand this situation of two fields as primary-key?

To help us to understand about Composite Key I will use a class named Car; this class has a car chassis serial number as primary key. Imagine that a new software requirement just arrive saying that, we need the Car primary key to be composed by the chassis serial number and with the engine serial number.

We will keep our JPA study at the same point that we have stopped in the last post (JPA SequenceGenerator). If you need any help to compile the code of this post, you can check the others posts about JPA that shows how to set up the environment: JPA TableGenerator – Simple Primay KeyAuto Create Schema Script with: Ant, Hibernate 3 e JPA 2Tutorial Hibernate 3 with JPA 2.

We need to create a class to be our composite primary-key.
Let us see how the CarPK code will be:

1
2
3
4
5
6
7
8
9
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
package com;
 
import java.io.Serializable;
 
public class CarPK implements Serializable {
 
    private String chassisSerialNumber;
    private String engineSerialNumber;
 
    public CarPK(){
        // Your class must have a no-arq constructor
    }
 
    @Override
    public boolean equals(Object obj) {
        if(obj instanceof CarPK){
            CarPK carPk = (CarPK) obj;
 
            if(!carPk.getChassisSerialNumber().equals(chassisSerialNumber)){
                return false;
            }
 
            if(!carPk.getEngineSerialNumber().equals(engineSerialNumber)){
                return false;
            }
 
            return true;
        }
 
        return false;
    }
 
    @Override
    public int hashCode() {
        return chassisSerialNumber.hashCode() + engineSerialNumber.hashCode();
    }
 
    public String getChassisSerialNumber() {
        return chassisSerialNumber;
    }
 
    public void setChassisSerialNumber(String chassisSerialNumber) {
        this.chassisSerialNumber = chassisSerialNumber;
    }
 
    public String getEngineSerialNumber() {
        return engineSerialNumber;
    }
 
    public void setEngineSerialNumber(String engineSerialNumber) {
        this.engineSerialNumber = engineSerialNumber;
    }
}

There are some rules that your PK class should follow:

  • It must have a default constructor without arguments.
  • It must implement the java.io.Serializable interface.
  • It must override the methods equals and hashCode.

Let us create now the Car class:

1
2
3
4
5
6
7
8
9
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
package com;
 
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.IdClass;
import javax.persistence.Table;
 
@Entity
@Table(name="CAR")
@IdClass(value=CarPK.class)
public class Car {
 
    @Id
    private String chassisSerialNumber;
    @Id
    private String engineSerialNumber;
 
    @Column
    private String name; // Yes, some people like to give name to theirs cars.
 
    public String getChassisSerialNumber() {
        return chassisSerialNumber;
    }
 
    public void setChassisSerialNumber(String chassisSerialNumber) {
        this.chassisSerialNumber = chassisSerialNumber;
    }
 
    public String getEngineSerialNumber() {
        return engineSerialNumber;
    }
 
    public void setEngineSerialNumber(String engineSerialNumber) {
        this.engineSerialNumber = engineSerialNumber;
    }
 
    public String getName() {
        return name;
    }
 
    public void setName(String name) {
        this.name = name;
    }
}

Notice that in our Car class, we just added the @Id annotation without the need of any other annotation type. PS.: At the “name” attribute you will find the @Column annotation but this annotation is not necessary in our case.

Let us see our Main class code that will insert a record into the database:

1
2
3
4
5
6
7
8
9
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
package com;
 
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;
 
public class Main {
 
    public static void main(String[] args) {
        EntityManagerFactory emf = Persistence.createEntityManagerFactory("Hello");
        EntityManager em = emf.createEntityManager();
 
        try {
            em.getTransaction().begin();
 
            Car car = new Car();
 
            car.setChassisSerialNumber("9BW DA05X6 1 T050136");
            car.setEngineSerialNumber("ABC123");
            car.setName("Thunder");
 
            em.persist(car);
 
            em.getTransaction().commit();
        }
        catch (Exception e) {
            em.getTransaction().rollback();
            e.printStackTrace();
        }
        finally{
            emf.close();
        }
 
        System.out.println("It is over");
    }
}

Run the Main class and check the result in the console.

Console Image.
Database Image.

How can we find our recorded entity that uses composite primary-key? Let us edit our Main class and see the result:

1
2
3
4
5
6
7
8
9
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
package com;
 
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;
 
public class Main {
 
    public static void main(String[] args) {
        EntityManagerFactory emf = Persistence.createEntityManagerFactory("Hello");
        EntityManager em = emf.createEntityManager();
 
        try {
            em.getTransaction().begin();
 
            CarPK carPK = new CarPK();
 
            carPK.setChassisSerialNumber("9BW DA05X6 1 T050136");
            carPK.setEngineSerialNumber("ABC123");
 
            Car car = em.find(Car.class, carPK);
 
            System.out.println(car.getName());
 
            em.getTransaction().commit();
        }
        catch (Exception e) {
            em.getTransaction().rollback();
            e.printStackTrace();
        }
        finally{
            emf.close();
        }
 
        System.out.println("It is over");
    }
}

Run the Main class again.

Console Image.

To find an entity that uses its ID as composite primary-key we need to create an instance of the PK class and use it as parameter in the “find” method.

There is another way to map this primary-key attribute. Notice that we have the same ID fields in both classes: “Car and CarPK“. It both has the chassisSerialNumber and the engineSerialNumber fields. With a little lines added/removed from our code we can refactor it to remove this duplicated fields.

1
2
3
4
5
6
7
8
9
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
package com;
 
import java.io.Serializable;
 
import javax.persistence.Column;
import javax.persistence.Embeddable;
 
@Embeddable
public class CarPK implements Serializable {
 
    @Column
    private String chassisSerialNumber;
 
    @Column
    private String engineSerialNumber;
 
    public CarPK(){
        // Your class must have a no-arq constructor
    }
 
    @Override
    public boolean equals(Object obj) {
        if(!(obj instanceof CarPK)){
            CarPK carPk = (CarPK) obj;
 
            if(!carPk.getChassisSerialNumber().equals(chassisSerialNumber)){
                return false;
            }
 
            if(!carPk.getEngineSerialNumber().equals(engineSerialNumber)){
                return false;
            }
 
            return true;
        }
 
        return false;
    }
 
    @Override
    public int hashCode() {
        return chassisSerialNumber.hashCode() + engineSerialNumber.hashCode();
    }
 
    public String getChassisSerialNumber() {
        return chassisSerialNumber;
    }
 
    public void setChassisSerialNumber(String chassisSerialNumber) {
        this.chassisSerialNumber = chassisSerialNumber;
    }
 
    public String getEngineSerialNumber() {
        return engineSerialNumber;
    }
 
    public void setEngineSerialNumber(String engineSerialNumber) {
        this.engineSerialNumber = engineSerialNumber;
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
package com;
 
import javax.persistence.Column;
import javax.persistence.EmbeddedId;
import javax.persistence.Entity;
import javax.persistence.Table;
 
@Entity
@Table(name = "CAR")
public class Car {
 
    @EmbeddedId
    private CarPK carPK;
 
    @Column
    private String name; // Yes, some people like to give name to theirs cars.
 
    public String getName() {
        return name;
    }
 
    public void setName(String name) {
        this.name = name;
    }
 
    public CarPK getCarPK() {
        return carPK;
    }
 
    public void setCarPK(CarPK carPK) {
        this.carPK = carPK;
    }
}

We can see that just a few changes were done in the classes:

  • In the CarPK class there is a new annotation: the @Column. The car ID attributes will be mapped only inside the CarPK. There is a new annotation named @Embeddeable allowing our class to be part of another class.
  • We removed the Id attributes from the Car class. We also removed the @IdClass annotation. We added an attribute of the CarPK with the @EmbeddedId, this annotation allow the Car to use the CarPK fields as its primary-key.

If you wish, run the main class again and you will see that our class is returned by the find method without errors.

WarningWhen your class implements the composite primary-key it will not be able to use the sequence id generation. You will have to generate the ID by your software code.

Do you have any doubts or suggestions? Just post it.

See you later.


출처 - http://uaihebert.com/?p=42





I have a problem with JPA mapping for a particular relation. In my MySQL database I have 3 entity in relationship:

USER
USER_SERIAL_NUMBER VARCHAR(20) not null,
..... VARCHAR(4) not null,
..... VARCHAR(3) not null,
..... VARCHAR(10) not null,
primary key (USER_SERIAL_NUMBER)
);
OFFICE (
OFFICE_SERIAL_NUMBER VARCHAR(20) not null,
TOWN VARCHAR(15) not null,
YEAR VARCHAR(4) not null,
DESCRIPTION VARCHAR(200) not null,
NOTE VARCHAR(100) not null,
CREATION_DATE DATE not null,
LAST_UPDATE_DATE DATE not null,
primary key (OFFICE_SERIAL_NUMBER)
);

ROLE_TYPE (
ROLE_ID BIGINT not null,
DESCRIPTION VARCHAR(20) not null,
CREATION_DATE DATE not null,
LAST_UPDATE_DATE DATE not null,
primary key (ROLE_ID)
);

and the relation table

ROLE (
ROLE_ID BIGINT not null,
USER_SERIAL_NUMBER VARCHAR(20) not null,
OFFICE_SERIAL_NUMBER VARCHAR(20) not null,
YEAR VARCHAR(4) not null,
CREATION_DATE DATE not null,
LAST_UPDATE_DATE DATE not null,
primary key (ROLE_ID, USER_SERIAL_NUMBER, OFFICE_SERIAL_NUMBER, YEAR)
);

In the relation table I have a composite key with the 3 foreign key and a local key (the year), because a user can have more (different) role on particular office in a specified year but probably will change the next year (I have to mantain the historic role).

I have made many attempts to map these tables in entities but I didn't yet found the right mix so that everything works.

I would like to navigate the relationship in both direction, so I need to know who and how many are users who have a particular role in a particular office, but I also need to know in how many offices work a particular user, and what role he has.

Here is the Java code:

@Entity
@Scope(ScopeType.CONVERSATION)
@Name("role")
@Table(name = "ROLE")
@IdClass(RolePK.class)
public class Role implements Serializable {

private static final long serialVersionUID = 111111111L;

@Id
@OneToOne
@Column(name="USER_SERIAL_NUMBER")
private User User;

@Id
@OneToOne
@Column(name="OFFICE_SERIAL_NUMBER")
private Office office;

@Id
@OneToOne
@Column(name="ROLE_ID")
private RoleType roleType;

@Id
@Column(name = "YEAR", length = 4, nullable = false)
private String year;
@Temporal(TemporalType.TIMESTAMP)
@Column(name = "CREATION_DATE", nullable = false)
private Date creationDate;

@Temporal(TemporalType.TIMESTAMP)
@Column(name = "LAST_UPDATE_DATE", nullable = false)
private Date lastUpdateDate;

 getter and setter  
 equals and hash code 
 toString 
}

@Name("rolePK")
public class RolePK implements Serializable {

private static final long serialVersionUID = 2222222222222L;

private String office;
private Long roleType;
private String User;
private String year;

public RolePK() {

 getter and setter  
 equals and hash code 
 toString 
}
@Entity
@Scope(ScopeType.CONVERSATION)
@Name("user")
@Table(name = "USER")
public class User implements Serializable {

private static final long serialVersionUID = 333333333333L;

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "USER_SERIAL_NUMBER", length = 20, nullable = false)
private String serialNumber;

 other properties 

@OneToMany(mappedBy="user")
private Collection<Role> roleCollection;

 getter and setter  
 equals and hash code 
 toString 


@Entity
@Scope(ScopeType.CONVERSATION)
@Name("office")
@Table(name = "OFFICE")
public class Office implements Serializable {

private static final long serialVersionUID = 55555555555555555L;

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name="OFFICE_SERIAL_NUMBER", length=20, nullable=false)
private String officeSerialNumber;
 other properties 

@OneToMany(mappedBy="office")
private Collection<Role> roleCollection;

 getter and setter  
 equals and hash code 
 toString 

@Entity
@Scope(ScopeType.CONVERSATION)
@Name("roleType")
@Table(name = "ROLE_TYPE")
public class RoleType implements Serializable {

private static final long serialVersionUID = 44444444444444444L;

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "ROLE_TYPE_ID", nullable = false)
private Long id; 
 other properties 

@OneToMany(mappedBy="roleType")
private Collection<Role> roleCollection;

 getter and setter  
 equals and hash code 
 toString 

I tried to implement this solution (which I had already tried before), but I still have configuration problems. In this particular configuration, the error I have is that when Hibernate tries to create a query, does not seem to be able to generate a proper query. In particular, I have the names of the properties that are not equal to the names of the columns of the relationship table. I tried to use annotations to specify the name of the column on which to bind the property, but the query is generated using the property name and not the name of the column. Thus, the system throws an exception, specifically: com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: Unknown column 'roleco0_.user'in 'field list'.

Someone can help me? Thanks in advance

share|improve this question

Why don't you introduce an autogenerated ID in the Role table, and a unique constraint on the tuple (roleId, use, office, year). This would make sthings much much simpler.

If you really want to continue with this composite primary key, then you should readhttp://docs.jboss.org/hibernate/core/3.6/reference/en-US/html_single/#mapping-declaration-id, which ends with an example of an embedded ID containing an association. But be careful with your associations : they should be ManyToOne, and not OneToOne, since a user (for example) can be referenced by several roles.

You should thus have

@Embeddable
public class RoleId {
    @ManyToOne
    private User user;

    @ManyToOne
    private Office office;

    @ManyToOne
    private RoleType roleType;

    private int year;

    // constructor, getters, equals, hashCode
}

@Entity
public class Role {
    @EmbeddedId
    private RoleId id;

    private Date creationDate;
    // ...
}



출처 - http://stackoverflow.com/questions/5258630/jpa-mapping-composite-key-in-relationship





Posted by linuxism
,


3.Bean Definition Profiles

실제 상황에서 필요한 가장 빈번한 요구사항은 흔히 개발과 운영으로 구분되는 각각의 환경 하에서 서로 다른 bean 설정을 등록하도록 하는 기능이다. 아마 가장 흔한 사례는 개발 단계에서 일반적인 datasource 를 사용하다가 운영 단계로 배포시 JNDI 기반의 datasource 를 사용하도록 하는 것이다. Spring 3.1 버전부터 제공되는 Bean Definition Profiles 기능은 이런 경우를 해결하기 위한 일반적인 방법을 제시한다.

3.1.XML Profiles

다음은 movieService, movieDao와 함께 standalone 형태의 datasource를 설정한 context-movie.xml 예제이다.

<beans xmlns="http://www.springframework.org/schema/beans" 
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xmlns:jdbc="http://www.springframework.org/schema/jdbc" 
    xsi:schemaLocation="..."> 

    <bean id="movieService"
        class="org.anyframe.plugin.core.moviefinder.service.impl.MovieServiceImpl"> 
        <property name="movieDao" ref="movieDao"/> 
    </bean> 

    <bean id="movieDao"
        class="org.anyframe.plugin.core.moviefinder.service.impl.MovieDao">
        <property name="dataSource" ref="dataSource"/>        
    </bean>
	
    <bean id="dataSource" class="org.springframework.jdbc.datasource.SimpleDriverDataSource"> 
        <property name="driverClass" value="org.hsqldb.jdbcDriver" /> 
        <property name="url" value="jdbc:hsqldb:file:/./db/sampledb" /> 
    </property name="username" value="sa" /> 
</beans>

상기 예제에서는 Spring 3.0 버전부터 제공되는 jdbc 네임스페이스를 활용하여 내장 데이터베이스 유형을 간단히 설정하고 있다. 만약 운영단계에서 JNDI 기반의 datasource를 사용해야 한다면 위의 설정은 다음과 같이 변경되어야 한다.

<beans ...> 
    <bean id="movieService" ... /> 

    <bean id="movieDao"
        class="org.anyframe.plugin.core.moviefinder.service.impl.MovieDao">
        <property name="dataSource" ref="dataSource"/>        
    </bean>

    <jee:jndi-lookup id="dataSource" jndi-name="java:comp/env/jdbc/datasource"/> 
</beans>

각 설정들은 개별적으로 잘 동작하겠지만, 개발과 운영이 전환되어야 하는 상황에서는 위의 두가지 설정 내용을 어떻게 매끄럽게 전환할 것인지 고려해야 할 것이다. 다양한 해결책이 있겠지만 일반적으로 시스템의 환경 변수를 활용하거나, ${placeholder] 토큰을 활용한 <import/> XML 태그를 사용하여 해결한다.

3.1.1.XML 기반의 Profile 적용

Spring 3.1 버전부터는 <beans/> 태그가 profile 개념을 포함하게 되었다. 예제에서의 설정 파일은 이제 다음과 같이 3개의 설정으로 나뉘어 질 수 있는데, context-datasource-*.xml 파일의 profile 속성을 유의해서 살펴보기 바란다. 아래 설정 파일은 context-movie.xml 설정 파일의 내용이다.

<beans ...> 
    <bean id="movieService" ... /> 

    <bean id="movieDao"
        class="org.anyframe.plugin.core.moviefinder.service.impl.MovieDao">
        <property name="dataSource" ref="dataSource"/>        
    </bean>
</beans>

다음은 context-datasource-dev.xml 설정 파일의 내용이다. beans 태그의 profile 속성으로 "dev" 가 정의되어 있다.

<beans profile="dev"> 
    <bean id="dataSource" class="org.springframework.jdbc.datasource.SimpleDriverDataSource">
        <property name="driverClass" value="org.hsqldb.jdbcDriver" />
        <property name="url" value="jdbc:hsqldb:file:/./db/sampledb" />
        <property name="username" value="sa" />
    </bean>
</beans>

다음은 context-datasource-production.xml 설정 파일의 내용이다. beans 태그의 profile 속성으로 "production"이 정의되어 있다.

<beans profile="production"> 
    <jee:jndi-lookup id="dataSource" jndi-name="java:comp/env/jdbc/datasource"/>
</beans>

3.1.2.중첩된 beans 태그

Spring 3.1 버전부터는 <beans/> 태그를 동일한 파일 내에 중첩하여 사용할 수 있다. 앞 절에서 설명된 3개의 xml 파일을 하나의 설정 파일로 정의하면 다음과 같다.

<beans xmlns="http://www.springframework.org/schema/beans" 
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xmlns:jdbc="http://www.springframework.org/schema/jdbc" 
    xsi:schemaLocation="..."> 

    <bean id="movieService"
        class="org.anyframe.plugin.core.moviefinder.service.impl.MovieServiceImpl"> 
        <property name="movieDao" ref="movieDao"/> 
    </bean> 

    <bean id="movieDao"
        class="org.anyframe.plugin.core.moviefinder.service.impl.MovieDao">
        <property name="dataSource" ref="dataSource"/>        
    </bean>
			
    <beans profile="dev"> 
		<bean id="dataSource" class="org.springframework.jdbc.datasource.SimpleDriverDataSource">
    		<property name="driverClass" value="org.hsqldb.jdbcDriver" />
    		<property name="url" value="jdbc:hsqldb:file:/./db/sampledb" />
    		<property name="username" value="sa" />
		</bean>
    </beans>

    <beans profile="production"> 
        <jee:jndi-lookup id="dataSource" jndi-name="java:comp/env/jdbc/datasource"/>
    </beans>
</beans>

이러한 중첩 <beans/> 태그는 Beans Definitioin Profiles 용이 아닌 일반적인 용도로도 유용하다. 예를 들어 <beans/> 태그의 default-* 속성들 (default-lazy-init, default-init-method, default-destory-method 등)을 여러개의 bean 에 한번에 정의하고자 할 때에도 활용할 수 있다.

3.2.Profile 활성화

3.2.1.Programmatic Profile 활성화

이제 코드에서 명시적으로 ApplicationContext를 통하여, 앞서 정의된 profile 을 활성화 시키는 방법을 살펴보기로 하자. 다음 코드는 "dev" profile 을 활성화 시키는 예제이다.

GenericXmlApplicationContext ctx = new GenericXmlApplicationContext(); 
ctx.getEnvironment().setActiveProfiles("dev"); 
ctx.load("classpath:/spring/context-*.xml"); 
ctx.refresh();

위의 코드를 실행시키는 경우 각 설정 파일들은 컨테이너에서 다음과 같이 처리된다.

  • context-movie.xml 은 profile 속성이 정의되지 않았으므로 항상 파싱되어 처리된다.

  • context-datasource-dev.xml 은 profile="dev"로 설정되어 있으며, 현재 "dev" profile 이 활성되어 있으므로 파싱되어 처리된다.

  • context-datasource-production.xml 은 profile="production"로 설정되어 있으며, 현재 "production" profile 이 활성되어 있지 않으므로 처리가 생략된다.

3.2.2.선언적인 Profile 활성화

기 정의된 profile은 programmatic한 방식 이외에도 시스템 환경 변수, JVM 시스템 프로퍼티, web.xml내 서블릿 컨텍스트 파라메터 혹은 JNDI 엔트리 등으로 설정된 spring.profiles.active 속성을 통하여 선언적인 방법을 통하여 활성화시킬 수 있다.

<servlet> 
    <servlet-name>dispatcher</servlet-name> 
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> 
    <init-param> 
        <param-name>spring.profiles.active</param-name> 
        <param-value>production</param-value> 
    </init-param> 
</servlet>

3.2.3.다중 Profile 활성화

하나의 Profile 만 활성화시킬 수 있는 것이 아니라, 여러개의 Profile 을 활성화 시킬 수도 있다. 다음은 programmatic 한 예제이다.

ctx.getEnvironment().setActiveProfiles("profile1", "profile2");

선언적인 방법으로는, spring.profiles,active 값은 콤마로 구분된 profile 명을 리스트 형태로 가질 수 있다.

-Dspring.profiles.active="profile1,profile2"

다음과 같이 Bean 정의시 하나 이상의 profile 을 표기할 수도 있다.

<beans profile="profile1,profile2"> 
    ... 
</beans>

3.3.@Profile

Bean Definition Profiles 정의시 Annotation 을 활용하여 정의할 수도 있는데, 이때 사용되는 것이 @Profile 태그이다. 이해를 위하여 다음장에 이어서 나오는 Java Based Configuration 의 내용을 함께 참고하는 것이 좋다.

3.3.1.Annotation 기반의 Profile 적용

XML 기반의 Profile 적용에서 언급되었던 XML 설정 파일의 내용을 Annotation 기반의 Java Based Configuration 으로 변환하면 다음과 같다.

@Configuration
@Profile("dev")
public class StandaloneDataConfig {

    @Bean
    public DataSource dataSource() {
        return new SimpleDriverDataSource(
            new org.hsqldb.jdbcDriver(),
            "jdbc:hsqldb:file:/./db/sampledb",
            "sa",
            "");
    }

}
@Configuration
@Profile("production")
public class JndiDataConfig {

    @Bean
    public DataSource dataSource() throws Exception {
        Context ctx = new InitialContext();
        return (DataSource) ctx.lookup("java:comp/env/jdbc/datasource");
    }

}

동일한 방법으로 XML 기반의 MovieService 설정은 다음과 같이 Java Based Configuratoin 으로 변경될 수 있다.

@Configuration
public class MovieServiceConfig {

    @Autowired
    DataSource dataSource;

    @Bean
    public MovieService movieService() {
        return new MovieService(movieDao());
    }

    @Bean
    public MovieDao movieDao() {
        return new MovieDao(dataSource);
    }
}

이 때 @AutoWired 를 통해 인젝션되는 dataSource 는 다음과 같이 활성화된 profile 에 따라 결정된다.

AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.getEnvironment().setActiveProfiles("dev");
ctx.scan("org.anyframe.plugin.core.moviefinder");
ctx.refresh();

3.4.Environment Abstraction (Environment 추상화)

3.4.1.PropertySources

Spring 3.1부터 제공되는 Environment 추상화 기능은 우선순위 설정이 가능한 PropertySource 에 대하여 통합 검색 옵션을 제공한다.

ApplicationContext ctx = new GenericApplicationContext(); 
Environment env = ctx.getEnvironment(); 
boolean containsFoo = env.containsProperty("foo"); 
System.out.println("Does my environment contain the 'foo' property? " + containsFoo);

위의 코드를 살펴보면 'foo' 라는 속성이 현재 환경 내에서 정의되어 있는지를 확인하고 있다. 해당 속성값을 검색하기 위해 Environment 객체는 PropertySource 객체들에 대하여 검색을 수행한다.

ProyertySource 란 키-값 쌍으로 구성된 속성들의 추상화를 나타내는데, Spring 의 DefaultEnvironment 는 다음과 같이 두개의 PropertySource 로 구성되어 있다.

  • JVM 시스템 속성값의 집합 (System.getProierties()와 같은 방식)

  • 시스템 환경 변수의 집합 (System.getenv()와 같은 방식)

즉, 'foo' 라는 이름으로 시스템 속성값이 정의되어 있거나, 환경 변수가 정의되어 있다면 env.containsProperty("foo")의 값은 true 가 될 것이다. 검색은 순차적으로 수행되는데, 기본적으로 시스템 속성이 환경 변수보다 우선 순위가 높다. 따라서 env.getProperty("foo")을 실행하게 되면 시스템 속성값이 우선적으로 반환된다. 중요한 점은, 이러한 동작 원리에 대하여 설정 변경이 가능하다는 것이다. 만약 사용자 정의 PropertySource 를 만들었다면 순차적인 검색에 포함되도록 설정할 수 있다.

ConfigurableApplicationContext ctx = new GenericApplicationContext(); 
MutablePropertySources sources = ctx.getEnvironment().getPropertySources(); 
sources.addFirst(new MyPropertySource());

위의 예제 코드에서는 MyPropertySource 를 가장 높은 우선순위로 검색되도록 추가하는 모습을 나타내고 있다. 만약 원하는 값을 MyPropertySource 에서 찾지 못한다면 다른 PropertySource 에서 우선 순서에 따라 검색하게 된다.

3.4.2.PropertySource 활용

이제 실제 개발시 어플리케이션 상에서 어떻게 Environment 와 PropertySource 를 사용할 수 있는지 살펴보도록 한다.

  1. <import/> 구문 내에서의 ${placeholder} 처리

    <beans> 
        <import resource="spring/${customer}-config.xml"/> 
    </beans>

    고객 정보에 따라 각기 다른 설정 파일을 로드해야 하는 경우 <import/> 구문과 placeholder 를 통해 'customer' 값을 결정하게 된다. Spring 3.1 이전에는 placeholder 의 값은 오로지 JVM 시스템 속성이나 시스템 환경 변수에서만 검색되었다. 하지만 Spring 3.1 이후부터 Environment 추상화를 통해 검색 우선 순위를 조정할 수 있을 뿐만 아니라 사용자 정의 PropertySource 를 적절히 활용할 수 있게 되었다.

  2. bean 정의내에서의 ${placeholder} 처리

    <context:property-placeholder location="com/bank/config/datasource.properties"/> 
    
    <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"> 
        <property name="driverClass" value="${database.driver}"/> 
        <property name="jdbcUrl" value="${database.url}"/> 
        <property name="username" value="${database.username}"/> 
        <property name="password" value="${database.password}"/> 
    </bean>

    일반적으로 Spring에서 bean 정의시 사용된 ${...} placeholder 를 대치하기 위해서 PropertyPlaceholderConfigurer 나 <context:property-placeholder/>를 사용한다. Spring 3.1 버전부터는 <context:property-placeholder/> 에서 더이상 PropertyPlaceholderConfigurer 를 등록하지 않고 PropertySourcesPlaceholderConfigurer 를 등록하는데, 이 컴포넌트는 기존과 동일하게 datasource.properties 파일 내에서 ${database.*} 속성값을 찾지만 속성값을 찾는데 실패한 경우에는 대안으로써 현재의 Environment 객체의 PropertySource 를 사용한다. 이러한 개선 이전에는 속성값 검색 실패시, 대안으로 오직 시스템 속성값과 환경 변수만을 사용할 수 있었지만, 이제 개발자 측면에서 더 다양한 PropertySource 를 추가적으로 활용할 수 있게 되었다.

3.4.3.웹 어플리케이션내의 PropertySource 활용

상당수의 Spring 기반 어플리케이션은 웹 어플리케이션이며, ApplicationContext가 ContextLoaderListener에 의해 관리된다. 이제 PropertySource 를 제어하기 위하여 Spring 에서 제공하는 ApplicationContextInitializer 인터페이스를 구현하고, 서블릿 컨텍스트 파라메터에 contextInitializerClasses 값을 정의한 예제를 살펴보기로 한다. web.xml 의 일부는 다음과 같다.

<context-param>
    <param-name>contextInitializerClasses</param-name>
    <param-value>com.bank.MyInitializer</param-value>
</context-param>

ApplicationContextInitializer를 구현한 에제 클래스는 다음과 같다.

public class MyInitializer implements ApplicationContextInitializer<ConfigurableWebApplicationContext> {
    public void initialize(ConfigurableWebApplicationContext ctx) {
        PropertySource ps = new MyPropertySource();
        ctx.getEnvironment().getPropertySources().addFirst(ps);
        // 기타 컨텍스트 초기화 작업 수행 ....
    }
}

ApplicationContextInitializer를 구현하고 이를 등록함으로써, 간단한 방법으로 웹 어플리케이션의 ApplicationContext가 refresh 되기 전에 추가 처리를 할 수 있다.


출처 - http://dev.anyframejava.org/docs/anyframe/plugin/essential/core/1.5.0/reference/html/ch03.html






일반적인 PropertyPlaceholderConfigurer
org.springframework.beans.factory.config 패키지 안에 속해 있다.

java.lang.Object
    - org.springframework.core.io.support.PropertiesLoaderSupport
        - org.springframework.beans.factory.config.PropertyResourceConfigure
            - org.springframework.beans.factory.config.PropertyPlaceholderConfigurer

public class PropertyPlaceholderConfigurer
extends PropertyResourceConfigurer
implements BeanNameAware, BeanFacotyAware

외부에 저장된 프로퍼티 파일로 부터 bean definition으로 값을 끌어 당길 수 있다. (외부 프로퍼티 값을 빈 정의부로 가져올 수 있다는 뜻)
Ant / Log4J / JSP EL(Expression Language) 스타일 처럼 ${...} 표기법을 사용한다.

주로 DB 설정값만을 저장해 놓은 Property 파일로 부터 값을 읽어와서 bean definition에서 ${...} 표기법을 통해 사용할 수 있다.

<bean id="dataSource" class="org.springframework.jdbc.dataSource.DriverManagerDataSource">
    <property name="drivateClassName"><value>${dirver}</value></property>
    <property name="url"><value>jdbc:${dbname}</value></property>
</bean>

위의 bean definition 에서 보면 위에 설명한 내용이 그대로 반영되어 있다.

이렇게 bean definition에서 사용하기 위해서는 선행적으로 PropertyPlaceholderConfigurer을 선언해야 한다.

<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
    <property name="location">
         <value>classpath:config/jdbc.properties</value>
    </property>
</bean>

PropertyPlaceholderConfigurer에서는 location, 즉 setLocation 메소드가 존재하지 않고 상속 구조의 상단에 해당하는 PropertiesLoaderSupport 클래스까지 가야지 찾을 수 있다.

PropertiesLoaderSupport 클래스의 setLoaction 메소드의 인자로는 Resource 타입을 전달해 주면 된다. 클래식한 properties 파일이나 JDK 1.5의 properties XML 포맷이면 가능하다.
또한, 여러개의 properties 파일을 설정하고자 하면 setLocations(bean definition에서는 locations) 메소드가 있고 <list> 태그를 사용하여 properties를 설정하면 된다.

<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
    <property name="locations">
        <list>
            <value>classpath:cars.properties</value>
          <value>classpath:airplanes.properties</value>
        </list>
    </property>
</bean>

setLocation(bean definition에서는 location) 메소드와 동일하게 클래식 properties 파일이나 JDK 1.5 properties XML 형식으로 읽어들일 properties 파일을 구성하면 된다.


Spring 2.5에서의 PropertyPlaceholderConfigurer
Spring 2.5에서는 위의 작업들이 보다 간편하게 발전하였는데, 알아보자.
위의 몇 개 과정을 통합해서 하나의 <context:property-placeholder> 태그로 지원하고 있다. 

<context:property-placeholder> 태그를 사용하기 위해서는 몇 가지 확인해야 할 포인트들이 있다.
1. XML Schema 기반의 Bean definition 
2. XML namespace 선언
3. 2.5 기반의 태그 사용

XML Schema 기반이 아니라, 굳이 DTD 기반의 Bean definition이라면 맨 위에서 쓴 대로 기존 방식을 사용하면 된다. 

<beans xmlns="http://www.springframework.org/schema/beans"
            xmlns:context="http://www.springframework.org/schema/context"
            xmlns:xsi=http://www.w3.org/2001/XMLSchema-instance
            xsi:schemaLocation="http://www.springframework.org/schema/beans
                                    http://www.springframework.org/schema/spring-beans-2.5.xsd
                                    http://www.springframework.org/schema/context
                             http://www.springframework.org/schema/spring-context-2.5.xsd
">

<context:property-placeholder location="classpath:config/jdbc.properties" />
(이 다음 부터는 DTD 기반과 사용법은 동일합니다.)


만약 여러개의 properties를 추가하고 싶다면, 콤마(,)를 구분자로 여러개의 properties를 적어주면 된다.
(multiple locations may be provied as a comma-seperated list - from Spring 2.5 reference)

하지만, 이 부분은 확인이 필요한 부분인데 Spring forum에 올려진 질문을 보면 콤마(,)로 구분자를 사용했음에도 불구하고 invalid 에러를 일으킨다라는 글이 있다.

또한 properties 안의 내용이 아니라 글자 그대로 출력되는 경우가 있어, 좀더 연구해 봐야 할 주제다.

 

 


출처 - http://blog.naver.com/PostView.nhn?blogId=tyboss&logNo=70048117732









 

Spring framwork의 IoC 컨테이너 XML 설정 파일을 사용하면 애플리케이션을 유연하게 구성할 수 있습니다. 배포 후에도 지역 상황에 맞춰서 컴포넌트를 다른 것으로 바꾼다거나 운영 설정 값을 바꾸거나 하는 일이 무척 쉽지요.  하지만, 이 파일에는 가볍게 다루기에는 너무 중요한 정보들이 들어 있습니다. 사실 객체 생성 로직이 들어 있는 코드라고 볼 수도 있습니다.

중요한 정보를 가진 파일에 환경에 따라서 자주 변경되는 런타임 설정값들은 같이 두는 것은 적당하지 않습니다. 자주 변경할 것 같은 값들을 컨테이너 설정 파일 외부로 빼서 별도 파일로 관리하는 것이 좋겠지요. spring에 이 용도로 제공하는 후처리기가 두 가지 있는데 하나는 PropertyPlaceholderConfigurer이고 또 하나는 PropertyOverrideConfigurer입니다.

PropertyPlaceholderConfigurer는 컨테이너 설정 파일의 빈들을 만들기 전에 설정 파일 중에 있는 임시표기(Placeholder를 우리말로 뭐라고 해야 할지...ㅡ.ㅡ)를 Properties 파일에 등록한 설정값으로 바꿔줍니다. PropertyOverrideConfigurer는 반대로 빈들을 다 만들고 DI까지 다 완료한 이후에 Properties 파일에 지정한 빈 이름과 필드명에 지정된 값들로 덮어씁니다.

전자는 property 이름을 빈의 구현과 관계없이 논리적으로 정할 수 있는 대신 컨테이너 설정 파일의 설정값이 들어가야 할 위치에 property 이름으로 임시표식을 삽입해줘야 합니다. 후자는 컨테이너 설정 파일을 전혀 손대지 않고도 기본으로 지정된 값을 배치된 운영 환경에 맞는 값으로 덮어쓸 수 있습니다. 하지만 property 이름이 빈 이름에 종속됩니다.

제약

이 두 가지 후처리기를 사용하면 운영 환경에 따라 바뀌는 값을 외부로 뺄 수 있지만 여전히 제약이 남아있습니다. 둘 다 외부 설정 파일로 Properties 파일만 사용할 수 있다는 것이지요. Properties 파일로도 큰 문제 없이 설정을 할 수 있지만 이런 요구가 있을 수 있습니다.

  • Poperties 파일 대신 XML 파일을 쓰고 싶다.
  • DB에 들어 있는 설정 값들로 객체를 만들고 싶다.
  • DB와 XML, Properties 파일을 다 쓰고 싶다.
  • JNDI Tree에 설정값을 넣고 싶다.
  • Windows의 ini 파일이나 Mac의 PropertyList 파일을 쓰고 싶다.

저는 인코딩 문제도 있고 읽기도 좋아서 Properties 보다는 XML을 선호합니다. 그리고 몇몇 설정값은 사용자에게 인터페이스를 제공해서 DB의 값을 조작하게 해야 하는 경우가 있습니다.


Apache Commons Configuration

애플리케이션 운영 환경 설정에 제가 주로 사용하는 기술이 Apache commons 프로젝트 중 한 컴포넌트인 Configuration 입니다.  홈페이지에 의하면 Commons Configuration은 일반화된 설정 인터페이스를 제공함으로써 자바 애플리케이션이 다양한 소스에서 설정을 읽을 수 있도록 해준다고 설명하고 있습니다.

열거하고 있는 소스는 다음과 같습니다.

  • Properties 파일
  • XML 문서
  • Windows INI 파일
  • Property list 파일 (plist)
  • JNDI
  • JDBC Datasource
  • System properties
  • Applet parameters
  • Servlet parameters

Commons Configuration은 이렇게 여러 소스에서 설정값을 읽어오는 기능 외에도 복잡한 XML을 효율적으로 다룰 수 있고  Properties 파일도 기본 API보다 훨씬 다양한 기능을 제공하고 있으며 다양한 타입 접근자를 가지고 있고 여러 소스의 설정값을 통합할 수 있습니다. 자세한 것은 사용자 설명서가 잘 되어 있으니 참고하십시오.

CommonConfigurationFactoryBean

Spring modules 프로젝트를 보면 Apache Commons의 여러 컴포넌트들 중 Configuration, Lang, Chain, Validator의 스프링 통합 컴포넌트들이 있습니다. 그중 Configuration 스프링 통합 컴포넌트가 CommonConfigurationFactoryBean 입니다. 이 빈은 아답터의 일종인데 PropertyPlaceholderConfigurer 같이 PropertyResourceConfigurer를 상속한 빈들에 Commons Configuration의 각종 configuration 객체를 붙일 수 있도록 해줍니다.

CommonConfigurationFactoryBean에 하나 이상의 소스에서 설정값을 읽은 Configuration을 등록한 후 PropertyPlaceholderConfigurer나 PropertyOverrideConfigurer의 properties 필드에 대입을 해주면 CommonConfigurationFactoryBean는 등록된 configuration들에 담겨있는 설정값들을 Properties로 변환해서 전달해줍니다. 

사실 CommonConfigurationFactoryBean은 내부적으로 Configuration의 CompositeConfiguration를 가지고 있어서 여러 소스의 Configuration을 합 할 수 있습니다. 

myconfig.xml이라는 XML 설정 파일을 사용해서 PropertyPlaceholderConfigurer를 등록하는 스프링 설정은 이렇습니다.

    <bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
        <property name="properties">
            <bean class="org.springmodules.commons.configuration.CommonsConfigurationFactoryBean">
                <property name="configurations">
                    <list>
                        <bean class="org.apache.commons.configuration.XMLConfiguration">
                            <constructor-arg type="java.lang.String">
                                <value>myconfig.xml</value>
                            </constructor-arg>
                        </bean>
                    </list>
                </property>
            </bean>
        </property>
    </bean>    

단순한 설정인데도 좀 복잡하게 나왔습니다.  보시면 PropertyPlaceholderConfigurer와 CommonsConfigurationFactoryBean와 XMLConfiguration 세 가지 객체가 만들어집니다.

PropertyPlaceholderConfigurer는 위에 설명한 것처럼 스프링 설정에 환경 설정값을 넣어주는 후처리기입니다. XMLConfiguration은 생성자에 지정한 XML 파일을 읽어서 설정값을 담은 Commons Configuration의 configuration 객체 중 하나입니다. CommonsConfigurationFactoryBean는 XMLConfiguration을 받아서 설정값을 Properties로 변환한 후 PropertyPlaceholderConfigurer에게 전달하는 역할을 합니다.

기본값과 로컬 설정의 분리

별도 운영 설정 파일을 만들더라도 모든 설정을 해줄 필요 없이 변경이 필요한 한두 설정값만  변경하고 나머지는 기본값이 대응되도록 하는 것이 운영하는데 도움이 됩니다. 다음과 같이 local_config.xml과 default_config.xml을 만들어서 각각 로컬 설정과 기본 설정 값을 넣도록 합니다.

local_config.xml

<config>
<smtp>
  <host>mail.abc.co.kr</smtp_host>
</smtp>
</config>

default_config.xml

<config>
<smtp>
  <host>mail.myhost.co.kr</smtp_host>
  <port>25</port>
</smtp>
</config>

그리고 이 두 파일을 사동하도록 설정합니다.

    <bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
        <property name="properties">
            <bean class="org.springmodules.commons.configuration.CommonsConfigurationFactoryBean">
                <property name="configurations">
                    <list>
                        <bean class="org.apache.commons.configuration.XMLConfiguration">
                            <constructor-arg type="java.lang.String">
                                <value>local_config.xml</value>
                            </constructor-arg>
                        </bean>
                        <bean class="org.apache.commons.configuration.XMLConfiguration">
                            <constructor-arg type="java.lang.String">
                                <value>default_config.xml</value>
                            </constructor-arg>
                        </bean>
                    </list>
                </property>
            </bean>
        </property>
    </bean> 

   <bean id="mailService" class=".....">
        <property name="host" value="${smtp.host}"/>
        <property name="port" value="${smtp.port}"/>
   </bean>

이렇게 local_config.xml을 먼저 등록하고 default_config.xml을 나중에 등록하면 먼저 local_config.xml의 설정값을 먼저 찾고 만약 이 파일에 찾으려는 설정값이 없으면 그다음 configration인 default_config.xml에서 찾는다.

Configuration을 직접 사용

Commons Configuration은 Properties로 변환해서 컨테이너 설정 파일의 설정값을 지정하는 용도 외에도 복잡한 애플리케이션 설정값을 표현하는 능력이 있습니다. 이런 Configuration의 기능을 직접 쓰고 싶은 경우를 위해서 설정을 분리하고 필요한 객체에서 참조할 수 있도록 하면 유용합니다.

    <bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
        <property name="properties">
            <bean class="org.springmodules.commons.configuration.CommonsConfigurationFactoryBean">
               <property name="configurations">
                    <list>
                         <ref bean="configuration"/>
                    </list>
                </property>
            </bean>
        </property>
    </bean>
    
    <bean id="configuration" class="org.apache.commons.configuration.CompositeConfiguration">
        <constructor-arg>
            <list>
                <bean class="org.apache.commons.configuration.XMLConfiguration">
                    <constructor-arg type="java.lang.String">
                        <value>user-config.xml</value>
                    </constructor-arg>
                </bean>
                <bean class="org.apache.commons.configuration.XMLConfiguration">
                    <constructor-arg type="java.lang.String">
                        <value>default-config.xml</value>
                    </constructor-arg>
                </bean>
            </list>
        </constructor-arg>
    </bean>

   <bean id="mailService" class=".....">
        <property name="host" value="${smtp.host}"/>
        <property name="port" value="${smtp.port}"/>
   </bean>

   <bean id="anotherService" class=".....">
        <property name="configuration" ref="configuration"/>
   </bean>

anotherService는 직접 Configuration 객체를 받아서 그 안에 담겨있는 설정값들을 Commons Configuration이 제공하는 다양한 방법으로 읽어서 작동할 때에 참고할 수 있습니다.

 


출처 - http://gyumee.egloos.com/1471952





 


 

<bean id="ttt" class="com.foo.bar">
  <property name="properties">
    <bean class="org.springframework.beans.factory.config.PropertiesFactoryBean">
      <property name="location">
        <value>classpath:test.properties</value>
      </property>
    </bean>
  </property>
</bean>



<bean id="sqlProps" class="org.springframework.beans.factory.config.PropertiesFactoryBean"> 
    <property name="location" value="/WEB-INF/sql.properties"/>
</bean>                                               
                                   
즉, 위에서 "sqlProps"는 java.util.Properties 타입의 컴포넌트 빈이 되는 것입니다. 그리고 그 소스 파일은, location 프로퍼티에 지정된 값이죠.
                          
                                                            
그 런데, 프로퍼티 파일을 하나로부터가 아니라, 여러개로부터 읽어들여서 하나의 java.util.Properties 객체를 만들고 싶다면, 다음과 같이 하시면 됩니다. 즉, location 대신에, locations 프로퍼티를 사용하고, 이것의 값을 List로 설정하는 것이죠. 다음 예는, sql1.properties 파일과 sql2.properties 파일을 모두 읽어 들여, sqlProps 라는 객체에 설정하는 것입니다.
             
<bean id="sqlProps" class="org.springframework.beans.factory.config.PropertiesFactoryBean">                                                 
    <property name="locations">                                                 
        <list>                                                             
            <value>/WEB-INF/sql1.properties</value>                                         
            <value>/WEB-INF/sql2.properties</value>                                         
        </list>                                                             
    </property>                                                             
</bean>                                                                 
                                                           
참고로, 컴포넌트 빈의 프로퍼티의 타입이 배열인 경우 (예를 들어, String [], Resource [] 등과 같이), Spring Framework에서는 기본적으로 <list> 형태로 프로퍼티를 설정하시면 됩니다.
                                                           
그리고, 프로퍼티 파일의 value들이 한글로 들어가 있는 경우, 인코딩을 다음과 같이 설정하셔야만 합니다.                     

<bean id="sqlProps" class="org.springframework.beans.factory.config.PropertiesFactoryBean"> 
    <property name="fileEncoding" value="EUC-KR" />                                         
    <property name="location" value="/WEB-INF/sql.properties"/>
</bean>




'Framework & Platform > Spring' 카테고리의 다른 글

spring data - mongodb 설정 예제  (0) 2013.05.19
spring - ehcache config  (0) 2013.04.16
spring oxm  (0) 2013.01.10
spring java based configuration  (0) 2013.01.06
spring data jpa - Unable to build EntityManagerFactory  (0) 2013.01.05
Posted by linuxism
,


보통 vi 로 작업하시는 분들은 터미널로 검색을 많이 하십니다.


원하는 결과를 얻기 위해서는 여러가지 검색 옵션을 사용하게 되는데요


너무 평이한 검색어를 사용함으로써 원치 않는 결과는 좀 빼고 보면 좋겠다 싶을때가 있습니다.


이럴때 사용하는 옵션이 있는데 의의로 모르는 분들이 만습니다.


간단히 grep 의 옵션을 살펴봅니다.


root@boggle70-desktop:linux# grep --help

사용법: grep [옵션]... 패턴 [파일]...

각 파일 및 표준 입력에서 패턴 찾기

패턴은 기본값으로 기본 정규 표현식(BRE)입니다.

예제: grep -i 'hello world' menu.h main.c


정규식 선택과 해석

  -E, --extended-regexp     확장된 정규 표현식으로 된 패턴 (ERE)

  -F, --fixed-strings       개행 문자로 구분된 고정 길이 문자열로 된 패턴

  -G, --basic-regexp        기본 정규 표현식으로 된 패턴 (BRE)

  -P, --perl-regexp         펄 정규 표현식으로 된 패턴

  -e, --regexp=PATTERN      패턴을 이용해서 찾기

  -f, --file=FILE           파일에서 패턴을 가져옴

  -i, --ignore-case         대소문자 구분 안 함

  -w, --word-regexp         전체 단어에 대해서만 패턴 비교

  -x, --line-regexp         전체 라인에 대해서만 패턴 비교

  -z, --null-data           새 줄이 아닌 0 바이트인 줄 끝 데이터


Miscellaneous:

  -s, --no-messages         suppress error messages

  -v, --invert-match        select non-matching lines

  -V, --version             print version information and exit

      --help                display this help and exit

      --mmap                use memory-mapped input if possible


Output control:

  -m, --max-count=NUM       stop after NUM matches

  -b, --byte-offset         print the byte offset with output lines

  -n, --line-number         print line number with output lines

      --line-buffered       flush output on every line

  -H, --with-filename       print the filename for each match

  -h, --no-filename         suppress the prefixing filename on output

      --label=LABEL         print LABEL as filename for standard input

  -o, --only-matching       show only the part of a line matching PATTERN

  -q, --quiet, --silent     suppress all normal output

      --binary-files=TYPE   assume that binary files are TYPE;

                            TYPE is `binary', `text', or `without-match'

  -a, --text                equivalent to --binary-files=text

  -I                        equivalent to --binary-files=without-match

  -d, --directories=ACTION  how to handle directories;

                            ACTION is `read', `recurse', or `skip'

  -D, --devices=ACTION      how to handle devices, FIFOs and sockets;

                            ACTION is `read' or `skip'

  -R, -r, --recursive       equivalent to --directories=recurse

      --include=FILE_PATTERN  search only files that match FILE_PATTERN

      --exclude=FILE_PATTERN  skip files and directories matching FILE_PATTERN

      --exclude-from=FILE   skip files matching any file pattern from FILE

      --exclude-dir=PATTERN directories that match PATTERN will be skipped.

  -L, --files-without-match print only names of FILEs containing no match

  -l, --files-with-matches  print only names of FILEs containing matches

  -c, --count               print only a count of matching lines per FILE

  -T, --initial-tab         make tabs line up (if needed)

  -Z, --null                print 0 byte after FILE name


Context control:

  -B, --before-context=NUM  print NUM lines of leading context

  -A, --after-context=NUM   print NUM lines of trailing context

  -C, --context=NUM         print NUM lines of output context

  -NUM                      same as --context=NUM

      --color[=WHEN],

      --colour[=WHEN]       use markers to highlight the matching strings;

                            WHEN is `always', `never', or `auto'

  -U, --binary              do not strip CR characters at EOL (MSDOS)

  -u, --unix-byte-offsets   report offsets as if CRs were not there (MSDOS)



아 상당히 많은 옵션이 있습니다. (너무 당연한가요?)

몇몇 중요한 옵션고 함께 살펴봅니다.

먼저 
r 옵션은 recursive 옵션입니다.  서브 디렉토리까지 뒤진다는 거죠
n 옵션은 라인 넘버를 출력해 주는 옵션입니다.
r 옵션은 invert 옵션입니다.
            지정한 검색어를 포함하지 않는 라인만 출력하라는 것입니다.

r 옵션은 파이프를 이용한 검색에서 효과적입니다.
즉 내가 검색한 단어가 너무 많이 출력될때... 특히 ctag 와 같은 파일이 함께 있을때
귀찮을 정도로 그곳에서 많이 뜨게 됩니다.

리눅스 커널 디렉토리에서 ctag 를 만든후 grep 으로 검색하면 ctag 에서 저장된 단어들이 주욱 나옵니다.

root@boggle70-desktop:linux# grep -rn sk_buff

이렇게 하면 엄청난 검색어를 보이게 됩니다.
하지만 

root@boggle70-desktop:linux# grep -rn sk_buff ./ | grep -v tags

이렇게 하면 tags 라는 단어를 포함한 라인은 모두 제거됩니다.

커널의 include 디렉토리에서 sk_buff 의 선언을 찾는다고 하고 시도합니다.

grep -rnw sk_buff ./include/ | grep -v tags | grep -v cscope | grep -v extern | grep -v static | grep -v "*"

첫번째는 r 서브디렉토리 검색옵션 + 라인 넘버 출력 + 단어 일치 옵션
두번째는 검색결과에서 tags 제거
세번째는 검색결과에서 cscope 제거
네번째는 검색결과에서 extern  제거
다섯번째는 검색결과에서 static  제거
여섯번재는 검색결과에서 포인터를 나타내는 * 제거

그러면 결과가 20개 안쪽에서 함수의 선을 찾을수 있습니다.

물론 저는 ctags 를 만들어 

vi -t sk_buff    와 같이 해서 

태그가 정의된 위치에서 파일을 열어 해당 위치에 커서를 놓아주는 -t
vim 의 옵션을 씁니다.


출처 - http://forum.falinux.com/zbxe/index.php?document_srl=550758&mid=lecture_tip




'System > Common' 카테고리의 다른 글

X.Org Server  (0) 2014.04.09
X Window System, X11, X  (0) 2014.04.09
GCC Optimization Options  (0) 2012.10.28
FUSE(Filesystem in USEerspace)  (0) 2012.10.28
TCP Offload Engine(TOE)에 대한 이해  (0) 2012.10.28
Posted by linuxism
,